3

В моем движке у меня есть простая система отражения, заполненная информацией о классах во время компиляции (т. Е. Построенная вокруг набора шаблонов, что позволяет мне автоматизировать процесс генерации метаинфо).Как передать произвольную функцию-член ЛЮБОГО класса в шаблон, чтобы разрешить его подпись?

Рассмотрим следующий пример:

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. Ницца.

Теперь мне нужно что-то идентичное для функции членов. «Идентичный» означает, что я могу добавить функцию так же, как сейчас добавляю свойства.

Моя первая мысль была VARIADIC шаблоны (мой двигатель построен с полной поддержкой 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::*)()) 
{ 
} 

Функция была «пересылается» рекурсивно (я знаю, что это не фактическая рекурсия) с одним параметром меньше каждый раз. Однако я не вижу, как это подходит для моего дела.

+1

Найдено в [Accept-любой-вид-на-вызываемая-и-и-ноу-аргумент типа] (http://stackoverflow.com/questions/21465394/accept-any-kind-of-callable-and-also-know-argument-type/21466644#comment32402978_21466644), http://coliru.stacked-crooked.com/a/00750bf7564ab6d4 тоже может помочь. – Jarod42

+0

Спасибо за этот чрезвычайно полезный ресурс! –

ответ

1

Там нет необходимости рекурсии, просто использовать пакет расширения:

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)...}; 
} 

мы помещаем пакет расширения внутри рамно-Init-список, чтобы гарантировать, что звонки в addArgumentType<...> сделаны в правильном порядке.

+1

Вы должны добавить не менее 1 элемента в 'dummy', если нет Args. – Jarod42

+1

Мне нравится это решение. Однако я не могу догадаться, почему вы это сделали: (addArgumentType (), 0). Что означает это «0»? И каков тип элементов в манекене []? Мне также интересно, если это будет работать в случае передачи функции без параметров. –

+0

@MateuszGrzejek: '(addArgumentType (), 0) ...' использовать оператор запятой для вызова 'addArgumentType ()' и оценивать '0' для расширения. вам просто нужно иметь '{0, (addArgumentType (), 0) ...}' для поддержки 0 arg. 'auto' -' int'. – Jarod42

1
... 

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::*)() 
} 

Я не знаком с variadic args. Это было единственным решением перед C++ 11. Но теперь новые возможности C++ 11 могут решить эту проблему более элегантно.

BTW. Сначала я увидел этот способ разрешения подписи в реализации библиотеки boost.pyton.

1

Использование 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 

 Смежные вопросы

  • Нет связанных вопросов^_^