Developing Apama Applications > Writing Correlator Plug-ins > Writing a Plug-in in C and C++ > A simple plug-in in C++
A simple plug-in in C++
As an example, this topic describes the development of a simple plug-in called, appropriately, simple_plugin. It has only one function, called test, which takes a string as its sole parameter, makes some alterations to it, prints it out, and passes back another string as the result.
Let us first consider a C++ example using the C++ API. The first requirement is to include the header file correlator_plugin.hpp. This header file contains the definitions for the C++ API. The file is located in the Apama installation’s include directory.
The C++ method that implements this plug-in function must be defined as follows:
class SimplePlugin {
  public:

  static void AP_PLUGIN_CALL test(
    const AP_Context& ctx,
    const AP_TypeList& args,
    AP_Type& rval,
    AP_TypeDiscriminator rtype);
}
The definition for all plug-in functions must be as for SimplePlugin::test above. In essence only the method name and the enclosing class name should vary as far as the definition is concerned. This is important, since it is the Correlator’s Plug-in Support Mechanism that will be calling this C++ method and filling in its parameters.
*The ctx parameter is known as the execution context and is used internally by the event correlator to make the call to the plug-in function. The developer normally need not be concerned with it in the function’s implementation.
*args is an array of parameters; in effect the parameters that the EPL writer will have to supply when calling test.
*rval denotes the return value. Plug-in function implementations must pass out any return value through this parameter, although as will be shown, in EPL the function will appear to return a result in the traditional way. The return value can be a float, boolean, string, integer or chunk, and the expected return type is indicated by rval.discriminator(). It is not possible to return sequences or other correlator types.
*rtype is the expected return type, identical to the result of calling rval.discriminator() and is retained for backwards compatability.
The next important step is to define exactly what type of parameters the above plug-in function should expect and accept, what it should return, and under what name it should appear within EPL.
/** Parameter types for the 'test' function */
static const char8* testParamTypes[1] = {"string"};

/** Declare functions provided by this plugin */
static AP_Function Functions[1] = {
  {"test", &SimplePlugin::test, 1, &testParamTypes[0], "string"}
};
The static array of AP_Functions structures needs to be defined in every plug-in to describe which functions that plug-in is exporting to the event correlator. In this case the C++ method SimplePlugin::test has been mapped to appear as the external plug-in function “test”, to take a single parameter, with the latter being of the EPL type string (as defined within testParamTypes), and return a value of EPL type string. If a particular function returns nothing, the return type should be specified as void.
All that is left is to implement the “C” plug-in initialization method:
AP_PLUGIN_DLL_SYM AP_ErrorCode AP_PLUGIN_CALL
  AP_INIT_FUNCTION_NAME {
    const AP_Context& ctx,
    uint32& version,
    uint32& nFunctions,
    AP_Function*& functions
);
the “C” plug-in shutdown method:
AP_PLUGIN_DLL_SYM AP_ErrorCode AP_PLUGIN_CALL
  AP_SHUTDOWN_FUNCTION_NAME (const AP_Context& ctx);
and the “C” plug-in library version check:
AP_PLUGIN_DLL_SYM AP_ErrorCode AP_PLUGIN_CALL
  AP_LIBRARY_VERSION_FUNCTION_NAME(const AP_Context& ctx,
uint32& version);
The names of the functions are macros defined in correlator_plugin.hpp.
Linking limitations require that these three functions be defined as “C” functions. Both should at least implement the code as indicated in the full source code example below. For most situations it is recommended that the developer re-deploy the initialization and shutdown methods provided unchanged, although more complex plug-ins may include plug-in-specific startup and shutdown code in these functions. Note that the initialization and shutdown functions are invoked each time the library is loaded or unloaded, so these functions must be re-entrant and able to be safely invoked multiple times.
Going back to the implementation of the test method, through use of the extensive library of helper functions available on the AP_Type class, the developer can manipulate the values passed through by the EPL code.
For example, this code displays an integer argument passed to a function:

cout << args[0].integerValue();
while this call increments the second element of a sequence argument:

AP_Type &element = args[0][2];
element.integerValue(element.integerValue()+1);
Note that this is relevant since sequences and chunks are passed by reference. So, if the EPL code calling it was:

sequence<integer> mySeq := [0,10,20,30];
myPlugin.exampleFunction(mySeq);
print mySeq;
then after the call mySeq is [0,10,21,30].
Note that this is modifying the sequence mySeq refers to, not altering the value of mySeq itself. A plug-in function cannot do the equivalent of: mySeq := otherSeq;
Similarly, it is not possible to modify primitives passed to a plug-in as arguments. Strings, while strictly speaking a reference type, are immutable and so cannot be modified either.
The complete code base of this simple example is as follows, and may be found in the Apama installation’s samples\correlator_plugin\cpp\simple_plugin.cpp directory. A ‘makefile’ (for use with GNU Make) and batch file (for Microsoft’s Visual Studio .NET) are provided in this folder to assist with compiling plug-ins on UNIX and Windows platforms respectively. A README.txt file in the directory describes how to build the example.


/**
* simple_plugin.cpp
*
* A very simple example plugin.
*
* $Copyright(c) 2013 Software AG, Darmstadt, Germany and/or its licensors$
* $Revision: 188575 $ $Date: 2012-07-17 20:22:08 +0100 (Tue, 17 Jul 2012) $
*/
 
#include <correlator_plugin.hpp>
#include <iostream>
#include <string>
#include <cstring>
 
// Used in the test function below
#define TEST_STRING "Hello, World"
 
using namespace std;
 
/**
* Plugin implementation class. Contains *static* members matching the
* AP_FunctionPtr typedef in "correlator_plugin.hpp". These could also be
* provided as static top-level functions with C++ linkage.
*/
class SimplePlugin {
 
public:
/**
* Plugin function. Takes a single string parameter and returns
* another string. The input string will also be modified (pass by
* reference for non-primitive types).
*
* @param ctx Execution context for this invocation of the function.
* @param args Function parameters, as declared below.
* @param rval Function return value, to be filled in by plugin.
*
* @throw AP_PluginException If anything goes wrong.
*/
static void AP_PLUGIN_CALL test(
const AP_Context& ctx,
const AP_TypeList& args,
AP_Type& rval,
AP_TypeDiscriminator rtype) {
cout << "simple_plugin function test called" << endl;
cout << "args[0] = " << args[0].stringValue() << endl;
// This demonstrates the use of the new string allocation
// functions in AP_Context. Note that string assignment copies
// the string, so we must free our local copy to avoid a memory
// leak.
char8* newStr = ctx.char8alloc(strlen(TEST_STRING) + 1);
// +1 for the terminator!
strcpy(newStr, TEST_STRING);
rval = newStr;
ctx.char8free(newStr);
cout << "return value = " << rval.stringValue() << endl;
cout.flush();
}
};
 
/** Parameter types for the 'test' function */
static const char8* testParamTypes[1] = { "string" };
 
/** Declare functions provided by this plugin */
static AP_Function Functions[1] = {
{ "test", &SimplePlugin::test, 1, &testParamTypes[0], "string" }
};
 
/**
* Initialisation and shutdown functions. Must be declared extern "C" so that
* the plugin loader can look them up by name. Signatures must match the
* AP_InitFunctionPtr and AP_ShutdownFunctionPtr typedefs, respectively.
* Likewise, the function names must use the AP_INIT_FUNCTION_NAME and
* AP_SHUTDOWN_FUNCTION_NAME macros.
*/
extern "C" {
 
/**
* Plugin initialisation function. Called each time the plugin library is
* loaded, so must expect to be called more than once.
*
* @param ctx Execution context for the function call.
* @param version Active plugin API version. The plugin should verify it
* is compatible with this version (and return AP_VERSION_MISMATCH_ERROR
* if not) and update version to indicate the API version the plugin was
* compiled against.
* @param nFunctions The plugin should set this to the number of functions
* it exports.
* @param functions The plugin should set this to the address of an array
* of AP_Functions structures, of length nFunctions, describing the
* functions exported by the plugin. This data will be read and copied by
* the plugin API.
*
* @return An AP_ErrorCode indicating the success or otherwise of the
* library initialisation.
*/
AP_PLUGIN_DLL_SYM AP_ErrorCode AP_PLUGIN_CALL AP_INIT_FUNCTION_NAME(
const AP_Context& ctx,
uint32& version,
uint32& nFunctions,
AP_Function*& functions) {
// Export the plugin's version
version = AP_PLUGIN_VERSION;
// Export function declarations
nFunctions = 1;
functions = &Functions[0];
return AP_NO_ERROR;
}
 
/**
* Plugin shutdown function. Called each time the plugin library is
* unloaded, so must expect to be called more than once.
*
* @param ctx Execution context for the function call.
*
* @return An AP_ErrorCode indicating the success or otherwise of the
* library initialisation.
*/
AP_PLUGIN_DLL_SYM AP_ErrorCode AP_PLUGIN_CALL AP_SHUTDOWN_FUNCTION_NAME(
const AP_Context& ctx) {
// Nothing to do -- just indicate success
return AP_NO_ERROR;
}
 
/**
* Plugin library version function.
*
* @param ctx Execution context for the function call.
*
* @param version The plugin should fill this in with the version of the
* API it was compiled against.
*
* @return An AP_ErrorCode indicating the success or otherwise of the
* function call.
*/
AP_PLUGIN_DLL_SYM AP_ErrorCode AP_PLUGIN_CALL AP_LIBRARY_VERSION_FUNCTION_NAME(
const AP_Context& ctx,
uint32& version) {
// Just return the version number
version = AP_PLUGIN_VERSION;
return AP_NO_ERROR;
}
 
/**
* Plugin capabilities function.
*
* @param ctx Execution context for the function call.
*
* @return An AP_Capabilities bitset describing the capabilities of this
* plugin.
*/
AP_PLUGIN_DLL_SYM AP_Capabilities AP_PLUGIN_CALL AP_PLUGIN_GET_CAPABILITIES_FUNCTION_NAME(
const AP_Context& ctx) {
// This plugin promises no special capabilities. But if you wanted to
// declare any, you could return them, or-ed together.
return AP_CAPABILITIES_NONE;
}
 
} // extern "C"



Copyright © 2013 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, and/or Terracotta Inc., San Francisco, CA, USA, and/or Software AG (Canada) Inc., Cambridge, Ontario, Canada, and/or, Software AG (UK) Ltd., Derby, United Kingdom, and/or Software A.G. (Israel) Ltd., Or-Yehuda, Israel and/or their licensors.