Export Functions

Guide to export functions from your C++ plugin to be used by other language modules within Plugify.

In the Plugify ecosystem, C++ plugins can export functions to make them accessible to other plugins. This guide explains how to export functions in C++ and provides examples to help you integrate your plugins seamlessly.

Basic Type Mapping

The following table lists how types are exposed to the C++ API:

C++ TypePlugify AliasRef Support ?
voidvoid
boolbool
charchar8
char16_tchar16
int8_tint8
int16_tint16
int32_tint32
int64_tint64
uint8_tuint8
uint16_tuint16
uint32_tuint32
uint64_tuint64
uintptr_tptr64
uintptr_tptr32
floatfloat
doubledouble
void*function
plg::stringstring
plg::anyany
plg::vector<bool>bool
plg::vector<char>char8
plg::vector<char16_t>char16
plg::vector<int8_t>int8
plg::vector<int16_t>int16
plg::vector<int32_t>int32
plg::vector<int64_t>int64
plg::vector<uint8_t>uint8
plg::vector<uint16_t>uint16
plg::vector<uint32_t>uint32
plg::vector<uint64_t>uint64
plg::vector<uintptr_t>ptr64
plg::vector<uintptr_t>ptr32
plg::vector<float>float
plg::vector<double>double
plg::vector<plg::string>string
plg::vector<plg::any>any
plg::vector<plg::vec2>vec2
plg::vector<plg::vec3>vec3
plg::vector<plg::vec4>vec4
plg::vector<plg::mat4x4>mat4x4
plg::vec2vec2
plg::vec3vec3
plg::vec4vec4
plg::mat4x4mat4x4

Exporting Functions in C++

To export a function in a C++ plugin, you need to ensure that the function is visible to other plugins. This is typically done by marking the function with the PLUGIN_API macro, which ensures the function is exported when the plugin is compiled as a dynamic link library (DLL).

Key Points

  • Static Functions: Exported functions should generally be static to avoid requiring an object instance for invocation.
  • C Linkage: The PLUGIN_API macro does not automatically include extern "C" to prevent name mangling. We need ensure the function can be found just by name by adding extern "C".
  • Parameter and Return Types: Use Plugify's native types (e.g., plg::string, plg::vector) for seamless integration.

Generating the PLUGIN_API Macro

The PLUGIN_API macro is generated using CMake. Here’s how you can set it up in your CMakeLists.txt file:

include(GenerateExportHeader)
generate_export_header(${PROJECT_NAME}
    EXPORT_MACRO_NAME CPPLM_EXPORT
    EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/exports/module_export.h
)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR}/exports)

This generates a header file (module_export.h) that defines the PLUGIN_API macro. Include this header in your plugin source files to mark functions for export.

Basic Example

Here’s a simple example of exporting a function in a C++ plugin:

Function Definition

plugin.cpp
#include <plugify/plugify.h>
#include "module_export.h" // Include the generated export header

extern "C" PLUGIN_API int32_t AddNumbers_Exported_Name(int32_t a, int32_t b) {
    return a + b;
}

Exporting the Function

When the plugin is loaded, the function AddNumbers will be exported and can be called by other plugins.

Plugin Manifest Example

All exported functions must be described in the plugin's manifest file under the exportedMethods section. Here’s an example manifest for a plugin that exports the AddNumbers function:

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "exportedMethods": [
    {
      "name": "AddNumbers",
      "funcName": "AddNumbers_Exported_Name",
      "paramTypes": [
        {
          "type": "int32",
          "name": "a"
        },
        {
          "type": "int32",
          "name": "b"
        }
      ],
      "retType": {
        "type": "int32"
      }
    }
  ]
}

Parameter and Return Type Conventions

Plugify uses specific conventions for parameter and return types to ensure compatibility across plugins. Below are the guidelines for C++:

1. Arrays

  • Parameter: Pass arrays as plg::vector<T>&.
  • Return: Return arrays as plg::vector<T> by value.

2. Strings

  • Parameter: Pass strings as const plg::string&.
  • Return: Return strings as plg::string by value.

3. Structures

  • Parameter: Pass structures by reference (const T&).
  • Return: Return structures by value.

4. Primitive Types

  • Parameter: Pass primitive types (e.g., int, float) by value or by reference (if modification is needed).
  • Return: Return primitive types by value.

Advanced Example: Exporting Complex Functions

Here’s an example of exporting a function with complex parameter and return types:

Function Definition

plugin.cpp
#include <plugify/plugify.h>
#include "module_export.h"

extern "C" PLUGIN_API plg::vector<plg::string> ProcessData_Exported_Name(const plg::vector<double>& data, const plg::string& prefix) {
    plg::vector<plg::string> result;
    for (double value : data) {
        result.push_back(prefix + std::to_string(value));
    }
    return result;
}

Plugin Manifest

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "exportedMethods": [
    {
      "name": "ProcessData",
      "funcName": "ProcessData_Exported_Name",
      "paramTypes": [
        {
          "type": "double[]",
          "name": "data"
        },
        {
          "type": "string",
          "name": "prefix"
        }
      ],
      "retType": {
        "type": "string[]"
      }
    }
  ]
}

Handling Callbacks

Plugify allows you to export functions that accept callbacks as parameters. Here’s an example:

Function Definition

#include <plugify/plugify.h>
#include "module_export.h"

using CallbackFunction = plg::string(*)(int32_t, const plg::string&);

extern "C" PLUGIN_API void ExecuteWithCallback_Exported_Name(int32_t value, const plg::string& input, CallbackFunction callback) {
    plg::string result = callback(value, input);
    // Process the result
}

Plugin Manifest

plugin_name.pplugin
{
  "name": "ExamplePlugin",
  "version": "1.0.0",
  "exportedMethods": [
    {
      "name": "ExecuteWithCallback",
      "funcName": "ExecuteWithCallback_Exported_Name",
      "paramTypes": [
        {
          "type": "int32",
          "name": "value"
        },
        {
          "type": "string",
          "name": "input"
        },
        {
          "type": "function",
          "name": "callback",
          "prototype": {
            "name": "ExampleCallback",
            "funcName": "ExampleCallback_Exported_Name",
            "paramTypes": [
              {
                "type": "int32",
                "name": "value"
              },
              {
                "type": "string",
                "name": "input"
              }
            ],
            "retType": {
              "type": "string"
            }
          }
        }
      ],
      "retType": {
        "type": "void"
      }
    }
  ]
}

Best Practices

  1. Use PLUGIN_API: Always use the PLUGIN_API macro to mark functions for export.
  2. Follow Type Conventions: Adhere to Plugify's type conventions for parameters and return values.
  3. Document Your Functions: Clearly document the purpose, parameters, and return values of exported functions.
  4. Test Thoroughly: Test your exported functions to ensure they work as expected when called by other plugins.
  5. Update the Manifest: Always describe exported functions in the plugin manifest under the exportedMethods section.

Conclusion

Exporting functions in C++ plugins is straightforward when you follow Plugify's conventions and best practices. By using the PLUGIN_API macro, adhering to type conventions, and describing functions in the plugin manifest, you can create robust and interoperable plugins. For more advanced use cases, such as handling callbacks or returning C++ objects, use the techniques outlined in this guide.