Comments 14
Превращать вариадики в списки типов, мсье знает толк. Но у меня всё же вопрос: Зачем?
С вариадиками можно работать в нативном виде, без преобразования к рудиментарному списку типов. Хранить их не сложно, сделать псевдоконтейнер и всё.
template<typename… Args>
struct holder;
Этого достаточно для того чтобы работать как угодно с вариадиками.
Небольшой пример ideone.com/tb6Jm7 возможно натолкнет на дальнейшее развитие знаний.
С вариадиками можно работать в нативном виде, без преобразования к рудиментарному списку типов. Хранить их не сложно, сделать псевдоконтейнер и всё.
template<typename… Args>
struct holder;
Этого достаточно для того чтобы работать как угодно с вариадиками.
Небольшой пример ideone.com/tb6Jm7 возможно натолкнет на дальнейшее развитие знаний.
Да, конечно. Ответ: just for fun. Тут всё можно заменить на что-то вроде:
и т.д.
Спасибо за пример
template<typename T, typename ...Args>
struct Exists :
std::false_type
{
};
template<typename T, typename ...Args>
struct Exists<T, T, Args...> :
std::true_type
{
};
template<typename T, typename NotT, typename ...Args>
struct Exists<T, NotT, Args...> :
std::integral_constant<bool, Exists<T, Args...>::value>
{
};
и т.д.
Спасибо за пример
Вопрос 1:
А как по этому TypeList'у foreach'eм пройтись так чтоб без for'a? :)
Скажем в ваш TypeList ложим список классов со статическими функциями. А потом вызываем некую статическую do_func() из них.
Вопрос 2:
И вообще — можно ли аналогом такого вот TypeList'а организовать систему с плагинами? Ну, есть список объектов классов в каждом классе есть, скажем функция (не статичексая) begin() и end(). Нужно в определённых местах вызвать все begin() и все end() из списка. И делать это в критических по времени местах. Для этого не использовать vector и for, и естественно классы не содержат виртуальных функций. Так чтобы компилятор преобразовал это просто в вызов функций по порядку.
P.S. Признаться по правде не силен в темплейтах.
А как по этому TypeList'у foreach'eм пройтись так чтоб без for'a? :)
Скажем в ваш TypeList ложим список классов со статическими функциями. А потом вызываем некую статическую do_func() из них.
Вопрос 2:
И вообще — можно ли аналогом такого вот TypeList'а организовать систему с плагинами? Ну, есть список объектов классов в каждом классе есть, скажем функция (не статичексая) begin() и end(). Нужно в определённых местах вызвать все begin() и все end() из списка. И делать это в критических по времени местах. Для этого не использовать vector и for, и естественно классы не содержат виртуальных функций. Так чтобы компилятор преобразовал это просто в вызов функций по порядку.
P.S. Признаться по правде не силен в темплейтах.
Вопрос 1:Например, так:
А как по этому TypeList'у foreach'eм пройтись так чтоб без for'a? :)
Скажем в ваш TypeList ложим список классов со статическими функциями. А потом вызываем некую статическую do_func() из них.
Скрытый текст
template <class First, class Rest>
struct typelist;
struct nil;
// ----------------------------------------------------------------------------
template <class TypeCollection, template <class T> class StaticFunctor>
struct ForEach;
template <class Last, template <class T> class StaticFunctor>
struct ForEach<typelist<Last, nil>, StaticFunctor>
{
static void iterate()
{
StaticFunctor<Last>::call();
}
};
template <class First, class Rest, template <class T> class StaticFunctor>
struct ForEach<typelist<First, Rest>, StaticFunctor>
{
static void iterate()
{
StaticFunctor<First>::call();
ForEach<Rest, StaticFunctor>::iterate();
}
};
#define FOR_EACH(collection, action) \
ForEach<collection, action>::iterate()
#define DEFINE_STATIC_CALLER_FUNCTOR(function) \
namespace Call \
{ \
template <class T> \
struct function \
{ \
static void call() \
{ \
T::function(); \
} \
}; \
}
// ----------------------------------------------------------------------------
#include <iostream>
#define DEFINE_CLASS_SPECIMEN(name) \
struct name \
{ \
static \
void do_function() \
{ \
std::cout << #name "::function()\n"; \
} \
};
DEFINE_CLASS_SPECIMEN(A)
DEFINE_CLASS_SPECIMEN(B)
DEFINE_CLASS_SPECIMEN(C)
typedef typelist<A, typelist<B, typelist<C, nil> > > TestTypelist;
DEFINE_STATIC_CALLER_FUNCTOR(do_function)
int main()
{
FOR_EACH(TestTypelist, Call::do_function);
}
(В Бусте наверняка есть что-то готовое для этого.)
Вопрос 2:
...
Как-то я рисовал систему с плагинами и как раз определенные методы искал у классов, делая некоторые постконструкторы и преддеструкторы, вызов которых был аналогичен конструкторам и деструкторам (порядок вызова их в иерархии наследования), а для определения метода в классе хорошо подходит SFINAE. (По приведенной ссылке можно это посмотреть на реализациях поиска и вызова FinalizeConstruct / BeforeRelease).
А я надеялся что там не о том. Эх теперь придется читать :))
Вам виртуальный +1
Вам виртуальный +1
Да, кстати, может поздно и не важно, но статья действительно хорошая(как и реализация) и у меня возник вопрос ещё тогда по поводу FinalizeConstruct /BeforeRelease. Может, это смешно, но всё таки спрошу — почему не сделать что-то типа такого макроса для проверки есть ли метод в классе ?:
#define DEFINE_METHOD_CHECKER(RETURN_TYPE, METHOD_NAME, PARAMETERS) \
template<typename T> \
struct Is ## METHOD_NAME ## MemberFunctionExists \
{ \
private: \
typedef char True; \
typedef char (&False)[2]; \
template<typename U, RETURN_TYPE (U::*)PARAMETERS = &U::METHOD_NAME>\
struct Checker \
{ \
typedef True Type; \
}; \
template<typename U> \
static typename Checker<U>::Type Tester(const U*); \
static False Tester(...); \
public: \
enum { value = (sizeof(Tester(static_cast<const T*>(0))) == sizeof(True)) }; \
}
// IsMethodMemberFunctionExists<T>::value
DEFINE_METHOD_CHECKER(int, Method, (bool));
// IsTestMemberFunctionExists<T>::value
DEFINE_METHOD_CHECKER(int*, Test, (int&, char));
#include <iostream>
class Exists
{
public:
int Method(bool);
int* Test(int&, char);
};
class NotExists
{
};
int main()
{
std::cout << IsMethodMemberFunctionExists<Exists>::value << std::endl;
std::cout << IsTestMemberFunctionExists<Exists>::value << std::endl;
std::cout << IsMethodMemberFunctionExists<NotExists>::value << std::endl;
std::cout << IsTestMemberFunctionExists<NotExists>::value << std::endl;
}
Пределу совершенствования кода как правило нет :)
Да, такой макрос вполне мог существовать в рамках приведенного материала. Для меня макросы всеже менее приемлемы в коде, чем их законные языковые заменители. А так как в реализации не было много мест, где надо проверять наличие тех или иных методов класса и такая проверка не позиционировалась как вспомогательная для кода пользователя, то я просто написал пару почти аналогичных проверок, без заворачивания их в макросы и обобщения на все случаи. (В приведенных Вами реализациях еще бы const для методов поддержать… Это, думаю, не проблема:) А там еще вспоминается про volatile. Получается хорошее обобщение.) В моем случае я не стал просто делать такие усилия. Не скажу, что я долго задумывался над выбором простого почти «копипастного» небольшого решения или полноценного обобщения. Как правило к макросам стараюсь прибегнуть как можно реже, когда их выгода сильно перекрывает их недостатки или вообще их существование. В то же время полностью не чураюсь их, и если без них никак не достичь желаемого результата, то «пачкаюсь ими по полной» и осознанно, как пример тому еще один мой пост. Где без макросов достичь желаемого минимализма у меня никак не получилось.
Да, такой макрос вполне мог существовать в рамках приведенного материала. Для меня макросы всеже менее приемлемы в коде, чем их законные языковые заменители. А так как в реализации не было много мест, где надо проверять наличие тех или иных методов класса и такая проверка не позиционировалась как вспомогательная для кода пользователя, то я просто написал пару почти аналогичных проверок, без заворачивания их в макросы и обобщения на все случаи. (В приведенных Вами реализациях еще бы const для методов поддержать… Это, думаю, не проблема:) А там еще вспоминается про volatile. Получается хорошее обобщение.) В моем случае я не стал просто делать такие усилия. Не скажу, что я долго задумывался над выбором простого почти «копипастного» небольшого решения или полноценного обобщения. Как правило к макросам стараюсь прибегнуть как можно реже, когда их выгода сильно перекрывает их недостатки или вообще их существование. В то же время полностью не чураюсь их, и если без них никак не достичь желаемого результата, то «пачкаюсь ими по полной» и осознанно, как пример тому еще один мой пост. Где без макросов достичь желаемого минимализма у меня никак не получилось.
Спасибо за ответ.
А по поводу const, volatile:
Т.е. Вы просто указываете полную сигнатуру метода + возвращаемый тип. Удобно в том плане, что различие между этим и действительным объявлением метода в классе — только 2 запятые.
Кстати, я пошёл дальше и ради интереса — а можно ли узнать о методе, если он шаблонный?
И, всё работает, только не со студией:) 3й рядок не компилируется с ошибкой «An internal error has occurred in the compiler».
vc++(13 студия, Microsoft ® C/C++ Optimizing Compiler Version 18.00.21005.1 for x86): результат;
gcc(g++ 4.8.1 (g++ -Wall -std=c++11 -O2)): результат;
clang(clang 3.4 (clang++ -Wall -std=c++11 -O2)): результат;
На всякий случай — зарепортил
А по поводу const, volatile:
DEFINE_METHOD_CHECKER(int, Method, (bool) const);
DEFINE_METHOD_CHECKER(int*, Test, (int&, char) volatile);
Т.е. Вы просто указываете полную сигнатуру метода + возвращаемый тип. Удобно в том плане, что различие между этим и действительным объявлением метода в классе — только 2 запятые.
Кстати, я пошёл дальше и ради интереса — а можно ли узнать о методе, если он шаблонный?
#define DEFINE_TEMPLATE_METHOD_CHECKER(RETURN_TYPE, METHOD_NAME, PARAMETERS_INST, ARGS) \
template<typename T> \
struct Is ## METHOD_NAME ## TemplateMemberFunctionExists \
{ \
private: \
typedef char True; \
typedef char (&False)[2]; \
template<typename U, RETURN_TYPE (U::*)PARAMETERS_INST = &U::template METHOD_NAME ARGS >\
struct Checker \
{ \
typedef True Type; \
}; \
template<typename U> \
static typename Checker<U>::Type Tester(const U*); \
static False Tester(...); \
public: \
enum { value = (sizeof(Tester(static_cast<const T*>(0))) == sizeof(True)) }; \
}
// IsVCFailTemplateMemberFunctionExists<T>::value
DEFINE_TEMPLATE_METHOD_CHECKER(void, VCFail, (), <int>);
// IsVCOkTemplateMemberFunctionExists<T>::value
DEFINE_TEMPLATE_METHOD_CHECKER(void, VCOk, (int, bool), <int>);
#include <iostream>
class Exists
{
public:
template<typename T>
void VCFail();
template<typename T>
void VCOk(T, bool);
};
class NotExists
{
};
int main()
{
std::cout << IsVCOkTemplateMemberFunctionExists<Exists>::value << std::endl;
std::cout << IsVCOkTemplateMemberFunctionExists<NotExists>::value << std::endl;
//std::cout << IsVCFailTemplateMemberFunctionExists<Exists>::value << std::endl;
std::cout << IsVCFailTemplateMemberFunctionExists<NotExists>::value << std::endl;
}
И, всё работает, только не со студией:) 3й рядок не компилируется с ошибкой «An internal error has occurred in the compiler».
vc++(13 студия, Microsoft ® C/C++ Optimizing Compiler Version 18.00.21005.1 for x86): результат;
gcc(g++ 4.8.1 (g++ -Wall -std=c++11 -O2)): результат;
clang(clang 3.4 (clang++ -Wall -std=c++11 -O2)): результат;
На всякий случай — зарепортил
Кстати, спасибо за наведение на мысль. Учту в дальнейшем рефакторинге. Приведенный материал по линк я все же по небольшими порциями, но развиваю…
Попроще, чем у ilammy. Можно сделать так. Основной костяк:
Всё, теперь определяем наш «функтор» — по сути тело цикла для каждого типа
Используем:
Результат:
// Делается по "накатанной" схеме - определяем основу шаблона.
// В этом случае шаблон инстанцируется для
// - любого другого шаблонного класса, который имеет один параметр(<b>Caller</b>)
// - любого другого типа(<b>TL</b>)
template<template<typename> class Caller, typename TL>
struct ForEach
{
};
// Дальше - уточняем - на самом деле мы хотим иметь только:
// - любой другой шаблонный класса, как первый аргумент(<b>Caller</b>)
// - именно какую-то версию <b>TypeList</b>, как другой аргумент
template<template<typename> class Caller, typename ...Args>
struct ForEach<Caller, TypeList<Args...>>
{
typedef TypeList<Args...> TL;
void operator()() const
{
// Для Caller<T> - должен быть перегружен operator(),
// где T - это TL::Head. Можно выбрать и любую другую ф-ю.
Caller<typename TL::Head>()();
// Рекурсия - вызываем всё то же для хвоста
ForEach<Caller, typename TL::Tail>()();
}
};
// Говорим, что когда список типов пуст - ничего не делать
template<template<typename> class Caller>
struct ForEach<Caller, EmptyTypeList>
{
void operator()() const
{
}
};
Всё, теперь определяем наш «функтор» — по сути тело цикла для каждого типа
T
:// Для любого типа
template<typename T>
struct Call
{
void operator()() const
{
std::cout << typeid(T).name() << std::endl;
}
};
// Делаем полную специализацию шаблона для 'float', допустим
template<>
struct Call<float>
{
void operator()() const
{
std::cout << "Call<float>" << std::endl;
}
};
Используем:
typedef TypeList<double, float, float, double, int, char, char, int, char> TL;
ForEach<Call, TL>()();
Результат:
double
Call<float>
Call<float>
double
int
char
char
int
char
Компилирование рекурсивных алгоритмов при большом размере контейнера будет зависеть от установленной максимальной глубины рекурсии. К примеру, у меня clang-700-1.81 и по дефолту максимальная глубина равна 256. Соотвественно, при размере контейнера примерно >100 лимит рекурсии превышается (все это я, естественно, профайлил). Конечно, можно увеличить глубину, но размер контейнера все равно будет ограничен возможностями компилятора и ОС.
Sign up to leave a comment.
TypeList и Крестики-нолики