Как переключаться между 2 наборами функций в C ++? - PullRequest
5 голосов
/ 07 февраля 2011

Есть ли способ, которым я могу эффективно переключаться между 2 подобными наборами функций (C / C ++)? Чтобы лучше объяснить, что я имею в виду, скажем, у меня есть 2 набора глобальных функций, таких как:

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

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

И я хочу иметь возможность "переключаться" в моей программе во время выполнения, какая из них используется. НО: я не хочу иметь один, если условие при каждой функции, например:

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

Потому что, я ожидаю, что каждая функция вызывается много в моем mainloop - поэтому было бы предпочтительнее, если бы я мог сделать что-то вроде этого (при запуске mainloop или при изменении someState):

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

, а затем просто позвоните

useFunctionsOfType _someCoolFunction();

Надеюсь, это понятно, что я имею в виду… Моя справочная информация: я пишу приложение, которое должно уметь обрабатывать OpenGL ES 1.1 и OpenGL ES 2.0 одновременно, но я не хочу писать каждый метод рендеринга 2 раза (например: renderOpenGL1() и renderOpenGL2() Я бы предпочел написать только render()). У меня уже есть похожие методы: glLoadIdentity(); myLoadIdentity();… Но нужен способ как-то переключаться между этими двумя. Есть ли способ сделать это эффективным способом?

Ответы [ 7 ]

8 голосов
/ 07 февраля 2011

Несколько вариантов, включая (но не ограничиваясь):

  • Использовать указатели функций.
  • Оберните их в классы и используйте полиморфизм.
  • Иметь две отдельные копии цикла.

Но, пожалуйста, профиль , чтобы убедиться, что это на самом деле проблема, за до вы внесете какие-либо значительные изменения в свой код.

4 голосов
/ 07 февраля 2011

Поскольку вопрос, похоже, интересует решение C ++, и никто не прописал полиморфное решение (слишком очевидно?), Здесь идет речь.

Определите абстрактный базовый класс с требуемым API, а затем реализуйте производный класс для каждой поддерживаемой реализации:

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

Обратите внимание, что это именно то, для чего предназначен C ++, поэтому, если здесь можно использовать C ++, это самый простой способ.

Теперь более тонкая проблема, с которой вы можете столкнуться, - это если ваша версия 2.0 требует, чтобы процесс загружал динамически связанную библиотеку во время выполнения с реализацией платформы 2.0. В этом случае просто поддержки переключения API недостаточно (какое бы решение не было). Вместо этого поместите каждый конкретный класс OpenGL в свою собственную связанную библиотеку и в каждом предоставьте фабричную функцию для создания этого класса:

OpenGlAbstract* create();

Затем загрузите нужную библиотеку во время выполнения и вызовите метод create () для доступа к API.

2 голосов
/ 07 февраля 2011

Что-то вроде boost :: function (std :: function) будет соответствовать всем требованиям.Используя ваш пример:

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

}

Скомпилировано с g ++ - 4.4.4, это выдает:

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

Самое большое ограничение в том, что типы f1, f2 и т. Д. Не могут изменяться, поэтомулюбая функция, которую вы им назначаете, должна иметь одинаковую сигнатуру (т. е. void (int) в случае f2).

2 голосов
/ 07 февраля 2011

В C (так как кажется, что вам нужны и C, и C ++), это делается с помощью указателя на функции.

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

Возможно, вы захотите переключить эти функции в группы, поэтому вам лучше установить массив указателей на функции и передать этот массив. Если вы решите сделать это, вы, возможно, захотите также определить некоторый макрос, чтобы облегчить чтение:

   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;

но в этом случае вы потеряете статическую проверку типов аргументов (и, конечно, вам придется инициализировать массив).

Полагаю, в C ++ у вас будут два разных объекта с одним и тем же родительским классом и разной реализацией их методов (но я не программист C ++!)

2 голосов
/ 07 февраля 2011

Вы можете использовать функции указателей.Вы можете прочитать о них много, если вы в Google, но кратко указатель на функцию хранит указатель на адрес памяти функции.

Указатели функций могут использоваться так же, как и функции, но могут быть назначены адреса различных функций, что делает их несколько «динамическими» функциями.Как пример:

typedef int (*func_t)(int);


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

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

int main() {
    func_t f = &divide;
    f(2); //returns 1
    f = &multiply;
    f(2); //returns 4
}
1 голос
/ 07 февраля 2011

В C вы обычно делаете это с struct, содержащим указатели на функции:

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;

Чтобы переключить таблицу активных функций, вы должны использовать:

ftable = &table_a;

Для вызова функций вы должны использовать:

ftable->someCoolFunction();
1 голос
/ 07 февраля 2011

Простой способ - хранить указатели на функции и изменять их по требованию.

Но лучший способ - использовать нечто похожее на абстрактный заводской шаблон проектирования. Хорошую обобщенную реализацию можно найти в библиотеке Loki .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...