OpenGL arabellek nesneleri dizisi için C ++ sarmalayıcı sınıfı - yalnızca yapıcılar

Aug 18 2020

Benzer bir soru sorulmuştur burada , ben de aynı şeyi yapmaya çalışıyorum. Fakat

  1. Türeterek farklı bir yaklaşım deniyorum std::arrayve
  2. 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,

  1. Hata işleme tasarım kararı: kurucular hata kontrolü yapmalı mı yoksa istenirse arayana hata kontrolü bırakmalı mıyım?
  2. 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_objectsverimli mi? Bunu profilleme yoluyla değerlendirebilir miyim?
  3. Are memcpygeçerli olmayan sahibi devlet SağDeğerler koymak s değerinde o, yoksa olmayan sahibi olarak kalanlar SağDeğerler davranabilirsiniz?
  4. Temel sınıfın hareket oluşturucusunu doğru bir şekilde kullanıyor muyum?

Yanıtlar

5 MartinYork Aug 18 2020 at 04:43

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::arraygerç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?

srcDeğ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 dstkopyadan ö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::arrayBir ü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;
    }
}