OpenGL 버퍼 객체 배열을위한 C ++ 래퍼 클래스 — 생성자 만
비슷한 질문이 여기에 있었는데 저도 같은 일을하려고합니다. 그러나
- 에서 파생하여 다른 접근 방식을 시도
std::array
하고 있습니다. - 이것은 생성자에 대해서만 매우 집중된 질문입니다.
여기 내 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
이 코드 / 접근법에 대한 몇 가지 구체적인 질문은 해당되는 경우
- 오류 처리 설계 결정 : 생성자가 발생해야합니까, 아니면 원하는 경우 호출자에게 오류 검사를 남겨야합니까?
- 디자인 결정 : 여기서 템플릿을 사용하는 것이 좋은 생각입니까? 다른 크기를 많이 사용하는 경우 문제가있는 코드 부풀림이 발생합니까
buffer_objects
, 아니면 효율적입니까? 프로파일 링을 통해 이것을 평가할 수 있습니까? memcpy
rvalue를 유효한 비 소유 상태에 두는 것이 가치가 있습니까 , 아니면 rvalue를 비 소유 잔여 물로 취급 할 수 있습니까?- 기본 클래스의 이동 생성자를 올바르게 사용하고 있습니까?
답변
이 코드 / 접근법에 대한 몇 가지 구체적인 질문은 해당되는 경우
확인.
오류 처리 설계 결정 : 생성자가 발생해야합니까, 아니면 원하는 경우 호출자에게 오류 검사를 남겨야합니까?
개체가 올바르게 초기화되어 사용할 준비가되었거나 throw되어야합니다. 2 단계 초기화 (구성 후 확인)는 올바른 작업을 수행하기 위해 사용자에게 열려 있기 때문에 나쁜 생각입니다. 당신은 당신의 물건이 남용되지 않도록해야합니다.
디자인 결정 : 여기서 템플릿을 사용하는 것이 좋은 생각입니까?
사용하려는 경우 std::array
선택의 여지가 없습니다.
크기가 다른 buffer_objects를 많이 사용하는 경우 문제가있는 코드 부풀림이 발생합니까, 아니면 효율적입니까? 프로파일 링을 통해 이것을 평가할 수 있습니까?
잠재적으로 이로 인해 다른 유형에 대해 메서드가 컴파일됩니다. 그러나 이것은 컴파일러에 따라 다르며 일부 최신 컴파일러는이를 최적화 할 수 있습니다. 그러나 당신도 그것을 비교하고 있습니까?
memcpys는 rvalue를 유효한 비 소유 상태에 놓을 가치가 있습니까, 아니면 rvalue를 비 소유 잔여 물로 취급 할 수 있습니까?
src
값 을 무효화해야한다고 생각 합니다. 그렇지 않으면 소멸자가 이름을 공개 할 것입니다. 또는 개체가 유효한지 여부에 대해 더 많은 상태를 저장 glDeleteBuffers()
하고 유효한 상태에있는 개체에 대해 조건부 호출을 수행 할 수 있습니다.
하지만 여기에도 버그가 있다고 생각합니다. 방금 값을 복사했습니다. 그러나 dst
사본 전에 측면 의 값을 해제하지 않았 으므로 거기에 저장된 이름이 손실되었습니다.
std :: array에는 자체 이동이 없습니다 (데이터가 동적으로 할당되지 않은 객체에 로컬이기 때문에). 컨테이너간에 기본 개체를 이동합니다.
기본 클래스의 이동 생성자를 올바르게 사용하고 있습니까?
예, 그것은 ligit처럼 보입니다.
제안.
나는 std::array
a 멤버를 오히려 상속 하도록 만들 것입니다.
나는 할것이다:
#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;
}
}