2016-10-22 74 views
1

我正在开发Android的小游戏引擎与Android NDK和opengl ES 2.0,最近该项目变得越来越大,我需要重构一些代码,我不能'为下一个问题寻找合适的设计模式。初始静态成员的C++设计,任何时候需要

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

我有类似“方形”的“方形对象”,每个都有自己的类属性,并且可以绘制每个“方形对象”,所以方块可以访问该类的静态(opengl)成员,这些成员用于正确渲染。所以这个静态成员必须在绘制对象之前初始化,我在创建或重新创建opengl上下文时执行它。每个类都有自己的opengl属性,所以每个类都有自己的参数单独初始化,所以我想要一个设计,每个类可以设置一些初始参数,传递或捕获这些参数来初始化静态成员班(我忘了说这些参数是私人的)。但正如我之前所说,每次应用程序恢复时都需要重新初始化这些参数。

目前我初始化这些成员分别喜欢

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 

所以我想一些(静态/ harcoded)变量设置为分类,然后当将创建opengl上下文,该类将以“魔术”方式进行初始化,而不需要为每个类的init方法编写调用代码。

我试过使用继承,但问题是我需要初始化类而不是对象,也试图实现静态对象并在cpp文件中初始化此对象,并存储指向对象的指针在一个矢量中,当它是在他的构造器中创建的时候,是一个在该对象自己的类中的向量,但是这种设计给了我很多问题。

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

编辑:我的课

init()功能的stucture是非常大的,因为shaderfrag参数路径文件,我对他们执行一些任务,合格的结果进行,以OpenGL和返回我一个ID这是该程序静态变量,用OpenGL部分的所有clases实现这个同样的过程,参数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: 

} 
+0

将所有内容加入到一个Singleton中。 –

+0

@πάνταῥεῖ是的,我曾经想过,但也是一样的,我必须为每个类调用'init()',你会如何实现它? – quetzalfir

+0

C++没有办法迭代现有的类,如Java /。网有 - 如果你对此有所了解。所以你需要直接调用“init”或存储指针。你的类在“init”函数中有什么共同点? – Evgeniy

回答

1

这是一个多解你的任务是如何可以解决的。这个想法是有功能的一些初始化列表(标准::向量)应在YOUT Opengl_Initializer被称为::的init():

std::vector<std::function<void()>> initializer_list; 

如果我们能够把所有的广场/圆/三角...初始化函数在此列表中,你的任务就变得琐碎 - 只是迭代列表和电话的所有功能:

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

您可以手动添加功能,例如,从int main()中:

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

但我建议你需要一些架构设计,使您可以将功能添加到初始化程序列表中,而无需更改主要或任何其他全局代码。 为了解决这个任务,我们可以让小助手类,它会自动注册您的初始化函数:

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

所以,你可以在你方/圆声明这个类的实例:

struct Square 
    { 
     static OpenGLHelper_initializer __initializer; 
    }; 

而在你的Square.cpp文件:

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

所以,当程序加载,这一切的初始化将被构建和你的“初始化”功能将被注册成initializ er_list。

这看起来像更多的代码,但它会让你能够添加尽可能多的形状,而无需改变Opengl_Initializer :: init();或main.cpp中或任何其他全局代码

您现在可以卸下初始化函数,如果你不喜欢他们,并用lambda表达式:

// 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

BTW,初始化这样的方式使用Qt的元类型系统 - 它使用宏来简化代码

UPDATE: 作为本建议,我们可以消除bynamic路段分配,如果小内存泄漏我们将把初始化列表放入一个静态函数中。 Here is new code

+0

简直令人惊讶,这就是我一直在寻找,谢谢你 – quetzalfir

+0

不幸的是,这遭受了静态初始化顺序失败 - 你的全局变量在那里和每个地方都添加函数指针到一个还不存在的向量中。你已经使用一个泄漏的动态分配来解决它,一个更好的方法是在一个函数内定义单例向量(作为一个具有由引用返回的静态存储持续时间的本地),而不是全局或静态类成员。 –

+0

@BenVoigt,好主意,谢谢!你是对的,这个列表将成为小内存泄漏的原因 – Evgeniy

1

我建议版本系统,使初始化可以在时间的使用自动执行,但在跳过它非常便宜的方式在初始化早已已完成。喜欢的东西

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

这是一个很好的解决方案,但是我实例化了大量对象,因此对于程序必须检查init是否完成的每个对象,这可能是一个缺点? – quetzalfir

+1

@quetzalfir:但是这个检查是一个单独的整数比较,当你实际计划使用opengl对象时,你只需要它,任何opengl活动都会使一个比较的代价变得更加渺小。 –