2011-11-09 14 views
10

Desidero sapere se è possibile utilizzare il numero di argomenti passati a un modello variadic come segnaposto in una chiamata boost :: bind.Boost argomento segnaposto uguale al numero di argomenti Variadic Template

Qualcosa di simile a questo:

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 

è possibile?

Grazie

+4

C'è una bella implementazione di un'utilità make_indice qui: [http://preney.ca] (http://preney.ca/paul/2011/10/16/applying-stdtuple-to-functors-efficiently/) ma sto facendo fatica a capire come posso usarlo con boost :: arg <> – Blizter

+0

Quel link è stato una buona lettura, e la funzione 'apply (Func, std :: tuple)' potrebbe tornare utile un giorno . –

+0

Vedo 'typename ... Args'. Stai usando C++ 11? – kennytm

risposta

1

C'è sicuramente un modo con la specializzazione parziale. il tuo variadic non conosce il numero di argomenti subito giusto? devi usare la ricorsione in fase di compilazione, durante questo tempo puoi impilare i tuoi argomenti usando boost :: mpl (o contarli usando un semplice incremento costante integrale). quindi nell'ultima chiamata di ricorsione non-variadica (con 0 arg) chiami mpl :: size sul tuo contenitore (o usa semplicemente il contatore integrale se hai scelto in quel modo) per chiamare un Callable come l'altra risposta, che porta tutte le argomenti, più un parametro di modello integrale all'inizio della lista dei tipi. e questo è ciò che sei specializzato. si effettua un chiamante per ogni numero di argomenti che chiamerà il binding corretto in base al suo numero specializzato di argomenti. (le strutture Callable sono (parzialmente) specializzate in base al numero di parametri del parametro integrale dell'argomento e anche se la funzione Chiama prende il numero massimo di argomenti, racchiude solo la corretta chiamata boost :: bind per esempio il binding (.. , _1, _2) per il Callable < 2, T1, T2, T3>) non è terribile, ma confermo che ho usato questo approccio in C++ 03 in passato.

0

Forse si dovrebbe spiegare che cosa si vuole fare in un po 'più in dettaglio. Se siete solo in cerca di una soluzione per gestire tre firme diverse che si differenziano dai loro tipi di parametri, si potrebbe fare qualcosa di simile:

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) {} 
}; 
0

Questa non è una risposta al problema specifico, ma una buona soluzione per il problema che si sta tentando di risolvere.

Ho riscontrato lo stesso problema durante l'implementazione di un meccanismo di delegato generico. La mia soluzione era usare un wrapper in cima solo al bind call, specializzandolo per le variazioni. Anche se non risolve il problema, riduce al minimo il codice ridondante alla sola chiamata al bind e, soprattutto, mi dà un sistema di delegati basato su parametri variabili che posso usare ovunque.

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

Uso finisce noi alla ricerca come questo ..

class Foo 
{ 
public: 
    void Bar(int x); 
} 

Foo foo; 
Delegate<void, int> myDelegate; 

myDelegate.Bind(&foo, &Foo::Bar); 

myDelegate.Callback(3); 
0

Utilizzando _1, _2, ... direttamente non è possibile con il modello variadic. È necessario utilizzare invece macro espansive.

Tuttavia, è possibile avvolgere questi segnaposto in una factory con modelli per ottenere _1 con l'argomento template 1, _2 per 2, ecc.

implementazioni come gcc/msvc già definiscono segnaposto struct come su modelli (rispettivamente std :: _ Segnaposto e std :: _ Ph) in modo da poter definire si fabbrica in questo modo:

struct ph_factory { 
    template<size_t holder> 
    static std::_Placeholder<holder> make_ph() { 
     return std::_Placeholder<holder>(); 
    } 
}; 

Questa definito, è possibile espandere un parametro pacchetto con tutti i segnaposto che si desidera:

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

Così il seguente codice di uscita "chiamando: 10 c 42 un"

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

L'utilizzo di trucchi come make_indice ti consentirà di raggiungere il tuo obiettivo originale.

+2

Mai fare affidamento su '_Names', sono completamente privati ​​dell'implementazione e non hai motivo di pensarci nemmeno. Ci sono modi migliori, uno dei quali è quello di inserire tutti i segnaposto in una 'tupla' e' get 'loro con l'espansione del pacchetto. L'altro è che si registra il proprio segnaposto (lo standard fornisce la caratteristica 'is_placeholder' per questo motivo, che è possibile specializzare. – Xeo

Problemi correlati