This is a modular collection of utility functions I wrote for various plugins for the X-Plane flight simulator. The code is portable between Windows, macOS and Linux. It is not a classic library in the sense that this code doesn't provide build support for a shared / dynamically linked library.
The code for each module of the XPPL collection resides in their own directory (cf. list
of modules below). The corresponding public interface header files can all be found in
the include
directory.
The xppl_common
module is mandatory; without it being present, none of the other modules
will compile. All public interface functions from the xppl_common
module are declared
in the header file xppl.h
; other modules have their own dedicated header file.
The easiest way to incorporate some of XPPL's functionality into a plugin project is to add it as a git submodule, and cherry-pick the modules you actually need by adding the corresponding modules to your own plugin's build scripts.
# add XPPL as submodule
git submodule add https://github.com/daemotron/xppl.git
# update the XPPL submodule
git submodule update --remote xppl
Assuming the above, the file system layout of your plugin project might look like this:
+--+ myPlugin
|
+ SDK/
|
+ src/
|
+ xppl/
|
+-- CMakeLists.txt
SDK
contains a copy of the X-Plane SDKsrc
contains your own plugin source filesxppl
is this repository, embedded as submodule
In your CMakeLists.txt
, you can embed XPPL modules this way:
- Add the XPPL
include
directory to the include directories of your project:include_directories ("xppl/include")
- Include the matching XPPL headers in your own source files:
#include <xppl.h>
- Add the mandatory
xppl_common
module to your source files:file(GLOB_RECURSE XPPL_COMMON_SOURCES "xppl/xppl_common/*.c") ... set(SOURCES ... ${XPPL_COMMON_SOURCES})
- Optionally add further modules in the same way as you added the
common
module.
Please Note: the xppl_common
module is mandatory, all other modules are optional.
#include <xppl.h>
/* min and max macros */
int i = xppl_max(3, 5);
int j = xppl_min(3, 5);
/* mark unused arguments to prevent compiler warnings */
PLUGIN_API void
XPluginReceiveMessage(XPLMPluginID from, int msg, void *param)
{
xppl_unused(from);
xppl_unused(msg);
xppl_unused(param);
}
/* numeric constants to string */
const char *s = xppl_str(12345);
/* for fatal errors */
xppl_crash();
#include <xppl_alloc.h>
/* will crash gracefully if memory allocation fails */
void *ptr = xppl_malloc(1024);
void *buf = xppl_calloc(50, sizeof(char));
ptr = xppl_realloc(ptr, 2048);
Platform-independent byte-order conversion for 16, 32 and 64 bit integers, as well as float and double
#include <xppl_endianess.h>
if (XPPL_ENDIANESS_LE) {
/* we're little endian */
}
if (XPPL_ENDIANESS_BE) {
/* we're big endian */
}
uint16_t sn = xppl_htons(1234);
uint32_t ln = xppl_htonl(1234);
uint64_t lln = xppl_htonll(1234);
uint16_t sh = xppl_ntohs(sn);
uint32_t lh = xppl_ntohl(ln);
uint64_t llh = xppl_ntohll(lln);
float fn = xppl_htonf(123.45);
double dn = xppl_htond(123.45);
float fh = xppl_ntohf(fn);
double dh = xppl_ntohd(dn);
Comparing floating-point with '==' or '!=' is unsafe
#include <xppl_float.h>
if (xppl_float_almost_equal_f(123.4, 123.4)) {}
if (xppl_float_almost_equal_d(12345.678, 12345.678)) {}
if (xppl_float_almost_equal_l(123456789.123456789, 123456789.123456789)) {}
You need big integers?
#include <xppl_int.h>
#ifdef XPPL_INT_128
int128_t super;
uint128_t usuper;
#else
int64_t super;
uint64_t usuper;
#endif
usuper = xppl_int_abs(super);
Non-cryptographic Fowler–Noll–Vo hash functions
#include <xppl_hash.h>
uint64_t h1 = xppl_hash_fnv1("my identifying string");
uint64_t h1a = xppl_hash_fnv1a("my identifying string");
#include <xppl_log.h>
/* initialize logger */
xppl_log_init(XPLMDebugString, XPPL_LOG_INFO, "My Plugin");
/* send log messages */
xppl_log_debug("This message won't show");
xppl_log_warn("This one will");
/* also works with format strings, just like printf */
int t = 15;
xppl_log_info("The outside temperature is %d degrees", t);
/* clean up your mess */
xppl_log_destroy();
Check and manipulate paths across different platforms
#include <xppl_path.h>
/* check if a path exists */
if (xppl_path_exists("Resources/plugins/foo")) {}
/* create a directory */
xppl_path_create_dir("foo");
/* create a directory recursively */
xppl_path_create_recursive("Output/foo/bar", "/");
/* chop off the last part of a path */
char buffer[256] = { '\0' };
size_t len = xppl_path_dirname("foo/bar/baz", "/", buffer, 256);
printf("%s\n", buffer) /* result: foo/bar */
Cf. Configuration File with XPPL for a more in-depth description
#include <xppl_config.h>
/* initialize configuration context */
xppl_config_ctx_t cfg;
xppl_config_init(&cfg, "MyPlugin Configuration", "Output/myplugin/config.txt", XPLMGetDirectorySeparator(), true)
/* register configuration options and their default values */
long long default_int = 1234;
char default_str[] = "Hello World";
xppl_config_register(&cfg, "my_int", XPPL_CONFIG_INT, &default_int, "My configurable integer");
xppl_config_register(&cfg, "my_str", XPPL_CONFIG_STRING, default_str, "My configurable string");
/* load or create configuration file */
xppl_config_load(&cfg);
/* retrieve configuration value by key */
long long conf_int = xppl_config_data_get_i(&cfg, "my_int");
char conf_str[255];
size_t len = xppl_config_data_get_s(&cfg, "my_str", conf_str, 255);
Simple hash maps with optional persistence
#include <xppl_map.h>
/* create a map */
xppl_map_t map;
xppl_map_init(&map, 0);
/* put some values */
xppl_map_set_i(&map, "my int", 123);
xppl_map_set_f(&map, "my float", 123.456F);
/* get value from map */
int i = xppl_map_get_i(&map, "my int");
/* save map to a file */
xppl_map_save(&map, "Output/myplugin/data.map");
/* clean up */
xppl_map_destroy(&map);
Simple unit test -- to see how this works, best take a look at the unit tests in ./test
.