#include <iostream>
#include <cstdlib>
#include <functional>

// C library with some callbacks

typedef void (*CallbackTypeOne)(void* userdata, double arg1);
typedef void (*CallbackTypeTwo)(void* userdata, int arg1);
typedef void (*CallbackTypeThree)(void* userdata, int arg1, float arg2);

typedef void(*GenericCallback)();

void registerAndCallCallback(int typeID, GenericCallback callback, void* userdata)
{
    switch (typeID) {
    case 0: ((CallbackTypeOne)callback)(userdata, 3.14); break;
    case 1: ((CallbackTypeTwo)callback)(userdata, 42); break;
    case 2: ((CallbackTypeThree)callback)(userdata, 42, 3.14f); break;
    };
}

// C++ app using the above library

template <const size_t TypeID, typename T, typename RetType, typename... Args>
struct FunctionFactory
{
public:
    static void bind(RetType(T::*f)(Args...)) {
        instance().fn_ = [f](T* t, Args&&... args) {
            return (t->*f)(std::forward<Args>(args)...);
        };
    }

    static RetType invoke(void* userdata, Args... args) {
        T * t = reinterpret_cast<T*>(userdata);
        return instance().fn_(t, args...);
    }

    typedef decltype(&FunctionFactory::invoke) pointer_type;
    static pointer_type ptr() {
        return &invoke;
    }

private:
    static FunctionFactory & instance() {
        static FunctionFactory inst_;
        return inst_;
    }

    FunctionFactory() = default;

    std::function<RetType(T*, Args...)> fn_;
};

template <const size_t TypeID, typename T, typename RetType, typename... Args>
typename FunctionFactory<TypeID, T, RetType, Args...>::pointer_type
getFunctionPtr(RetType(T::*f)(Args...))
{
    FunctionFactory<TypeID, T, RetType, Args...>::bind(f);
    return FunctionFactory<TypeID, T, RetType, Args...>::ptr();
}

class MyClass
{
public:
    MyClass() {
        registerAndCallCallback(0, reinterpret_cast<GenericCallback>(getFunctionPtr<0>(&MyClass::callbackOne)), this);
        registerAndCallCallback(1, reinterpret_cast<GenericCallback>(getFunctionPtr<1>(&MyClass::callbackTwo)), this);
        registerAndCallCallback(2, reinterpret_cast<GenericCallback>(getFunctionPtr<2>(&MyClass::callbackThree)), this);
    }

    void callbackOne(double arg1) { std::cout << "callback 1 called: " << arg1 << std::endl; }
    void callbackTwo(int arg1) { std::cout << "callback 2 called: " << arg1 << std::endl; }
    void callbackThree(int arg1, float arg2) { std:: cout << "callback 3 called: " << arg1 << ", " << arg2 << std::endl; }
};

int main()
{
    MyClass myclass;
    return 0;
}
