C++ Using templates to create a function wrapper/adapter to bind dynamic languages

Started by
-1 comments, last by SyncViews 7 years, 6 months ago

I am working on creating some C++ templates to ease bind functions and member functions to a dynamic system (Lua/Python/console/CLI/etc.), avoiding large numbers of hand coded wrappers / specs.

In the past I have either just gone with doing it by hand, or some sort of code gen. Which always seems to end up being a pain, since I still had a separate "specification" file, and it added build requirements like having a recent version of Ruby/Python/whatever+libs and poor/nonexistent IDE support.

So want to see if its possible in pure C++ now with variadic templates.

e.g. in the simple case of just dealing with strings (e.g. for a command console), to be able to do something such as this:


typedef std::vector<std::string> Args;

std::string to_string(float x); //plus normal and custom overloads
template<class T> T from_string(const std::string &str); //with specialisations

float add(float a, float b);
float mul(float a, float b);
void kill(Entity *entity);

FunctionTable functions = {
  {"add", &add},
  {"mul", &mul},
  {"kill", &kill}
};



I came up with a solution to have a template function that can call another function pointer type, although that results in me storing both pointers, and for member function pointers I cant safely cast them to/from void*/anything, so ended up using memcpy.

I understand that it is possible to pass a function pointer, member function pointer, or member variable pointer directly as a template argument, thus becoming part of the template itself. But can this be made to work with a pointer of unknown type? It seemed the template had to declare each component explicitly, e.g.:


  template<MyReturn (MyClass::*Func)(int, float)> Method wrapper();
  wrapper<&McClass::foo>(); //OK, but not generic
  
  template<typename Func> Method wrapper();
  wrapper<&McClass::foo>(); //invalid template argument for 'Func', type expected

Prototype code:


//This was only way I found to unpack each element of an array-like container into a normal function call
//Its just to give a parameter pack such as "0, 1, 2, 3" for a 4-arg function
template<size_t...> struct Indices {};
template<size_t N, size_t... Is> struct BuildIndices
    : BuildIndices<N - 1, N - 1, Is...> {};
template<size_t... Is> struct BuildIndices<0, Is...> : Indices<Is...> {};

template<class... Args, class Func, size_t ...indices>
std::string do_call_inner(Func func, const std::vector<std::string> &args, Indices<indices...>)
{
    //call by unpacking args and converting each from a from_string specialisation
    return to_string(func(from_string<Args>(args[indices])...));
}
//Another wrapper to add in BuildIndices, since needed Args separated in the function signature
template<class RetType, class ...Args>
std::string do_call(RetType(*func)(Args...), const std::vector<std::string> &args)
{
    if (args.size() != sizeof...(Args)) throw std::runtime_error("Invalid argument count");
    return do_call_inner<Args...>(func, args, BuildIndices<sizeof...(Args)>{});
}
template<class Func>
std::string wrapped_call(void *storage, const std::vector<std::string> &args)
{
    return do_call((Func)storage, args);
}

struct Method
{
    template<class Func> Method(Func f) : storage(f), caller(&wrapped_call<decltype(f)>) {}
    std::string operator ()(const std::vector<std::string> &args)const
    {
        return caller(storage, args);
    }
    void *storage;
    Caller caller;
};

And of course overloaded functions wont work with this approach. But is there any reasonable solution for that? I am thinking ill just carry on hand-coding the overload resolution for those.

This topic is closed to new replies.

Advertisement