1

私はAndroid NDKとopengl es 2.0を使ってAndroid用の小さなゲームエンジンを開発しています。最近、プロジェクトは大きくなり、コードをリファクタリングする必要がありました。次の問題のための適切な設計パターンを見つけることができません。いつも静的なメンバのためのC++デザイン

AndroidでOnPause()状態に到達すると、OpenGLコンテキストは破棄されますが、javaおよびC++の変数とオブジェクトの状態は維持されます。プレイヤーが一時停止してアプリケーションを再開するたびに、私はOpenGLの部分、バッファー、シェーダー、頂点などを再初期化する必要があります。

「スクエア」のような「正方形のオブジェクト」を作るクラスがあり、属性、および各「四角いオブジェクト」を描くことができるので、四角形は、適切にレンダリングされたクラスの静的(opengl)メンバにアクセスできます。ですから、オブジェクトを描画する前にこの静的メンバーを初期化しなければなりません。OpenGLコンテキストが作成または再作成されたときにそれを行います。

さらに、各クラスには独自のopengl属性があるため、各クラスは独自のパラメータで個別に初期化されるため、各クラスが初期パラメータを設定できるように設計したり、これらのパラメータを渡したりキャッチしてクラス(私はこれらのパラメータがプライベートであると言うことを忘れていた)。しかし、私が前に言ったように、これらのパラメータは、アプリが再開されるたびに再初期化する必要があります。

現在だから私は、ときにクラスにいくつかの(静的/ harcoded)の変数を設定し、したい、私は個別に

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 

のようなものを書きたいですOpenGLコンテキストを作成し、クラスを「魔法」の方法で初期化し、各クラスのinitメソッドへの呼び出しをコーディングする必要はありません。

私は継承を使用しようとしましたが、オブジェクトではなくクラスを初期化する必要があり、静的オブジェクトを実装し、このオブジェクトをcppファイルで初期化し、これがコンストラクタで作成されたときに、オブジェクト自身のクラスにあるベクトルで、ベクター内で生成されますが、この設計は私に多くの問題をもたらしました。

誰も私を助けることができるいくつかのデザインを知っていますか?

EDIT:私のクラスのstucture

shaderfragパラメータはパスのファイルであり、私は彼らにいくつかのタスクを実行するためinit()機能は本当に大きいですが、その結果を渡すの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

すべてを単一のシングルトンに結合します。 –

+0

@πάνταῥεῖはい、私はそれについて考えていましたが、同じです。各クラスに対して 'init()'を呼び出さなければなりません。どうすれば実装できますか? – quetzalfir

+0

C++には、Java /のような既存のクラスを反復する方法がありません。ネットは持っている - それについて鍛えている場合。したがって、直接、または格納されたポインタによって "init"を呼び出す必要があります。あなたのクラスは "init"関数で共通するものがありますか? – Evgeniy

答えて

1

のようなものはこれはあなたのタスクを解決することができる方法をもう一つの解決策です。アイデアはいくつかの初期化リスト(STD ::ベクトル)のyout Opengl_Initializerで呼び出されるべき機能の::のinit()することです:私たちはすべてのあなたの広場/サークル/トライアングルを置くことができる場合

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

を...このリストにINIT機能は、あなたのタスクは些細になる - すべての機能だけ反復リストおよびコール:

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

しかし、私:あなたはint型のmain()から、例えば、手動で機能を追加することができます

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

あなたがメインまたは他のグローバルコードを変更することなく、関数をイニシャライザリストに追加できるアーキテクチャデザインが必要です。

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

したがって、プログラムが読み込まれると、この初期化子がすべて構築され、すべての「init」関数がinitializeに登録されますer_list。

これはもっとコードに似ていますが、Opengl_Initializer :: init()を変更することなく、必要なだけ多くの図形を追加することができます。 main.cppにまたは任意の他のグローバルコードまたは

あなたは今、あなたがそれらを好まない場合は、初期化機能を削除し、ラムダを使用することができます。

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

は(静的関数を使用して更新)完全なソースコードである(ただし、

#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 
0123オールインワン) - cppのファイルなし

Live test

ところで、初期化のような方法は、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アクティビティが1つの比較コストを犠牲にする場合にのみ必要です。 –

関連する問題