2016-10-22 77 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活動都會使一個比較的代價變得更加渺小。 –