Search code examples
c++design-patternsinitializationrecurring-events

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


I'm developing a little game engine for Android with Android NDK and opengl es 2.0, recently the project is getting big, and I need to refactor some code, and I couldn't find a proper design pattern for the next problem.

On android when the app reach the OnPause() state the opengl context is destroyed, but the state of the variables and objects in java and c++ are maintained. so each time the player pauses and resumes the app I have to reinitializate the opengl part, buffers, shaders, vertex, etc.

I have classes like "Square" that makes "square objects", and each one has its own attributes, and each "square object" can be drawn, so the squares can access to static (opengl) members of the class, that are used to be properly rendered. So this static members must be initialized before objects can be drawn, I do it when the opengl context is created or recreated.

moreover each class has its own opengl attributes, so each class is initialized individually with its own parameters, so I want a design in what each class can set some initial parameters, pass or catch those parameters to initialize the static members of the class (I forgot to say that these parameters are private). But as I said before, these parameters need to be reinitialized each time the app is resumed.

currently I initialize these members individually like

Square::init(/*hardcoded parameters*/);
Circle::init(/*hardcoded parameters*/);
Triangle::init(/*hardcoded parameters*/);
Polygon::init(/*hardcoded parameters*/);
Shape::init(/*hardcoded parameters*/);
.
.
.
.
// Many other inits.....
.

and I want to write something like

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

So I want to set some (static/harcoded) variables to the class and then when the opengl context be created, the class be initialized in a "magic" way, and not having the need to code the call to an init method for each class.

I've tried to use inheritance, but the issue is that I need to initialize the class not the object, also tried to implement a static object and initialize this object in the cpp file, and store a pointer to the object in a vector when this is created in his contructor, in a vector that is in the object's own class, but this design has gave me many problems.

Does anyone know some design that can help me?

EDIT: the stucture of my classes

the init() function is really big because shader and frag parameters are paths file and I perform some task on them, pass the result of that perform to opengl and returns me a ID that is the program static variable, all clases with opengl part implement this same process, the parameter camera is just to attach it into a 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);
}

and maybe some structure I'd want is

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:

}

Solution

  • This is one more solution how your task can be solved. The idea is to have some initialization list (std::vector) of functions that should be called in yout Opengl_Initializer::init() :

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

    If we can put all your Square/Circle/Triangle... init functions into this list, your task become trivial - just iterate list and call all functions:

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

    You can add functions manually, for example, from int main():

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

    But I suggest that you need some arhitecture design that will make you able adding functions into initializer list without changing main or any other global code. To solve this task we can make small helper class that will register your init functions automatically:

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

    So you can declare instance of this class in your Square/Circle:

       struct Square
       {
          static OpenGLHelper_initializer __initializer;
       };
    

    And in your Square.cpp file:

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

    So, when program loads, all this initializer will be constructed and all your "init" function will be registered into initializer_list.

    This looks like more code, but it will make you able to add as many shapes as you need without changing Opengl_Initializer::init(); or main.cpp or any other global code

    Your can now remove init functions, if you dont like them and use lambdas:

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

    Here is complete source code (Updated with using static function) (but without cpp files - all in one):

    #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;
    }
    

    Output:

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

    Live test

    BTW, such way of initialization is used by QT's metatype system - it uses macros to simplify code

    UPDATE: As Ben suggested, we can eliminate small memory leak from bynamic link allocation if we will put initialization list into a static function. Here is new code