Вопрос

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.

Это было полезно?

Решение

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.

Другие советы

...

template <typename P, typename R, typename Arg1, typename Arg2>
void Func(R (P::*)(Arg1 arg1, Arg2 arg2))
{
    // function type is R (P::*)(Arg1 arg1, Arg2 arg2)
}

template <typename P, typename R, typename Arg1>
void Func(R (P::*)(Arg1 arg1))
{
    // function type is R (P::*)(Arg1 arg1)
}

template <typename P, typename R>
void Func(R (P::*)())
{
     // function type is R (P::*)()
}

I am not familiar with variadic args. This was an only solution before C++11. But now new features of C++11 may solve this problem more elegantly.

BTW. First I saw this way of resolving signature in boost.pyton library implementation.

Using decompose_mem_fun_ptr from http://coliru.stacked-crooked.com/a/00750bf7564ab6d4

template <typename M>
TypeMethod(const char* methodName, M&m)
{
    setName(methodName);
    setObjectType<typename decompose_mem_fun_ptr<M>::class_type>();
    setReturnType<typename decompose_mem_fun_ptr<M>::return_type>();

    // use other info from decompose_mem_fun_ptr<M>.

    using args_type = typename decompose_mem_fun_ptr<M>::arguments; 

    internal_setArgs<args_type>(make_index_sequence<std::tuple_size<args_type>::value>());
}

template<typename Tuple, std::size_t...Is>
void internal_setArgs(index_sequence<Is...>)
{
    // Assuming setArg<T>(i_th).
    int dummy[] = {0, (setArg<typename std::tuple_element<Is, Tuple>::type>(Is), 0)...};
    static_cast<void>(dummy); // silent warning about unused variable.
}

for index_sequence and make_index_sequence:

#if 1 // Not in C++11
#include <cstdint>

template <std::size_t ...> struct index_sequence {};

template <std::size_t N, std::size_t ...Is>
struct make_index_sequence : make_index_sequence < N - 1, N - 1, Is... > {};

template <std::size_t ... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> {};

#endif // make_index_sequence
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top