2012-06-14 19 views
16

Sto cercando di capire come sia possibile assegnare un puntatore a funzioni a funzioni con un numero diverso di argomenti.C++: puntatore alle funzioni con numero variabile di argomenti

Ho un ciclo while che accetta un numero di funzioni diverse come un'istruzione condizionale, quindi invece di scrivere più cicli while con lo stesso codice all'interno mi piacerebbe averne uno con un puntatore a funzione. Tutte le funzioni sono in formato bool f(...). Credo che un certo codice sarà meglio illustrare quello che voglio dire:

int a, b, c, d; 
MyClass* my_class; 

typedef bool (MyClass::*my_fun_t)(); 
my_fun_t my_fun; 

if (condition1) 
    my_fun = &MyClass::function_one(); 
else if (condition2) 
    my_fun = &MyClass::function_two(a, b); 
else if (condition3) 
    my_fun = &MyClass::function_three(a, b, c); 
else if (condition4) 
    my_fun = &MyClass::function_four(a, b, c, d); 

while ((my_class->*my_fun)()) 
{ ... } 

Ora, questo, ovviamente, non funziona perché le funzioni hanno firme diverse. È possibile farlo funzionare in modo simile? I functoid sono qualcosa che dovrei guardare?

+0

Come sapresti come applicare quel puntatore a funzione se non sai quanti argomenti ci vogliono? – mfontanini

+0

Si noti che la sintassi dell'assegnazione a un puntatore di funzione è comunque non valida, si * sta * invocando * le funzioni, prendendo il risultato, ottenendo un puntatore alla sua memoria temporanea e assegnando * quel * al puntatore della funzione dichiarato in precedenza. –

+0

Sì, lo so che non è una sintassi valida. Mostra solo l'idea. – jaho

risposta

7

È possibile utilizzare std::function<> e std::bind().

#include <functional> 

using std::placeholders::_1; 

typedef std::function<bool(MyClass&)> my_fun_t; 
my_fun_t my_fun; 

if (condition1) 
    my_fun = std::bind(&MyClass::function_one, _1); 
else if (condition2) 
    my_fun = std::bind(&MyClass::function_two, _1, a, b); 
else if (condition3) 
    my_fun = std::bind(&MyClass::function_three, _1, a, b, c); 
else if (condition4) 
    my_fun = std::bind(&MyClass::function_four, _1, a, b, c, d); 

while (my_fun(my_class)) { ... } 

Questi presuppongono che si utilizzerà C++ 11. Se non puoi usare C++ 11 ma puoi usare TR1, sostituisci tutto std:: con std::tr1::. C'è anche un Boost implementation.

+1

Eccellente, grazie. Non posso usare C++ 11. Qualche ragione per preferire tr1 over boost (che sto usando comunque)? – jaho

+1

@Marian TR1 è incorporato nella libreria standard, boost no. – kennytm

1

Sto cercando di capire come essere in grado di assegnare un puntatore a funzioni a funzioni con un numero diverso di argomenti.

Non è possibile. I puntatori di funzione sono specifici per una firma di funzione. Questo è del tutto logico: come invochereste una funzione del genere? (Sì, C permette funzioni invoca senza specificare nella loro dichiarazione quanti parametri della funzione ha, ma questo non funziona in C++ in quanto sovverte il sistema di tipi.)

Sono functoid qualcosa che dovrei guardare?

Generalmente sì, ma non risolvono questo problema.

+0

Hmm, ma solo la "firma" di una funzione con argomenti variabili è ancora unica, non è vero? La parte degli argomenti delle variabili '...' è almeno un camuffamento di 'va_list' utilizzato all'interno dell'implementazione delle funzioni. Invocare tale funzione tramite un puntatore a funzione non dovrebbe (non dovrebbe) differire comunque dal richiamare tale funzione nel solito modo (con una lista variabile di argomenti). –

+0

@ g-makulik Gli argomenti variabili funzionano, ma questa è una firma specifica, non equivale al binding a diverse firme di funzioni. –

+0

Ah, capisco il tuo punto! Ma l'OP non ha menzionato esplicitamente il legame (potrebbe essere che ho letto il suo codice "pseudo" errato). Utilizzando le definizioni di interfaccia (polimorfismo) potrebbe ancora essere possibile ottenere ciò che l'OP vuole (inteso). –

3

Si desidera std::function, un oggetto funzione polimorfico e std::bind per creare oggetti funzione vincolando argomenti ai parametri di altri funtori.

Se non è possibile utilizzare C++ 11, quindi boost::function e boost::bind sono equivalenti, anche se leggermente più restrittivi.

std::function<bool()> my_fun; 

if (condition1) 
    my_fun = std::bind(&MyClass::function_one, my_class); 
else if (condition2) 
    my_fun = std::bind(&MyClass::function_two, my_class, a, b); 
else if (condition3) 
    my_fun = std::bind(&MyClass::function_three, my_class, a, b, c); 
else if (condition4) 
    my_fun = std::bind(&MyClass::function_four, my_class, a, b, c, d); 

while (my_fun()) 
{ ... } 
+1

Se è possibile utilizzare C++ 11, è anche possibile utilizzare le funzioni lambda ed estrarre le variabili dall'ambito di applicazione lessicale. Qualunque cosa tu preferisca, credo. – user268396

+0

@ user268396: Probabilmente. Per il momento, sono bloccato con un compilatore che non supporta lambda, quindi non ne so molto di loro. –

+0

La soluzione boost ha funzionato per me. Grazie. – jaho

4

questo funziona per me:

#include <iostream> 
#include <cstdarg> 

using namespace std; 

class MyInterface 
{ 
public: 
    virtual bool func(int argc, ...) = 0; 
}; 

class MyImpl : public MyInterface 
{ 
public: 
    virtual bool func(int argc, ...); 
}; 

bool MyImpl::func(int argc, ...) 
{ 
    va_list varargs; 
    va_start(varargs,argc); 
    cout << "Arguments passed:" << endl; 
    for(int i = 0; i < argc; ++i) 
    { 
     // expect double values 
     double val = va_arg(varargs,double); 
     cout << val << endl; 
    } 
    va_end(varargs); 
    return true; 
} 

typedef bool (MyInterface::*MyFunc)(int, ...); 

int main() { 

    MyImpl impl; 
    MyInterface* interface = &impl; 
    MyFunc pfunc = &MyInterface::func; 

    if(!(interface->*pfunc)(2,double(3.14),double(2.72))) 
    { 
     return 1; 
    } 
    return 0; 
} 

uscita:

Arguments passed: 
3.14 
2.72 

Ovviamente è possibile dichiarare e utilizzare i puntatori a funzione per le funzioni (iscrizioni a) utilizzando argomenti variabili.

Problemi correlati