Аргументът за заместител на Boost bind е равен на броя на аргументите на вариатичния шаблон

Искам да знам дали е възможно да използвам броя на аргументите, предадени на променлив шаблон като контейнер в извикване на boost::bind.

Нещо като това:

template <typename ... Args>

boost::bind(&function, this, anArg, _1));         //If Args count equals 1
boost::bind(&function, this, anArg, _1, _2));     //If Args count equals 2
boost::bind(&function, this, anArg, _1, _2, _3)); //If Args count equals 3

Възможно ли е това?

Благодаря ти


person Blizter    schedule 09.11.2011    source източник
comment
Тук има хубава реализация на помощна програма make_indice: http://preney.ca/paul/2011/10/16/applying-stdtuple-to-functors-efficiently/, но ми е трудно да разбера как мога да го използвам с boost::arg‹›   -  person Blizter    schedule 09.11.2011
comment
Тази връзка беше страхотно четиво и функцията apply( Func, std::tuple ) може да бъде полезна някой ден.   -  person deft_code    schedule 11.11.2011
comment
Виждам typename... Args. Използвате ли C++11?   -  person kennytm    schedule 18.11.2011


Отговори (4)


Определено има начин с частична специализация. вашият variadic не знае броя на аргументите веднага, нали? трябва да използвате рекурсия по време на компилиране, през това време можете да стекате аргументите си с помощта на boost::mpl (или да ги преброите, като използвате просто увеличение на интегралната константа). след това в последното ви извикване на невариатична рекурсия (с 0 аргумент) вие извиквате mpl::size на вашия контейнер (или просто използвате интегралния брояч, ако сте избрали този начин), за да извикате Callable като другия отговор, който носи всички аргументи , плюс един неразделен параметър на шаблона в началото на списъка с типове. и това е, което сте специализирали. правите извикващ за всеки брой аргументи, който ще извика правилното свързване според своя специализиран брой аргументи. (извикваемите структури са (частично) специализирани според броя на параметрите на интегралния шаблон на аргумента. и въпреки че функцията Call приема максималния брой аргументи, тя обгръща само правилното извикване на boost::bind, например bind(.., _1,_2) за Callable‹2, T1, T2, T3>) не е ужасно, но потвърждавам, че съм използвал този подход в C++03 в миналото.

person Lightness1024    schedule 23.01.2013

Може би трябва да обясните какво искате да направите малко по-подробно. Ако просто търсите решение за обработка на три различни подписа, които се различават по своите типове параметри, можете да направите нещо подобно:

template<typename signature>
struct callable;

template<typename P0, typename P1, typename P2>
struct callable<void (P0, P1, P2)>
{
    void bind()
    {
        boost::bind(&callable::operator(), this, _1, _2, _3);
    }

    void operator()(P0, P1, P2) {}
};
person 0xbadf00d    schedule 17.11.2011

Това не е отговор на конкретния проблем, а добро решение за проблема, който вероятно се опитвате да разрешите.

Сблъсках се със същия проблем при прилагането на общ механизъм за делегиране. Моето решение беше да използвам обвивка само върху извикването за свързване, като го специализирам за вариациите. Въпреки че не решава проблема, определено минимизира излишния код само до извикването за свързване и най-важното ми дава делегирана система, базирана на различни параметри, която мога да използвам навсякъде.

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(&foo, &Foo::Bar);

myDelegate.Callback(3);
person Shammi    schedule 20.07.2013

Директното използване на _1, _2, ... не е възможно с променлив шаблон. Вместо това трябва да използвате експанзивни макроси.

Можете обаче да опаковате тези заместители в шаблонна фабрика, за да получите _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(&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;
    }
};

Така че следният код ще изведе "calling:10 c 42 a"

int main() {
    tester t;
    t.test<3,2,1>(10);
}

Използването на трикове като make_indice ще ви даде възможност да постигнете първоначалната си цел.

person Kiwi    schedule 20.02.2014
comment
Никога не разчитайте на _Names, те са изцяло частни за изпълнението и нямате причина дори да мислите за тях. Има по-добри начини, като единият е да поставите всички контейнери в tuple и get<I> с разширението на пакета. Другото е, че регистрирате свой собствен заместител (стандартът предоставя характеристиката is_placeholder поради тази причина, която можете да специализирате. - person Xeo; 20.02.2014