2011-02-06 18 views
5

¿Hay alguna manera, puedo cambiar entre 2 conjuntos de funciones similares (C/C++) de manera efectiva? para explicar mejor lo que quiero decir, digamos que tengo 2 juegos de funciones globales como:¿Cómo cambiar entre 2 conjuntos de funciones en C++?

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

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

Y quiero poder "interruptor" en mi programa en tiempo de ejecución cuales uno es usado. PERO: No quiero tener uno si la condición en cada función, como:

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

Porque, espero que cada función se llama mucho en mi mainloop - por lo que sería preferible si podía hacer algo como esto (al inicio de mi mainloop o cuando se cambia someState):

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

y luego simplemente llaman

useFunctionsOfType _someCoolFunction(); 

espero que sea comprensible lo que quiero decir ... Mis antecedentes: estoy escribiendo una aplicación, que debería ser capaz de manejar OpenGL ES 1.1 y OpenGL ES 2.0 correctamente, pero no quiero escribir cada método de renderizado 2 veces (como: renderOpenGL1() y renderOpenGL2() prefiero escribir solo render()). Ya tengo métodos similares como: glLoadIdentity(); myLoadIdentity(); ... Pero necesito una forma de cambiar entre estos dos de alguna manera. ¿Hay alguna manera de lograr esto de una manera eficiente?

+0

Parece que los punteros a las funciones deberían ser el truco. – gablin

+7

Nota: no combine C y C++ de esta manera. Las posibles soluciones son muy diferentes según el idioma con el que estés trabajando. Por favor, elija uno! –

+0

Si cada función se llama 'mucho', entonces solo tiene que modificar el despachador dentro de' mucho() '. Por cierto, ¡este "mucho" debe ser una bestia peluda! http://hyperboleandahalf.blogspot.com/2010/04/alot-is-better-than-you-at-everything.html – Potatoswatter

Respuesta

2

En C (ya que parece que desea tanto C como C++) esto se hace con un puntero a las funciones.

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

que te pueden probablemente querrá cambiar las funciones de los grupos, así que es mejor establecer una matriz de punteros a funciones y pasar esa matriz alrededor.Si decide hacerlo, es posible que probablemente desee definir también una cierta macro para facilitar la lectura:

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; 

pero en este caso si no se pierden la comprobación estática de tipos de argumentos (y hay que inicializar la matriz , por supuesto).

supongo que en C++ tendrá instatiate dos objetos diferentes con la misma clase padre y diferente aplicación de sus métodos (pero no soy C prograammer ++!)

8

varias opciones, incluyendo (pero no limitados a):

  • punteros Uso de funciones.
  • Envuélvelas en clases y usa polimorfismo.
  • Tenga dos copias separadas del ciclo.

Pero por favor perfil de asegurar esto es en realidad un problema, antes de hacer cualquier grandes cambios en su código.

+0

+1 para el perfil antes de realizar cualquier cambio importante :) – neuro

1

La manera más fácil podría ser almacenar punteros a las funciones y cambiarlas de la demanda.

Pero la mejor manera es usar algo similar al patrón de diseño abstract factory. La implementación genérica agradable se puede encontrar en Loki library.

2

Puede utilizar los punteros de funciones. Puede leer mucho acerca de ellos si lo busca en google, pero brevemente un puntero de función almacena un puntero a la dirección de memoria de una función.

Los punteros de función se pueden usar de la misma manera que una función, pero se les puede asignar la dirección de diferentes funciones, lo que la convierte en una función de alguna manera "dinámica". Como ejemplo:

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

En C se hace normalmente esto con un struct que contienen punteros a funciones:

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; 

para cambiar la tabla de funciones activo, tendrá que utilizar:

ftable = &table_a; 

Para llamar t él funciona, tendrá que utilizar:

ftable->someCoolFunction(); 
2

Algo así como la función de impulso :: (std :: función) encajaría el proyecto de ley. Usando su ejemplo:

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

} 

compila con g ++ - 4.4.4, esta salidas:

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

La limitación más importante es que los tipos de F1, F2, etc no se puede cambiar, por lo que cualquier función que asigne para ellos debe tener la misma firma (es decir, void (int) en el caso de f2).

4

Como la pregunta parece estar interesada en una solución de C++ y nadie ha explicado la solución polimórfica (¿demasiado obvio?), Aquí va.

Definir una clase base abstracta con la API que necesita, y luego implementar una clase derivada para cada aplicación soportada:

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

Tenga en cuenta que este es exactamente el tipo de cosa que C++ fue diseñado para, por lo que si puede usar C++ aquí, esta es la forma más sencilla de hacerlo.

Ahora, un problema más sutil que podría enfrentar es si su versión 2.0 requiere que el proceso cargue una biblioteca vinculada dinámica en tiempo de ejecución con la implementación de la plataforma 2.0. En ese caso, solo es compatible con el cambio de API (cualquiera que sea la solución). En su lugar poner a cada clase concreta de OpenGL en su propia biblioteca enlazada y en cada uno proporciona una función de fábrica para crear esa clase:

OpenGlAbstract* create(); 

a continuación, cargar la biblioteca deseada en tiempo de ejecución y llama al método create() para acceder a la API.

+1

+1, comenzaba a preguntarme si la gente se mantendría en el puntero para funcionar y la matriz de punteros funcionaría durante mucho tiempo ... –

Cuestiones relacionadas