初始化静态成员的 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()
函数非常大,因为 shader
和 frag
参数是路径文件,我对它们执行一些任务,将执行结果传递给 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
顺便说一句,QT 的元类型系统使用了这种初始化方式——它使用宏来简化代码
更新:
正如 Ben 所建议的,如果我们将初始化列表放入静态函数中,我们可以从 bynamic link 分配中消除小的内存泄漏。 Here is new code
我正在用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()
函数非常大,因为 shader
和 frag
参数是路径文件,我对它们执行一些任务,将执行结果传递给 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
顺便说一句,QT 的元类型系统使用了这种初始化方式——它使用宏来简化代码
更新: 正如 Ben 所建议的,如果我们将初始化列表放入静态函数中,我们可以从 bynamic link 分配中消除小的内存泄漏。 Here is new code