Я не уверен, как я могу использовать RAII в полной мере в моей ситуации. Это ситуация:
Я создаю базовый рендер. Геометрия описывается классом Geometry, к которому могут быть добавлены вершины. Для того чтобы объекты Geometry использовались для рендеринга, его сначала нужно скомпилировать (т. Е. Для геометрии будет создан VBO). Компиляция (и декомпиляция) объектов геометрии выполняется с помощью объекта Renderer, декомпиляция должна быть выполнена после того, как вы закончите работу с объектами геометрии. Если декомпиляция не будет выполнена, будут утечки памяти.
Вот пример того, что я описываю:
Renderer renderer; // the renderer object
{
Geometry geometry;
// add vertices
// and play around with material, etc.
if(!renderer.compile(geometery); // compile the geometery, so we can use it
{
cerr << "Failed to compile geometry!\n";
}
// now we can use it...
renderer.render(geometry, RenderType::TriangleStrips);
} // if I don't call renderer.decompile(geometry) here, I will get a leak
То, что я пытаюсь сделать, это декомпилировать мою геометрию, без явного указания отладчику декомпилировать ее. Это просто для уменьшения утечек памяти. Моей первой мыслью было использовать RAII, но если бы я это сделал, класс Geometry потребовал бы класс Renderer, который кажется довольно грязным. Поскольку мне потребуется ссылка на объект Renderer, который скомпилировал геометрию.
Еще одна альтернатива, о которой я подумал, — заставить рендерер создавать геометрию, но это приведет к тому, что геометрические объекты будут размещаться динамически (то есть с новым), и это также выглядит довольно грязно.
Я также подумал о том, чтобы поместить объект дескриптора в геометрию, например unique_ptr, в абстрактный объект дескриптора.
например
class GeometeryHandle
{
virtual ~GeometeryHandle() = 0;
};
Что на самом деле может работать, так как оно также может быть использовано для хранения GLuint
внутри ручки. Хотя я не уверен, что это уместно, так как я мог бы просто декомпилировать геометрию напрямую через ссылку Renderer. то есть он будет делать то же самое, если я вызову его напрямую через деструктор.
Как мне правильно спроектировать это, чтобы случайно не декомпилировать геометрию?
Не ясно, кто должен нести ответственность за что в вашем дизайне. Это первый шаг в выяснении того, как использовать любую форму управления ресурсами: решить, кто за что отвечает.
Например, вы говорите, что «Геометрия описывается классом Geometry, к которому могут быть добавлены вершины». Хорошо, но как это относится к данным после компиляции? Если пользователь добавляет вершины в Geometry
после компиляции эти вершины автоматически помещаются в скомпилированные данные? Или скомпилированные данные полностью отделены от Geometry
класс после компиляции, так что меняется на Geometry
класс не обновляет скомпилированные данные?
Для меня это звучит так, как будто вы объединяете две совершенно разные идеи: GeometryBuilder
а также Renderable
, GeometryBuilder
это то, что вы кладете данные вершины. Затем вы берете это и создаете Renderable
из этого. Renderable
это оптимизированная форма данных, хранящихся в GeometryBuilder
, Объект полностью независим от GeometryBuilder
который создал это. Renderable
это то, что вы можете сделать с
Итак, вы бы сделали это:
GeometryBuilder meshBuilder;
meshBuilder.AddVertex(...);
...
Renderable renderMesh = render.CreateRenderable(meshBuilder);
После этого meshBuilder
не зависит от renderMesh
, Вы можете удалить один и другой в порядке. Вы можете «скомпилировать» renderMesh
несколько раз и получите идентичные копии одних и тех же данных. И так далее.
Вы можете использовать вспомогательный класс:
class CompileHandler
{
private:
Geometry& g;
Renderer& r;
public:
CompileHandler(Geometry& _g, Renderer& _r) : g(_g), r(_r)
{
}
~CompileHandler()
{
r.decompile(g);
}
};
И вы можете использовать его следующим образом:
{
Geometry geometry;
CompileHandler ch(geometry,renderer);
// add vertices
// and play around with material, etc.
if(!renderer.compile(geometery); // compile the geometery, so we can use it
{
cerr << "Failed to compile geometry!\n";
}
// now we can use it...
renderer.render(geometry, RenderType::TriangleStrips);
// decompilation is automatic on destruction of the CompileHandler object
}
Создайте более надежную иерархию:
GeometryCompiler
^
|
| inherits
|
Renderer
При компиляции геометрии геометрический компилятор (здесь, средство визуализации) уведомляет геометрию о том, что она была скомпилирована (и устанавливает GeometryCompiler
указатель внутри геометрии на него, компилятор).
Затем, при разрушении геометрии, если указатель не равен нулю, может потребоваться GeometryCompiler
декомпилировать это.
RAII требует, чтобы деструкторы выполняли операции «отменить».
В вашем случае геометрия разрушена, но рендер выживет. Единственный триггер, который у вас есть, — это деструктор Geometry, который должен знать, что откомпилировать, чтобы вызвать его для декомпиляции.
Но поскольку геометрия не предназначена для того, чтобы узнать о редерсе, вам, скорее всего, нужен вспомогательный класс (назовем его Compile_guard
) который должен быть создан сразу после завершения геометрии, принимая Geometry и Render в качестве параметров и вызывая Render :: compile при построении и Render :: decompile при уничтожении:
Renderer renderer; // the renderer object
{
Geometry geometry;
// add vertices
// and play around with material, etc.
Compile_guard guard(render, geometry);
if(!guard); // Invalid compilation ....
{
cerr << "Failed to compile geometry!\n";
return; // this is exception safe!
}
// now we can use it...
renderer.render(geometry, RenderType::TriangleStrips);
} //here guard will decompile
По поводу Compile_guard, это может быть что-то вроде
class Compile_guard
{
public:
Compile_guard(Render& r, Geometry& g) :render(&r), geometry(&g), good(false)
{ good = render->compile(*geometry); }
~Compile_guard()
{ if(good) render->decompile(*geometry); }
explicit operator bool() const { return good; }
Compile_guard(const Compile_guard&) =delete; //just avoid copy and assign.
Compile_guard& operator=(const Compile_guard&) =delete;
private:
Render* render;
Geometry* geometry;
bool good;
};