【问题标题】:How to switch between 2 function sets in C++?如何在 C++ 中的 2 个函数集之间切换?
【发布时间】:2011-06-22 10:50:38
【问题描述】:

有没有办法,我可以有效地在 2 个相似的函数集 (C/C++) 之间切换? 为了更好地解释我的意思,假设我有两组全局函数,例如:

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

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

而且我希望能够在运行时在我的程序中“切换”使用哪一个。但是:我不想在 每个 函数中都有一个 if 条件,例如:

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

因为,我希望每个函数在我的主循环中都被多次调用 - 所以如果我可以做这样的事情(在我的主循环开始时或更改 someState 时)会更好:

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

然后简单地调用

useFunctionsOfType _someCoolFunction();

我希望它可以理解我的意思……我的背景:我正在编写一个应用程序,它应该能够正确处理 OpenGL ES 1.1 和 OpenGL ES 2.0 - 但我不想编写每个渲染方法 2 次(例如:@ 987654326@ 和renderOpenGL2() 我宁愿只写render())。我已经有类似的方法,例如:glLoadIdentity(); myLoadIdentity(); ...但是需要一种方法来以某种方式在这两者之间切换。 有什么方法可以有效地做到这一点吗?

【问题讨论】:

  • 听起来像函数指针应该可以解决问题。
  • 注意:请不要将 C 和 C++ 混为一谈。潜在的解决方案因您使用的语言而异。请选择一个!
  • 如果每个函数都调用alot,那么你只需要修改alot()里面的dispatcher即可。顺便说一句,这个alot一定是个毛茸茸的野兽! hyperboleandahalf.blogspot.com/2010/04/…
  • if(someState = A_STATE) 应该是if(someState == A_STATE)。只是说。
  • @Constantin:它没有被禁止,但是 C++ 解决方案可能很难在 C 中实现...

标签: c++ c function performance


【解决方案1】:

几个选项,包括(但不限于):

  • 使用函数指针。
  • 将它们包装在类中,并使用多态性。
  • 有两个独立的循环副本。

但请配置文件以确保这实际上是个问题,您对代码进行任何大的更改之前。

【讨论】:

    【解决方案2】:

    简单的方法是存储指向函数的指针,并根据需要更改它们。

    但更好的方法是使用类似于abstract factory 设计模式的东西。很好的通用实现可以在Loki library 中找到。

    【讨论】:

      【解决方案3】:

      您可以使用函数指针。如果你用谷歌搜索,你可以读到很多关于它们的信息,但简单来说,函数指针存储了一个指向函数内存地址的指针。

      函数指针的使用方式与函数相同,但可以分配不同函数的地址,使其成为某种“动态”函数。举个例子:

      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
      }
      

      【讨论】:

        【解决方案4】:

        在 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++ 程序员!)

        【讨论】:

          【解决方案5】:

          在 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();
          

          【讨论】:

            【解决方案6】:

            像 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 等的类型不能改变,所以你分配给它们的任何函数都必须具有相同的签名(即 f2 的情况下为 void(int))。

            【讨论】:

              【解决方案7】:

              由于这个问题似乎对 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。

              【讨论】:

              • +1,我开始怀疑人们是否会长时间停留在函数指针和函数指针数组...
              猜你喜欢
              • 2016-06-10
              • 1970-01-01
              • 2021-01-25
              • 2011-02-19
              • 1970-01-01
              • 2016-03-04
              • 1970-01-01
              • 1970-01-01
              • 2017-04-01
              相关资源
              最近更新 更多