初始化静态成员的 C++ 设计,任何时候都需要

C++ design for init static members, any time required

我正在用AndroidNDK和opengl es 2.0为Android开发一个小游戏引擎,最近项目越来越大,我需要重构一些代码,但我不能'找不到适合下一个问题的设计模式。

On android 当应用程序达到 OnPause() 状态时,opengl 上下文将被销毁,但 java 和 c++ 中的变量和对象的状态将保持不变。所以每次玩家暂停和恢复应用程序时,我都必须重新初始化 opengl 部分、缓冲区、着色器、顶点等。

我有class像"Square"这样的"square objects",每个都有自己的属性,每个"square object"都可以画,所以方块可以访问用于正确呈现的 class 的静态 (opengl) 成员。所以必须在绘制对象之前初始化这个静态成员,我在创建或重新创建 opengl 上下文时这样做。

而且每个 class 都有自己的 opengl 属性,所以每个 class 都是用自己的参数单独初始化的,所以我想要一个设计,每个 class 可以设置一些初始值参数,传递或捕获那些参数来初始化class的静态成员(我忘了说这些参数是私有的)。但是正如我之前所说,每次恢复应用程序时都需要重新初始化这些参数。

目前我像

一样单独初始化这些成员
Square::init(/*hardcoded parameters*/);
Circle::init(/*hardcoded parameters*/);
Triangle::init(/*hardcoded parameters*/);
Polygon::init(/*hardcoded parameters*/);
Shape::init(/*hardcoded parameters*/);
.
.
.
.
// Many other inits.....
.

我想写一些类似

的东西
// here all the classes with opengl part are initialized
// all init methods of each class are called here, with their respective parameters
Opengl_Initializer::init(); // <--- magic way, no other init calls

所以我想为 class 设置一些 (static/harcoded) 变量,然后在创建 opengl 上下文时,class 在 "magic" 中初始化方式,并且不需要为每个 class.

编写对 init 方法的调用

我尝试过使用继承,但问题是我需要初始化 class 而不是对象,还尝试实现一个静态对象并在 cpp 文件中初始化此对象,并存储在他的构造函数中创建时指向向量中对象的指针,在对象自己的向量中class,但这种设计给我带来了很多问题。

有谁知道可以帮助我的设计吗?

编辑:我的 classes

的结构

init() 函数非常大,因为 shaderfrag 参数是路径文件,我对它们执行一些任务,将执行结果传递给 opengl 和 returns我一个ID是程序静态变量,所有带opengl部分的类都实现相同的过程,参数camera只是将它附加到相机

class Square {
    // static variable all classes have
    static GLuint program;

    // other glparameters not in common, initialized in the same static init() method
    static GLint uniform1;
    static GLint uniform2;

public;
    // the static init function has the same header on all the classes
    static init(const char* shader, const char* frag, const char *camera);
}

也许我想要的结构是

class Square {
    static GLuint program;
    static const char *vertex = "hardcode";
    static const char *frag = "hardcode";
    static const char *cam = "harcode";

    static init();

    /// or somethig like
    static Initializer init(
            "harcode shader", "hardcode frag", "hardcode camera", 
            [&] (void) ->void {
                //this is the init function
            }
    );

public:

}

我建议使用版本控制系统,以便可以在使用时自动执行初始化,但在初始化已经完成时以非常便宜的方式跳过它。像

int global_gl_generation = 0; // increment each time you recreate the context

inline bool check_gl_generation(int& local_generation)
{
    if (local_generation == global_gl_generation)
        return false;

    local_generation = global_gl_generation;
    return true;
}

然后在每个 class,

class Square
{
    // static variable all classes have
    static int generation_inited;

    static GLuint program;
    static GLint uniform1;
    static GLint uniform2;

    static init(const char* shader, const char* frag, const char *camera);

public;
    void draw() override
    {
         if (check_gl_generation(generation_inited)) init(...);

         // use program, uniform1, uniform2
    }
};

这是解决您的任务的另一种解决方案。这个想法是有一些应该在你 Opengl_Initializer::init() :

中调用的函数的初始化列表 (std::vector)
std::vector<std::function<void()>> initializer_list;

如果我们可以将所有 Square/Circle/Triangle... init 函数放入此列表,您的任务将变得微不足道 - 只需迭代列表并调用所有函数:

// inside Opengl_Initializer::init()
for (auto fn : initializer_list)
   fn();

您可以手动添加函数,例如,from int main():

initializer_list.push_back(&Square::init); 
...

但我建议您需要一些架构设计,使您能够在不更改 main 或任何其他全局代码的情况下将函数添加到初始化列表中。 为了解决这个任务,我们可以制作一个小助手 class 来自动注册您的初始化函数:

struct OpenGLHelper_initializer
{
    OpenGLHelper_initializer(std::function<void()> fn)
    {
        initializer_list.push_back(fn);
    }
};

因此您可以在 Square/Circle:

中声明此 class 的实例
   struct Square
   {
      static OpenGLHelper_initializer __initializer;
   };

并且在您的 Square.cpp 文件中:

OpenGLHelper_initializer Square::__initializer(&Square::init);

因此,当程序加载时,将构造所有此初始化程序,并且所有 "init" 函数将注册到 initializer_list。

这看起来像更多的代码,但它可以让您根据需要添加任意数量的形状,而无需更改 Opengl_Initializer::init();或 main.cpp 或任何其他全球代码

如果您不喜欢 init 函数并使用 lambdas,您现在可以删除它们:

// in square.cpp
OpenGLHelper_initializer Square::__initializer([](){
  std::cout << "Square is initialized now" << std::endl; 
});

这是完整的源代码(使用静态函数更新)(但没有 cpp 文件 - 全部在一个文件中):

#include <iostream>
#include <memory>
#include <vector>

using namespace std;


/////////////////////////////////////////
// opengl_helper.h
// this is some manager class that knows what should be initialized later
struct OpenGLHelper
{
    typedef std::function<void()> function_type;

    static std::vector<function_type>& get_initialization_list();

    static void register_initializer(function_type fn);

    static void run_init();
};
// helper class that will register some function at construction time
struct OpenGLHelper_initializer
{
    OpenGLHelper_initializer(OpenGLHelper::function_type fn)
    {
        OpenGLHelper::register_initializer(fn);
    }
};
/////////////////////////////////////////
//opengl_helper.cpp

// using this function we will make our initializer_list be constructued
// before adding anything into it
std::vector<OpenGLHelper::function_type>& OpenGLHelper::get_initialization_list()
{
    static std::vector<function_type> initializer_list;
    return initializer_list;    
}

// function that puts initializer into a list. 
void OpenGLHelper::register_initializer(OpenGLHelper::function_type fn)
{

    get_initialization_list().push_back(fn);
}

void OpenGLHelper::run_init()
{
    for (auto fn : get_initialization_list())
        fn();
}

/////////////////////////////////////////
// figure.h
// here is sample class that will be registered for initialization
struct Square
{
    static int to_be_initialized;

    // static member that will register Square class to be initialized
    static OpenGLHelper_initializer __initializer;
};

/////////////////////////////////////////
// Square.cpp
int Square::to_be_initialized = 0;
// this is the most interesting part - register square into initializer list
OpenGLHelper_initializer Square::__initializer([](){
    Square::to_be_initialized = 15; 
    std::cout << "Called Square::init: " << to_be_initialized << std::endl; 
});

int main() 
{
    std::cout << "Before initialization : " << Square::to_be_initialized << std::endl;
    OpenGLHelper::run_init();
    std::cout << "After  initialization : " << Square::to_be_initialized << std::endl;
    return 0;
}

输出:

Before initialization : 0
Called Square::init: 15
After  initialization : 15

Live test

顺便说一句,QT 的元类型系统使用了这种初始化方式——它使用宏来简化代码

更新: 正如 Ben 所建议的,如果我们将初始化列表放入静态函数中,我们可以从 bynamic link 分配中消除小的内存泄漏。 Here is new code