В моем движке у меня есть простая система отражения, заполненная информацией о классах во время компиляции (то есть построенная вокруг набора шаблонов, что позволяет мне автоматизировать процесс генерации metainfo).
Рассмотрим следующий пример:
class Type
{
//...
Map<PropertyHash, TypeProperty> _properties;
};
Для каждого типа есть функция:
template <class T>
void InitializeType(TypeInitializer* typeInitializer);
ответственный за инициализацию типа. TypeInitializer имеет несколько методов, используемых для добавления полей и базовых типов к типу. В общем, каждый новый тип требует только специализации этой функции. Позже, когда тип запрашивается в первый раз, TypeDatabase создает конкретный объект Type и вызывает для него InitializeType () (TypeInitializer получает указатель на тип во время построения). Например:
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);
}
И это все. Вся магия делается в конструкторе TypeProperty, который объявлен как:
template <class Object_type, class Property_type>
TypeProperty(const char* fieldName, Property_type (Object_type::* propertyPtr));
Это позволяет мне знать точный тип собственности. Я проверяю его на размер, постоянство, изменчивость и т. Д. И сохраняю эту информацию в объекте TypeProperty. Ницца.
Теперь мне нужно что-то идентичное для функций членов. «Идентичный» означает, что я могу добавить функцию точно так же, как я добавляю свойства прямо сейчас.
Моей первой мыслью были шаблоны с переменным числом аргументов (мой движок построен с полной поддержкой функций C ++ 11):
template <typename Object_t, typename Return_t, typename... Args>
TypeMethod(const char* methodName, Return_t (Object_t::*)(Args&&... args)
{
//What now?
}
Однако я не знаю, как мне извлекать типы из аргументов. Я видел статью с подходом, который использует перегрузку функций:
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::*)())
{
}
Функция была «перенаправлена» рекурсивно (я знаю, что это не реальная рекурсия), каждый раз на один параметр меньше. Я не вижу, однако, как это подходит для моего случая.
Нет необходимости в рекурсии, просто используйте расширение пакета:
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)...};
}
Мы помещаем расширение пакета в фигурный список инициализации, чтобы гарантировать, что вызовы addArgumentType<...>
сделаны в правильном порядке.
...
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::*)()
}
Я не знаком с разнообразными аргами. Это было единственное решение до C ++ 11. Но теперь новые возможности C ++ 11 могут решить эту проблему более элегантно.
КСТАТИ. Сначала я увидел этот способ разрешения подписи в реализации библиотеки boost.pyton.
С помощью decompose_mem_fun_ptr
от 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.
}
за index_sequence
а также 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