คลาส Wrapper C ++ สำหรับอาร์เรย์ของวัตถุบัฟเฟอร์ OpenGL - เป็นเพียงตัวสร้าง
มีการถามคำถามคล้าย ๆ กันที่นี่และฉันกำลังพยายามทำสิ่งเดียวกัน แต่
- ฉันกำลังลองใช้วิธีการอื่นโดยหาจาก
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
มูลค่ามันจะนำ rvalues ในรัฐที่ไม่ได้เป็นเจ้าของที่ถูกต้องหรือผมสามารถรักษา rvalues เป็นของเหลือที่ไม่ได้เป็นเจ้าของ? - ฉันใช้ตัวสร้างการย้ายของคลาสพื้นฐานอย่างถูกต้องหรือไม่
คำตอบ
คำถามเฉพาะบางอย่างที่ฉันมีเกี่ยวกับรหัส / แนวทางนี้คือถ้ามี
ตกลง.
ข้อผิดพลาดในการจัดการกับการตัดสินใจในการออกแบบ: ผู้สร้างควรโยนทิ้งหรือฉันควรปล่อยให้ผู้โทรตรวจสอบข้อผิดพลาดหากต้องการ
วัตถุนั้นเริ่มต้นอย่างถูกต้องและพร้อมใช้งานหรือควรโยนทิ้ง การเริ่มต้นสองขั้นตอน (สร้างแล้วตรวจสอบ) เป็นความคิดที่ไม่ดีเนื่องจากปล่อยให้ผู้ใช้ทำสิ่งที่ถูกต้อง คุณควรตรวจสอบให้แน่ใจว่าวัตถุของคุณไม่สามารถถูกทำร้ายได้ไม่ต้องพึ่งพาผู้ใช้ที่จะไม่ละเมิดคุณ
การตัดสินใจในการออกแบบ: ควรใช้เทมเพลตที่นี่หรือไม่
หากคุณจะใช้std::array
คุณไม่มีทางเลือกจริงๆ
สิ่งนี้จะนำไปสู่การขยายโค้ดที่มีปัญหาหรือไม่หากฉันใช้ buffer_objects ขนาดต่างๆจำนวนมากหรือมีประสิทธิภาพหรือไม่ ฉันสามารถประเมินสิ่งนี้ผ่านการทำโปรไฟล์ได้หรือไม่
สิ่งนี้อาจนำไปสู่การรวบรวมวิธีการสำหรับประเภทต่างๆ แต่นี่ขึ้นอยู่กับคอมไพเลอร์และคอมไพเลอร์สมัยใหม่บางตัวสามารถปรับให้เหมาะสมได้ แต่คุณเปรียบเทียบมันด้วยอะไร?
memcpys คุ้มค่าหรือไม่ที่จะทำให้ rvalues อยู่ในสถานะที่ไม่เป็นเจ้าของที่ถูกต้องหรือฉันสามารถถือว่า rvalues เป็นของเหลือที่ไม่ได้เป็นเจ้าของได้หรือไม่
ฉันคิดว่าคุณต้องลบค่าออกsrc
ไป มิฉะนั้นผู้ทำลายจะปล่อยชื่อ หรือคุณสามารถจัดเก็บสถานะเพิ่มเติมเกี่ยวกับว่าอ็อบเจ็กต์ถูกต้องหรือไม่และทำการเรียกglDeleteBuffers()
เงื่อนไขบนอ็อบเจ็กต์ที่อยู่ในสถานะที่ถูกต้อง
แต่ฉันก็คิดว่ามีจุดบกพร่องที่นี่ คุณเพิ่งคัดลอกค่าต่างๆ แต่คุณไม่ได้ปล่อยค่าที่dst
ด้านข้างก่อนสำเนาดังนั้นคุณจึงสูญเสียชื่อที่เก็บไว้ที่นั่น
โปรดจำไว้ว่าอาร์เรย์ std :: ไม่มีการย้ายของตัวเอง (เนื่องจากข้อมูลอยู่ในพื้นที่ของวัตถุที่ไม่ได้รับการจัดสรรแบบไดนามิก) มันย้ายวัตถุที่อยู่ข้างใต้ระหว่างคอนเทนเนอร์
ฉันใช้ตัวสร้างการย้ายของคลาสพื้นฐานอย่างถูกต้องหรือไม่
ใช่ดูเหมือนว่า ligit
ข้อเสนอแนะ.
ฉันจะให้std::array
สมาชิกคนหนึ่งได้รับมรดกจากมัน
ฉันจะทำ:
#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;
}
}