HOW TO: Building flexible structure definitions
Hi, Right now I'm trying to develop a tricky structure that MAY contain any of the following information: - Vertex coordinates (x,y,z) - Color components (r, g, b) - Texture coordinates (u, v) Basically, I'd end up having a structure that looks like this (with all information included): struct Vertex { float x,y,z; // vertex coordinates unsigned char r, g, b; // color components double u,v; // texture coordinates }; The tricky part is that the user isn't required to create a structure with all of this information. For example, the structure above could be missing texture coordinates, color coordinates, or both (however the vertex coordinates are required since I doubt we care about empty structs)! This is for OpenGL vertex buffers, so in addition to this information we need to be able to specify some information in this structure that tells external classes a little bit more about itself. Note that this information CANNOT modify the size of the Vertex structure and must be compile-time only. For example: struct Vertex { // Information (does not change size). Note that if static const unsigned int vertex_type = GL_FLOAT; // Tells openGL the type of the x,y,z members static const unsigned int color_type = GL_UNSIGNED_CHAR; // Tells openGL the type of the r,g,b members static const unsigned int texture_type = GL_DOUBLE; // Tells openGl the type of the u,v members float x,y,z; // vertex coordinates unsigned char r, g, b; // color components double u,v; // texture coordinates }; But wait! There's more I need! This is the part that I was hoping boost could help me with. I have a class that takes objects of type Vertex (well, at least in this example it does. It takes a template argument. See below). The class needs a way (at runtime) to check of the Vertex object passed in has either color, texture, or vertex data in it. Example: template< typename t_vertex > class VertexBuffer { public: VertexBuffer( const t_vertex& vert ) { // How do I check what information is in vert? } } struct Vertex1 { float x,y,z; // vertex coordinates unsigned char r, g, b; // color components double u,v; // texture coordinates }; // This vertex type has NO color! // How would VertexBuffer tell?!? struct Vertex2 { float x,y,z; // vertex coordinates double u,v; // texture coordinates }; VertexBuffer<Vertex1> vbuffer1; VertexBuffer<Vertex2> vbuffer2; Is there something in boost I can utilize to make the definition of Vertex more flexible and open-ended? Perhaps I can use boost in the VertexBuffer class as well to determine the construct of the template type being passed in? Any and ALL help is appreciated.
Robert Dailey wrote, On 16.11.2007 20:58:
Hi,
Right now I'm trying to develop a tricky structure that MAY contain any of the following information:
- Vertex coordinates (x,y,z) - Color components (r, g, b) - Texture coordinates (u, v)
Basically, I'd end up having a structure that looks like this (with all information included):
struct Vertex { float x,y,z; // vertex coordinates unsigned char r, g, b; // color components double u,v; // texture coordinates }; [...snipped...] Boost.Optional comes to my mind. But can you live with one extra bool for each of the optional structures? Or Boost.Variant but you still end up with bigger structures than is strictly necessary.
If you care about the memory and in your specific case you could exploit that the structures share common initial sequence (9.2/16). But then you will have to sprinkle your source with lots of reinterpret_casts or use lots of macros. Neither is perfect, it is up to you to weight the possibilities. -- wilx #include <iostream> #include <boost/variant.hpp> #include <boost/optional.hpp> struct Bools { bool a : 1; bool b : 1; bool c : 1; }; struct Vertex1 { float x,y,z; // vertex coordinates unsigned char r, g, b; // color components double u,v; // texture coordinates }; struct Vertex2 { float x,y,z; // vertex coordinates bool has_rgb; unsigned char r, g, b; // color components bool has_texture; double u,v; // texture coordinates }; struct Vertex3 { float x,y,z; // vertex coordinates unsigned char r, g, b; // color components bool has_rgb : 1; bool has_texture : 1; double u,v; // texture coordinates }; struct BaseVertex { float x,y,z; bool has_rgb : 1; bool has_texture : 1; }; struct RGBVertex : public BaseVertex { unsigned char r, g, b; // color components }; struct TxtVertex : public BaseVertex { double u, v; // texture coordinates }; struct RGBTxtVertex : public BaseVertex { unsigned char r, g, b; // color components double u, v; // texture coordinates }; union UniversalVertex { BaseVertex base; RGBVertex rgb; TxtVertex txt; RGBTxtVertex rgb_txt; }; namespace C { struct BaseVertex { float x,y,z; bool has_rgb : 1; bool has_texture : 1; }; struct RGBVertex { float x,y,z; bool has_rgb : 1; bool has_texture : 1; unsigned char r, g, b; // color components }; struct TxtVertex { float x,y,z; bool has_rgb : 1; bool has_texture : 1; double u, v; // texture coordinates }; struct RGBTxtVertex { float x,y,z; bool has_rgb : 1; bool has_texture : 1; unsigned char r, g, b; // color components double u, v; // texture coordinates }; union UniversalVertex { BaseVertex base; RGBVertex rgb; TxtVertex txt; RGBTxtVertex rgb_txt; UniversalVertex (UniversalVertex const & other) { switch (base.has_rgb | (base.has_texture)) { case 0: base = other.base; break; case 1: rgb = other.rgb; break; case 2: txt = other.txt; break; case 3: rgb_txt = other.rgb_txt; } } }; } namespace B { typedef boost::variant<C::BaseVertex, C::RGBVertex, C::TxtVertex, C::RGBTxtVertex> UniversalVertex; } namespace O { struct RGB { unsigned char r, g, b; }; struct Txt { double u, v; }; struct UniversalVertex { float x, y, z; boost::optional<RGB> rgb; boost::optional<Txt> txt; }; } #define _STRINGIFY(x) #x #define STRINGIFY(x) _STRINGIFY(x) #define PRINT_SIZEOF(x) {std::cout << "sizeof (" STRINGIFY(x) "): " << sizeof (x) << std::endl;} int main () { PRINT_SIZEOF (Bools); PRINT_SIZEOF (Vertex1); PRINT_SIZEOF (Vertex2); PRINT_SIZEOF (Vertex3); PRINT_SIZEOF (BaseVertex); PRINT_SIZEOF (RGBVertex); PRINT_SIZEOF (TxtVertex); PRINT_SIZEOF (RGBTxtVertex); PRINT_SIZEOF (UniversalVertex); PRINT_SIZEOF (C::BaseVertex); PRINT_SIZEOF (C::RGBVertex); PRINT_SIZEOF (C::TxtVertex); PRINT_SIZEOF (C::RGBTxtVertex); PRINT_SIZEOF (C::UniversalVertex); PRINT_SIZEOF (B::UniversalVertex); PRINT_SIZEOF (O::UniversalVertex); PRINT_SIZEOF (UniversalVertex[2]); PRINT_SIZEOF (C::UniversalVertex[2]); PRINT_SIZEOF (B::UniversalVertex[2]); PRINT_SIZEOF (O::UniversalVertex[2]); }
participants (2)
-
Robert Dailey
-
Václav Haisman