Аргумент заполнителя Boost bind, равный количеству переменных аргументов шаблона

#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 признак, на котором вы можете специализироваться.