2011-02-06 11 views
5

C'è un modo, posso passare tra 2 set di funzioni simili (C/C++) in modo efficace? Per spiegare meglio quello che voglio dire, Diciamo che ho 2 set di funzioni globali come:Come passare tra 2 set di funzioni in C++?

void a_someCoolFunction(); 
void a_anotherCoolFunction(int withParameters); 
… 

void b_someCoolFunction(); 
void b_anotherCoolFunction(int withParameters); 
… 

E voglio in grado di "switch" nel mio programma in fase di esecuzione quale viene utilizzato. MA: Non voglio avere uno se la condizione alla ogni funzione, come:

void inline someCoolFunction(){ 
    if(someState = A_STATE){ 
     a_someCoolFunction(); 
    }else{ 
     b_someCoolFunction(); 
    } 
} 

Perché, mi aspetto che ogni funzione viene chiamata molto nella mia mainloop - per cui sarebbe preferibile se avrei potuto fare qualcosa come questo (a inizio del mio ciclo principale o quando si cambia someState):

if(someState = A_STATE){ 
    useFunctionsOfType = a; 
}else{ 
    useFunctionsOfType = b; 
} 

e poi semplicemente chiamare

useFunctionsOfType _someCoolFunction(); 

spero sua comprensibile Cosa intendo ... Il mio background: Sto scrivendo un'App, che dovrebbe essere in grado di gestire OpenGL ES 1.1 e OpenGL ES 2.0 entrambi correttamente, ma non voglio scrivere ogni metodo di rendering 2 volte (come: renderOpenGL1() e renderOpenGL2() Preferisco scrivere solo render()). Ho già metodi simili come: glLoadIdentity(); myLoadIdentity(); ... Ma ho bisogno di un modo per passare in qualche modo tra questi due. Esiste un modo per farlo in modo efficace?

+0

Suoni come puntatori di funzione dovrebbero fare il trucco. – gablin

+7

Nota: non confondere C e C++ in questo modo. Le potenziali soluzioni sono molto diverse a seconda della lingua con cui stai lavorando. Per favore sceglierne uno! –

+0

Se ogni funzione è denominata 'alot', basta modificare il dispatcher all'interno di' alot() '. A proposito, questo "molto" deve essere una bestia pelosa! http://hyperboleandahalf.blogspot.com/2010/04/alot-is-better-than-you-at-everything.html – Potatoswatter

risposta

2

In C (poiché sembra che si voglia sia C che C++) questo viene fatto con il puntatore alle funzioni.

// Globals. Default to the a_ functions 
void(*theCoolFunction)()   = a_someCoolFunction; 
void(*theOtherCoolFunction)(int) = a_anotherCoolFunction; 

// In the code ... 
{ 
    ... 
    // use the other functions 
    theCoolFunction = b_someCoolFunction; 
    theOtherCoolFunction = b_anotherCoolFunction; 
    ... 
} 

si potrebbe probabilmente vuole cambiare queste funzioni in gruppi, quindi è meglio impostare un array di puntatori a funzioni e passare tale matrice intorno.Se si decide di farlo, si potrebbe probabilmente voler definire anche alcune macro per facilitare la lettura:

void (*functions_a[2])(); 
    void (*functions_b[2])(); 

    void (**functions)() = functions_a; 

    .... 
    #define theCoolFunction()  functions[0]() 
    #define theOtherCoolFunction(x) functions[1](x) 
    .... 

    // switch grooup: 
    functions = functions_b; 

ma in questo caso si perde il controllo statico sulla tipi di argomenti (e si deve inizializzare l'array , ovviamente).

Credo che in C++ si avrà instatiate due oggetti diversi con la stessa classe padre e diversa implementazione per i loro metodi (ma non sono un C prograammer ++!)

8

diverse opzioni, tra cui (ma non solo):

  • puntatori utilizzare la funzione.
  • Avvolgili in classi e utilizza il polimorfismo.
  • Avere due copie separate del ciclo.

Ma per favore profilo per garantire questo è in realtà un problema, prima di di apportare grandi modifiche al codice.

+0

+1 per il profilo prima di apportare grandi cambiamenti :) – neuro

1

Il modo semplice può essere memorizzare i puntatori alle funzioni e modificarle in base alla domanda.

Ma il modo migliore è utilizzare qualcosa di simile al modello di progettazione abstract factory. La bella implementazione generica può essere trovata in Loki library.

2

È possibile utilizzare i puntatori di funzioni. Puoi leggerne molte su google, ma in breve tempo un puntatore a funzione memorizza un puntatore all'indirizzo di memoria di una funzione.

I puntatori di funzione possono essere utilizzati allo stesso modo di una funzione, ma è possibile assegnare l'indirizzo di diverse funzioni, rendendolo una funzione "dinamica". Ad esempio:

typedef int (*func_t)(int); 


int divide(int x) { 
    return x/2; 
} 

int multiply(int x) { 
    return x * 2; 
} 

int main() { 
    func_t f = ÷ 
    f(2); //returns 1 
    f = &multiply; 
    f(2); //returns 4 
} 
1

In C si dovrebbe in genere fare questo con un struct contenenti puntatori a funzione:

struct functiontable { 
    void (*someCoolFunction)(void); 
    void (*anotherCoolFunction)(int); 
}; 

const struct functiontable table_a = { &a_someCoolFunction, &a_anotherCoolFunction }; 
const struct functiontable table_b = { &b_someCoolFunction, &b_anotherCoolFunction }; 

const struct functiontable *ftable = NULL; 

per commutare la tabella di funzione attiva, utilizza:

ftable = &table_a; 

Per chiamare t lui funziona, tu useresti:

ftable->someCoolFunction(); 
2

Qualcosa come boost :: function (std :: function) si adatterebbe al conto. Usando il tuo esempio:

#include <iostream> 
#include <boost/function.hpp> //requires boost installation 
#include <functional> //c++0x header 


void a_coolFunction() { 

    std::cout << "Calling a_coolFunction()" << std::endl; 
} 

void a_coolFunction(int param) { 

    std::cout << "Calling a_coolFunction(" << param << ")" << std::endl; 
} 

void b_coolFunction() { 

    std::cout << "Calling b_coolFunction()" << std::endl; 
} 

void b_coolFunction(int param) { 

    std::cout << "Calling b_coolFunction(" << param << ")" << std::endl; 
} 
float mul_ints(int x, int y) {return ((float)x)*y;} 


int main() { 

    std::function<void()> f1; //included in c++0x 
    boost::function<void(int)> f2; //boost, works with current c++ 
    boost::function<float(int,int)> f3; 

    //casts are necessary to resolve overloaded functions 
    //otherwise you don't need them 
    f1 = static_cast<void(*)()>(a_coolFunction); 
    f2 = static_cast<void(*)(int)>(a_coolFunction); 

    f1(); 
    f2(5); 

    //switching 
    f1 = static_cast<void(*)()>(b_coolFunction); 
    f2 = static_cast<void(*)(int)>(b_coolFunction); 

    f1(); 
    f2(7); 

    //example from boost::function documentation. No cast required. 
    f3 = mul_ints; 
    std::cout << f3(5,3) << std::endl; 

} 

compilato con g ++ - 4.4.4, questo uscite:

Calling a_coolFunction() 
Calling a_coolFunction(5) 
Calling b_coolFunction() 
Calling b_coolFunction(7) 
15 

La più grande limitazione è che i tipi di f1, f2, ecc non può cambiare, in modo che qualsiasi funzione assegnata a loro deve avere la stessa firma (vale a dire void (int) nel caso di f2).

4

Poiché la domanda sembra interessata a una soluzione C++ e nessuno ha specificato la soluzione polimorfica (troppo ovvia?), Ecco qui.

Definire una classe base astratta con l'API si richiede, e quindi implementare una classe derivata per ogni implementazione supportata:

class OpenGLAbstract 
{ 
    public: 
     virtual ~OpenGLAbstract() {} 
     virtual void loadIdentity() = 0; 
     virtual void someFunction() = 0; 
}; 

class OpenGLEs11 : public OpenGLAbstract 
{ 
    public: 
     virtual void loadIdentity() 
     { 
      // Call 1.1 API 

     } 
     virtual void someFunction() 
     { 
      // Call 1.1 API 
     } 
}; 


class OpenGLEs20 : public OpenGLAbstract 
{ 
    public: 
     virtual void loadIdentity() 
     { 
      // Call 2.0 API 
     } 
     virtual void someFunction() 
     { 
      // Call 2.0 API 
     } 
}; 

int main() 
{ 
    // Select the API to use: 
    bool want11 = true; 
    OpenGLAbstract* gl = 0; 
    if (want11) 
     gl = new OpenGLEs11; 
    else 
     gl = new OpenGLEs20; 

    // In the main loop. 
    gl->loadIdentity(); 

    delete gl; 
} 

Si noti che questo è esattamente il tipo di cosa che C++ era destinato, quindi, se puoi usare C++ qui, questo è il modo più semplice per andare.

Ora un problema più sottile che potreste dover affrontare è se la versione 2.0 richiede al processo di caricare una libreria collegata dinamica in fase di esecuzione con l'implementazione della piattaforma 2.0. In tal caso, il solo supporto dell'interruttore API non è sufficiente (qualunque sia la soluzione). Invece mettere ogni classe concreta OpenGL nella propria libreria collegata ed in ogni forniscono una funzione di fabbrica per creare quella classe:

OpenGlAbstract* create(); 

quindi caricare la libreria desiderata in fase di esecuzione e chiamare il metodo create() per accedere all'API.

+1

+1, stavo cominciando a chiedermi se la gente sarebbe restata al puntatore alla funzione e alla serie di puntatori per funzionare a lungo ... –

Problemi correlati