Search code examples
c++templatesc++11variadic-templatesfunction-signature

How do I pass arbitrary member function of ANY class to template in order to resolve its signature?


In my engine, I have simple reflection system filled with classes info at compile time (that is, built around set of templates, that allows me to automate the process of generating metainfo).

Consider following example:

class Type
{
  //...
  Map<PropertyHash, TypeProperty> _properties;
};

For each type there is a function:

template <class T>
void InitializeType(TypeInitializer* typeInitializer);

responsible for type initialization. TypeInitializer has few methods used to add fields and base types to type. So basically, every new type requires only specialization of this function. Later, when type is queried for the first time, TypeDatabase creates concrete Type object and calls InitializeType() for it (TypeInitializer gets pointer to type during construction). For example:

struct CST
{
    const float* fptr;
    volatile Uint32 vuint;
    void** vptr;
};

template <>
SvrInline void InitializeType<CST>(TypeInitializer* typeInitializer)
{
    typeInitializer->AddProperty("fptr", &CST::fptr);
    typeInitializer->AddProperty("vuint", &CST::vuint);
    typeInitializer->AddProperty("vptr", &CST::vptr);
}

And that's it. All magic is done in TypeProperty constructor, which is declared as:

template <class Object_type, class Property_type>
TypeProperty(const char* fieldName, Property_type (Object_type::* propertyPtr));

This allows me to know the exact type of the property. I test it for size, const-ness, volatile-ness etc., and save this info in TypeProperty object. Nice.

Now, I need something identical for members function as well. 'Identical' means, that I can add function in the very same way I'm adding properties right now.

My first thought were variadic templates (my engine is built with full support for C++11 features in mind):

template <typename Object_t, typename Return_t, typename... Args>
TypeMethod(const char* methodName, Return_t (Object_t::*)(Args&&... args)
{
  //What now?
}

I do not know, however, how should I extract types from args. I saw an article with an approach, that uses function overloading:

template <typename P, typename R, typename Arg1, typename... Args>
void Func(R (P::*)(Arg1 arg1, Args&&... args))
{
}

template <typename P, typename R, typename... Args>
void Func(R (P::*)(Args&&... args))
{
}

template <typename P, typename R>
void Func(R (P::*)())
{
}

Function was 'forwarded' recursively (I know it's not an actual recursion) with one parameter less each time. I don't see, however, how is this suitable for my case.


Solution

  • There's no need for recursion, just use pack expansion:

    template <typename Object_t, typename Return_t, typename... Args>
    TypeMethod(const char* methodName, Return_t (Object_t::*)(Args&&... args)
    {
      setName(methodName);
      setObjectType<Object_t>();
      setReturnType<Return_t>();
      auto dummy[] = {0, (addArgumentType<Args>(), 0)...};
    }
    

    We place the pack expansion inside a braced-init-list to ensure that the calls to addArgumentType<...> are made in the correct order.