#c #templates #boost #bind #variadic-templates
#c #шаблоны #увеличить #привязать #variadic-шаблоны
Вопрос:
Я хочу знать, возможно ли использовать количество аргументов, переданных шаблону variadic, в качестве заполнителя в вызове boost::bind.
Что-то вроде этого:
template <typename ... Args>
boost::bind(amp;function, this, anArg, _1)); //If Args count equals 1
boost::bind(amp;function, this, anArg, _1, _2)); //If Args count equals 2
boost::bind(amp;function, this, anArg, _1, _2, _3)); //If Args count equals 3
Возможно ли это?
Спасибо
Комментарии:
1. Здесь есть хорошая реализация утилиты make_indice: http://preney.ca но мне трудно понять, как я могу использовать его с boost::arg<>
2. Эта ссылка была отличной для чтения, и
apply( Func, std::tuple )
функция может когда-нибудь пригодиться.3. Я вижу
typename... Args
. Используете ли вы C 11?
Ответ №1:
Определенно есть способ с частичной специализацией. ваш variadic не сразу определяет количество аргументов, верно? вы должны использовать рекурсию во время компиляции, в течение этого времени вы можете складывать свои аргументы, используя boost::mpl (или подсчитывать их, используя простое приращение целочисленной константы). затем в вашем последнем вызове рекурсии без переменных (с аргументом 0) вы вызываете mpl::size в вашем контейнере (или просто используете счетчик целых чисел, если вы выбрали этот способ), чтобы вызвать вызываемый объект, подобный другому ответу, который содержит все аргументы, плюс один параметр шаблона с целым значением в начале списка типов. и это то, на чем вы специализируетесь. вы создаете вызывающую функцию для каждого числа аргументов, которая будет вызывать правильную привязку в соответствии с ее специализированным количеством аргументов. (вызываемые структуры (частично) специализированы в соответствии с целым параметром шаблона number of argument. и хотя функция вызова принимает максимальное количество аргументов, она переносит только правильный вызов boost::bind (например, bind(..,_1,_2) для вызываемого<2, T1, T2, T3>), это не страшно, но я подтверждаю, что я использовал этот подход в C 03 в прошлом.
Ответ №2:
Возможно, вам следует объяснить, что вы хотите сделать, немного подробнее. Если вы просто ищете решение для обработки трех разных подписей, которые отличаются типами своих параметров, вы могли бы сделать что-то вроде этого:
template<typename signature>
struct callable;
template<typename P0, typename P1, typename P2>
struct callable<void (P0, P1, P2)>
{
void bind()
{
boost::bind(amp;callable::operator(), this, _1, _2, _3);
}
void operator()(P0, P1, P2) {}
};
Ответ №3:
Это не ответ на конкретную проблему, а хороший обходной путь для проблемы, которую вы, вероятно, пытаетесь решить.
Я столкнулся с той же проблемой при реализации универсального механизма делегирования. Мое решение состояло в том, чтобы использовать оболочку поверх только вызова bind, специализируясь на вариантах. Хотя это не решает проблему, это определенно сводит к минимуму избыточный код до простого вызова bind и, что наиболее важно, предоставляет мне систему делегирования на основе переменных параметров, которую я могу использовать везде.
template<class CALLBACK_TARGET_CLASS, typename RETURN_TYPE>
std::function<RETURN_TYPE()> BindFunction(RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(), CALLBACK_TARGET_CLASS* callbackTarget)
{
return std::bind(memberFunction, callbackTarget);
}
template<class CALLBACK_TARGET_CLASS, typename RETURN_TYPE, typename P0>
std::function<RETURN_TYPE()> BindFunction(RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(P0), CALLBACK_TARGET_CLASS* callbackTarget)
{
return std::bind(memberFunction, callbackTarget, std::placeholders::_1);
}
template<class CALLBACK_TARGET_CLASS, typename RETURN_TYPE, typename P0, typename P1>
std::function<RETURN_TYPE()> BindFunction(RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(P0, P1), CALLBACK_TARGET_CLASS* callbackTarget)
{
return std::bind(memberFunction, callbackTarget, std::placeholders::_1, std::placeholders::_2);
}
template<typename RETURNTYPE, typename... ARGS>
struct Delegate
{
std::function<RETURN_TYPE (ARGS...)> callbackFunction;
template<class CALLBACK_TARGET_CLASS>
void Bind(CALLBACK_TARGET_CLASS* callbackTarget, RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(ARGS...))
{
callbackFunction = BindFunction<CALLBACK_TARGET_CLASS, RETURN_TYPE, ARGS...>(memberFunction, callbackTarget);
}
void Callback(ARGS... params)
{
callbackFunction(params...);
}
};
Использование заканчивается примерно так..
class Foo
{
public:
void Bar(int x);
}
Foo foo;
Delegate<void, int> myDelegate;
myDelegate.Bind(amp;foo, amp;Foo::Bar);
myDelegate.Callback(3);
Ответ №4:
Прямое использование _1, _2, … с шаблоном variadic невозможно. Вместо этого вам нужно использовать расширенные макросы.
Однако вы можете обернуть эти заполнители в шаблонную фабрику, чтобы получить _1 с аргументом шаблона 1, _2 для 2 и т.д…
Такие реализации, как gcc / msvc, уже определяют заполнители как шаблонную структуру (соответственно std::_Placeholder и std::_Ph), поэтому вы можете определить свою фабрику таким образом:
struct ph_factory {
template<size_t holder>
static std::_Placeholder<holder> make_ph() {
return std::_Placeholder<holder>();
}
};
После этого вы можете расширить пакет параметров всеми необходимыми заполнителями :
struct tester {
template<size_t ... holders>
void test(int val) {
auto callable = std::bind(amp;tester::call, this, val, ph_factory::make_ph<holders>()...);
callable('a', 42, 'c');
}
void call(int v1, char c1, int v2, char c2) {
cout << "calling :" << v1 << " " << c1 << " " << v2 << " " << c2 << endl;
}
};
Таким образом, следующий код выведет «вызов: 10 c 42 a»
int main() {
tester t;
t.test<3,2,1>(10);
}
Использование трюков, таких как make_indice, предоставит вам возможность достичь вашей первоначальной цели.
Комментарии:
1. Никогда не полагайтесь на
_Names
, они полностью приватны для реализации, и у вас нет причин когда-либо даже думать о них. Есть способы получше, один из которых заключается в том, что вы помещаете все заполнители вtuple
иget<I>
их в дополнение к расширению пакета. Другой способ заключается в том, что вы регистрируете свой собственный заполнитель (по этой причине стандарт предоставляетis_placeholder
признак, на котором вы можете специализироваться.