Providing a safe type dynamic API with a shared library

I am programming a plugin API for an application. Plugins are loaded as shared libraries at runtime. They have access to the application API through an interface, for example:

class IPluginAPI
{
public:
 virtual bool IsPluginsLoaded(void) = 0;
 virtual bool IsHookingEnabled(void) = 0;
 // And about 50 more methods
};

Plugins can request a "listening" of certain events (eg MouseClick, MouseScrolletc.). These functions make up a total of> 300 different events. Normally I would do something like this:

extern "C" void SetEventHooks(APITable& table)
{
 table.MouseClick = &PluginMouseClickEvent;
 table.MouseMove = &PluginMouseMoveEvent;
}

While the function SetEventHooksis located inside the plugin library and is called from the application, the plugins can listen to the functions of interest, indicating their functions. This is not the method that I want to use, but instead I want to offer some abstraction. That's what I meant:

// Interface IPluginAPI supplies a 'SetEventHook` method such as
void SetEventHook(HookID id, void * callback);

HookID , :

enum class HookID
{
 MouseClick,
 MouseMove,
 // ...
};

, :

pluginAPI->SetEventHook(ID::MouseClick, &myCallback);

, , ( ). 300 (, SetHookMouseMove(void (*)(int, int)) ..). , , , , ( ):

template <typename T>
SetEventHook(HookID id, T callback)
{
 if(typeof(T) == decltype(/* function type of the ID */))
  gPluginAPI->SetEventHook(id, callback);
 else static_assert("INVALID FUNCTION TYPE");
}

, ; , / > 300 ?

. , std::function

+5
1

Kerrek, . , API, , hook.

// The default traits. If you don't want to have default traits comment body
// of this type out (including curly braces).
template <HookID id>
struct CallbackTraits
{
   typedef void (*CallbackType)();
};

// Traits for MouseClick
template <>
struct CallbackTraits<HookID::MouseClick>
{
    typedef void (*CallbackType)(int);
};

// Traits for MouseDoubleClick are the same
template <>
struct CallbackTraits<HookID::MouseDoubleClick> : CallbackTraits<HookID::MouseClick> {};

// Traits for MouseMove
template <>
struct CallbackTraits<HookID::MouseMove> 
{
    typedef void (*CallbackType)(int, int);
};


// actual hooking function
template <HookID id>
void SetEventHook(typename CallbackTraits<id>::CallbackType callback)
{
    // do something with id and the callback
}

API :

// handlers prototypes
void MouseClicked(int button);
void MouseMoved(int x, int y);
void SomeEvent();

int main()
{
    // compiles ok
    SetEventHook<HookID::MouseClick>(MouseClicked);
    SetEventHook<HookID::MouseMove>(MouseMoved);

    // won't compile - function signature incompatible
    SetEventHook<HookID::MouseDoubleClick>(MouseMoved);

    // will compile if you left default traits body uncommented
    SetEventHook<HookID::HookWithNoTraitsDefined>(SomeEvent);
    return 0;
}

.

+3

All Articles