OpenGL arabellek nesneleri dizisi için C ++ sarmalayıcı sınıfı - yalnızca yapıcılar
Benzer bir soru sorulmuştur burada , ben de aynı şeyi yapmaya çalışıyorum. Fakat
- Türeterek farklı bir yaklaşım deniyorum
std::array
ve - Bu, yalnızca inşaatçılar hakkında çok odaklanmış bir sorudur.
İşte benim gl_wrap.h
:
// header guards snipped
#include <array>
#include <algorithm>
#include <cstring>
#include <GL/gl.h>
namespace gl_wrap {
// aliases for style consistency and to mark as parts of gl_wrap
using gl_name = GLuint;
using gl_enum = GLenum;
template<size_t N>
class buffer_objects : public std::array<gl_name, N> {
public:
buffer_objects() noexcept;
~buffer_objects();
buffer_objects(const buffer_objects&) = delete;
buffer_objects operator=(const buffer_objects&) = delete;
buffer_objects(buffer_objects&& from) noexcept;
buffer_objects& operator=(buffer_objects&& from) noexcept;
};
template<size_t N>
buffer_objects<N>::buffer_objects() noexcept
{
glGenBuffers(N, this->data());
}
template<size_t N>
buffer_objects<N>::~buffer_objects()
{
glDeleteBuffers(N, this->data());
}
template<size_t N>
buffer_objects<N>::buffer_objects(buffer_objects<N>&& from) noexcept
: std::array<gl_name, N>(std::move(from))
{
memset(from.data(), 0, N * sizeof(gl_name));
}
template<size_t N>
buffer_objects<N>& buffer_objects<N>::operator=(buffer_objects<N>&& from)
noexcept
{
std::array<gl_name, N>::operator=(std::move(from));
memset(from.data(), 0, N * sizeof(gl_name));
return *this;
}
}
// namespace gl_wrap
Bu kod / yaklaşımla ilgili bazı spesifik sorularım, varsa,
- Hata işleme tasarım kararı: kurucular hata kontrolü yapmalı mı yoksa istenirse arayana hata kontrolü bırakmalı mıyım?
- Tasarım kararı: Burada şablonları kullanmak iyi bir fikir mi? Çok farklı boyutta kullanıyorsam bu sorunlu kod şişmesine yol açar mı yoksa
buffer_objects
verimli mi? Bunu profilleme yoluyla değerlendirebilir miyim? - Are
memcpy
geçerli olmayan sahibi devlet SağDeğerler koymak s değerinde o, yoksa olmayan sahibi olarak kalanlar SağDeğerler davranabilirsiniz? - Temel sınıfın hareket oluşturucusunu doğru bir şekilde kullanıyor muyum?
Yanıtlar
Bu kod / yaklaşımla ilgili bazı spesifik sorularım, varsa,
TAMAM MI.
Hata işleme tasarım kararı: kurucular hata kontrolü yapmalı mı yoksa istenirse arayana hata kontrolü bırakmalı mıyım?
Ya nesne doğru bir şekilde başlatılmış ve kullanıma hazırdır ya da atması gerekir. İki aşamalı başlatma (inşa edip kontrol et), kullanıcıya doğru şeyi yapması için açık bıraktığı için kötü bir fikirdir. Nesnenizin kötüye kullanılmayacağından emin olmalısınız, kullanıcının sizi kötüye kullanmamasına güvenmemelisiniz.
Tasarım kararı: Burada şablonları kullanmak iyi bir fikir mi?
Kullanacaksanız, std::array
gerçekten bir seçeneğiniz yok.
Çok sayıda farklı boyutta buffer_object kullanıyorsam bu sorunlu kod şişmesine yol açar mı yoksa verimli mi? Bunu profilleme yoluyla değerlendirebilir miyim?
Potansiyel olarak bu, yöntemlerin farklı türler için derlenmesine yol açacaktır. Ancak bu derleyiciye bağlıdır ve bazı modern derleyiciler bunu optimize edebilir. Ama sen de neyi karşılaştırıyorsun?
Memcpys, rdeğerleri geçerli bir sahip olmayan duruma koymaya değer mi, yoksa r değerlerine sahip olmayan artıklar olarak davranabilir miyim?
src
Değerleri sıfırlamanız gerektiğini düşünüyorum . Aksi takdirde yıkıcı isimleri serbest bırakacaktır. Alternatif olarak, nesnenin geçerli olup olmadığı hakkında daha fazla durum depolayabilir ve geçerli glDeleteBuffers()
durumda olan nesne için koşullu çağrıyı yapabilirsiniz .
Ama burada da bir hata olduğunu düşünüyorum. Az önce değerleri kopyaladınız. Ancak dst
kopyadan önceki taraftaki değerleri serbest bırakmadınız, bu nedenle orada saklanan isimleri kaybettiniz.
Bir std :: dizisinin kendi hareketine sahip olmadığını unutmayın (veriler dinamik olarak tahsis edilmemiş nesneye yerel olduğundan). Temeldeki nesneleri kaplar arasında hareket ettirir.
Temel sınıfın hareket oluşturucusunu doğru bir şekilde kullanıyor muyum?
Evet, ligit görünüyor.
Öneriler.
std::array
Bir üyeyi ondan miras almasını tercih ederim .
Yapardım:
#ifndef THORSANVIL_GL_WRAPPER_H
#define THORSANVIL_GL_WRAPPER_H
#include <GL/gl.h>
#include <array>
#include <algorithm>
#include <cstring>
namespace ThorsAnvil::GL {
template<size_t N>
class BufferObjects
{
std::array<GLuint, N> buffer;
bool valid;
public:
BufferObjects() noexcept;
~BufferObjects();
// Note: These are non copyable objects.
// Deleting the copy operations.
BufferObjects(BufferObjects const&) = delete;
BufferObjects operator=(BufferObjects const&) = delete;
// Note: The move is as expensive as a copy operation.
// But we are doing a logical move as you
// can not have two objects with the same resource.
BufferObjects(BufferObjects&& from) noexcept;
BufferObjects& operator=(BufferObjects&& from) noexcept;
// Reset an invalid object.
// Note: If object is valid no action.
void reset();
};
template<size_t N>
BufferObjects<N>::BufferObjects() noexcept
: valid(false)
{
reset();
}
template<size_t N>
BufferObjects<N>::~BufferObjects()
{
if (valid) {
glDeleteBuffers(N, buffer.data());
}
}
template<size_t N>
BufferObjects<N>::BufferObjects(BufferObjects<N>&& from) noexcept
{
// Move the resources from the other object.
std::move(std::begin(from.buffer), std::end(from.buffer), std::begin(buffer));
// Maintain the correct valid states
// The rhs is no longer in a valid state and has no resources.
valid = from.valid;
from.valid = false;
}
template<size_t N>
BufferObjects<N>& BufferObjects<N>::operator=(BufferObjects<N>&& from)
noexcept
{
// The standard copy and swap not efficient.
// So we should do a test for self assignment
if (this != &from)
{
// Destroy then re-create this object.
// Call destructor and then placement new to use
// the move copy constructor code to set this value.
~BufferObjects();
new (this) BufferObjects(std::move(from));
}
return *this;
}
template<size_t N>
void BufferObjects::reset()
{
if (!valid) {
glGenBuffers(N, buffer.data());
valid = true;
}
}