2011-11-09 12 views
10

Quiero saber si es posible usar el número de argumentos pasados ​​a una plantilla variadic como marcador de posición en una llamada boost :: bind.Boost bind argumento de marcador de posición igual al número de argumentos de plantilla variable

Algo como esto:

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 

Es esto posible?

Gracias

+4

Hay una buena implementación de una utilidad make_indice aquí: [http://preney.ca] (http://preney.ca/paul/2011/10/16/applying-stdtuple-to-functors-efficiently/) pero estoy teniendo dificultades para entender cómo puedo usarlo con boost :: arg <> – Blizter

+0

Ese enlace fue una gran lectura, y la función 'apply (Func, std :: tuple)' puede ser útil algún día . –

+0

Veo 'typename ... Args'. ¿Estás usando C++ 11? – kennytm

Respuesta

1

Hay sin duda es una manera con la especialización parcial. su variadic no conoce la cantidad de argumentos de inmediato ¿no? tienes que usar la recursión en tiempo de compilación, durante este tiempo puedes apilar tus argumentos usando boost :: mpl (o contarlos usando un incremento de constante integral simple). luego en su última llamada de recursión no variadica (con 0 arg) llama a mpl :: size en su contenedor (o simplemente usa el contador integral si elige esa forma) para llamar a un Callable como la otra respuesta, que contiene todos los argumentos, más un parámetro de plantilla integral al comienzo de la lista de tipos. y eso es lo que te especializas hace una llamada para cada número de argumentos que llamará al enlace correcto según su número especializado de argumentos. (las estructuras invocables están (parcialmente) especializadas de acuerdo con el número de parámetro de plantilla integral de argumentos. Y aunque la función de llamada toma el número máximo de argumentos, solo envuelve la llamada boost :: bind correcta, por ejemplo, el enlace (.. , _1, _2) para el Callable < 2, T1, T2, T3>) no es terrible, pero confirmo que he utilizado este enfoque en C++ 03 en el pasado.

0

tal vez debería explicar lo que quiere hacer en un poco más de detalle. Si sólo está buscando una solución para manejar tres firmas diferentes que se diferencian por sus tipos de parámetros, se podía hacer algo así:

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

Esto no es una respuesta al problema específico, sino una buena solución para el problema que probablemente intente resolver.

Me encontré con el mismo problema al implementar un mecanismo delegado genérico. Mi solución fue usar un contenedor en la parte superior de solo la llamada de enlace, especializándolo para las variaciones. Aunque no resuelve el problema, definitivamente minimiza el código redundante solo para la llamada de enlace y lo más importante es que me da un sistema delegado basado en varios parámetros que puedo usar en cualquier lugar.

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 termina con nosotros con este aspecto ..

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

Foo foo; 
Delegate<void, int> myDelegate; 

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

myDelegate.Callback(3); 
0

Usando _1, _2, ... directamente no es posible con la plantilla variadic. Necesita usar macros expansivas en su lugar.

Sin embargo, puede envolver estos marcadores de posición en una fábrica con plantilla para obtener _1 con el argumento de plantilla 1, _2 para 2, etc.

implementaciones como gcc/msvc ya se definen los marcadores de posición struct como plantilla (respectivamente std :: _ marcador de posición y std :: _ Ph) para que pueda definir que la fábrica de esta manera:

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

Esto define, se puede ampliar un paquete de parámetros con todos los marcadores de posición que desee:

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

Así que el siguiente código de salida "llamando al: 10 c 42 una"

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

El uso de trucos como make_indice le dará la posibilidad de lograr su objetivo original.

+2

Nunca confíe en '_Names', son completamente privados para la implementación y no tiene ninguna razón para pensar siquiera en ellos. Hay formas mejores, una de ellas es rellenar todos los marcadores de posición en una 'tupla' y' obtener 'con la expansión del paquete. El otro es que usted registra su propio marcador de posición (el estándar proporciona el rasgo 'is_placeholder' por este motivo, que puede especializarse. – Xeo

Cuestiones relacionadas