C ++ дизайн для статических членов init, в любое время

Я разрабатываю небольшой игровой движок для Android с Android NDK и opengl es 2.0, в последнее время проект набирает обороты, и мне нужно реорганизовать некоторый код, и я не смог найти подходящий шаблон дизайна для следующей проблемы.

На 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

Поэтому я хочу установить некоторые (статические / скрытые) переменные для класса, а затем, когда будет создан контекст opengl, класс будет инициализирован «магическим» способом, и при этом не будет необходимости кодировать вызов метода init для каждого учебный класс.

Я пытался использовать наследование, но проблема в том, что мне нужно инициализировать класс, а не объект, также пытался реализовать статический объект и инициализировать этот объект в файле cpp и сохранять указатель на объект в векторе, когда это создается в его конструкторе, в векторе, который находится в собственном классе объекта, но этот дизайн доставил мне много проблем.

Кто-нибудь знает какой-нибудь дизайн, который может мне помочь?

РЕДАКТИРОВАТЬ: структура моих классов

init() функция действительно большая, потому что shader а также frag параметры — это файлы путей, и я выполняю над ними какую-то задачу, передаю результат этого выполнения в opengl и возвращает мне 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:

}

1

Решение

Это еще одно решение, как ваша задача может быть решена. Идея состоит в том, чтобы иметь некоторый список инициализации (std :: vector) функций, которые должны вызываться в yout Opengl_Initializer :: init ():

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

Если мы сможем поместить все ваши функции квадрата / круга / треугольника … init в этот список, ваша задача станет тривиальной — просто переберите список и вызовите все функции:

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

Таким образом, вы можете объявить экземпляр этого класса в вашем Square / Circle:

   struct Square
{
static OpenGLHelper_initializer __initializer;
};

И в вашем файле Square.cpp:

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

Таким образом, когда программа загружается, весь этот инициализатор будет создан, и вся ваша функция «init» будет зарегистрирована в initializer_list.

Это похоже на больше кода, но оно позволит вам добавлять столько фигур, сколько вам нужно, без изменения Opengl_Initializer :: init (); или main.cpp или любой другой глобальный код

Теперь вы можете удалить функции инициализации, если они вам не нравятся и вы используете лямбды:

// 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 — он использует макросы для упрощения кода

ОБНОВИТЬ:
Как предположил Бен, мы можем устранить небольшую утечку памяти из-за динамического распределения ссылок, если поместим список инициализации в статическую функцию. Вот новый код

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

По вопросам рекламы [email protected]