Linking and loading the type plugin

Updated: April 19, 2023

After fully implementing our custom data types, we must do some extra work to make them visible to the PiPS framework. Because the framework is written in C, the C++ Fast RTPS type implementation must be wrapped and exposed with C linkage.

Specifically, a corresponding C data type must be defined in a separate compilation unit, and linked to the C++ type. This design requires an accessor function that returns a TypeImpl base class pointer that stores the address of the singleton instance of the C++ type. Our sample DataBufferType (and KeyedDataBufferType) class exposes its implementation as follows:
DataBufferType DataBufferType::m_impl;

TypeImpl* DataBufferType::Impl()
{
    return &m_impl;
}
A separate function must be defined to return the memory address to the provider compilation units with C linkage. We define the fastrtps_databuffer_impl() function inside an extern "C" block (still within databuffer-impl.cxx) to force this linkage:
extern "C" {
    fastrtps_type_impl_t* fastrtps_databuffer_impl( void )
    {
        return DataBufferType::Impl();
    }
}
A similar function will be required for the KeyedDataBuffer class:
extern "C" {
    fastrtps_type_impl_t* fastrtps_keyeddatabuffer_impl( void )
    {
        return KeyedDataBufferType::Impl();
    }
}
These function are then linked to the C compilation unit in which the pluggable type definition is exposed (we call the C file databuffer-plugin.c). To link in the function, we must define a fastrtps_type_t structure for each type and set its .impl field to the function's address. Also, we must specify a loading function that the provider can call to register the type when the module is loaded:
extern fastrtps_type_impl_t* fastrtps_databuffer_impl( void );
extern fastrtps_type_impl_t* fastrtps_keyeddatabuffer_impl( void );

static fastrtps_type_t fastrtps_databuffer_plugin = {
    .name   = "DataBuffer",
    .impl   = fastrtps_databuffer_impl,
};

pips_type_t* fastrtps_databuffer_type = &fastrtps_databuffer_plugin.as_pips_type;

static fastrtps_type_t fastrtps_keyeddatabuffer_plugin = {
    .name   = "KeyedDataBuffer",
    .impl   = fastrtps_keyeddatabuffer_impl,
};

pips_type_t* fastrtps_keyeddatabuffer_type = &fastrtps_keyeddatabuffer_plugin.as_pips_type;

int fastrtps_type_load( fastrtps_provider_t* provider, void* handle ) 
{
    int rc = -1;
    if ( !handle ) {
        errno = ENODEV;
    }
    else {
        if ( !fastrtps_databuffer_plugin.handle ) {
            rc = fastrtps_provider_register_type( provider, 
                                                  &fastrtps_databuffer_plugin.as_pips_type );
            if ( 0 == rc ) {
                fastrtps_databuffer_plugin.handle = handle;
            }
        }
        if ( (EOK == rc) && !fastrtps_keyeddatabuffer_plugin.handle ) {
            rc = fastrtps_provider_register_type( provider, 
                                                  &fastrtps_keyeddatabuffer_plugin.as_pips_type );
            if ( 0 == rc ) {
                fastrtps_keyeddatabuffer_plugin.handle = handle;
            }
        }
    }
    return rc;
}
The new data type can now be loaded into the runtime environment. We do so by providing the name of the plugin DLL (e.g., fastrtps-databuffer.so) in the global PiPS configuration file. Note that the following excerpt is a diff, to indicate the changes needed:
    "typedefs":{
        "QNMP":[],
-               "FASTRTPS":[],
+               "FASTRTPS":[ "fastrtps-databuffer.so" ],
    },
The data type can also be loaded using extra configuration options when the provider is initialized:
const char* extra_options = "typedef=fastrtps-databuffer.so";

pips_provider_t* provider = pips_get_provider( "FASTRTPS" );
pips_init_provider( provider, NULL, extra_options );