Reputation: 411
The basic idea is to get a unordered_map that stores values of different types. What I am trying to do is to create an easy accessible object to a OpenGL Uniform Buffer Object. The end product would look something like:
UBO ubo = { "Uniforms", "translation", "scale", "rotation", "enabled" };
ubo["scale"] = 0.5f;
ubo["translation"] = { 0.1f, 0.1f, 0.0f };
ubo["rotation"] = { 90.0f, 0.0f, 0.0f, 1.0f };
ubo["enabled"] = GL_TRUE;
In my UBO class I have overloaded operator[]:
struct UBOData;
class UBO
{
std::unordered_map<std::string,UBOData>
...
public:
UBOData &operator[](std::string key)
{
UBOData data = new UBOData();
dataMap.emplace(key, data);
return data;
}
const UBOData& operator[](std::string key)
{
return const_cast<UBOData&>(*this)[key];
}
};
And I am using UBOData to store different data types. This is where my confidence wanes in the light of what is "right" in the c++ world.
.
.
.
struct UBOData
{
enum ReturnType {Undefined, rInt, rFloat, rDouble};
void *value;
ReturnType type;
int &operator=(int lhs);
float &operator=(float lhs);
double &operator=(double lhs);
};
I have truncated the types for this example, no std::array types. Also notice I am using a void * to store the value and tell me I need to rethink my design. Of course I do that's why I am here :)
int &UBOData::operator=(int lhs)
{
if (type == Undefined) { type = rInt; } else { assert(type == rInt); }
value = new int(lhs);
int &rValue = *((int*)value);
return rValue;
}
float &UBOData::operator=(float lhs)
{
if (type == Undefined) { type = rFloat; }
else { assert(type == rFloat); }
value = new float(lhs);
float &rValue = *((float*)value);
return rValue;
}
double &UBOData::operator=(double lhs)
{
if (type == Undefined) { type = rDouble; }
else { assert(type == rInt); }
value = new double(lhs);
double &rValue = *((double*)value);
return rValue;
}
I've attempted to wrap the void* with type checking but is there a better way to get a multi-type map without void *?
Note: I am using VS2013 on Windows and clang on Mac and Linux.
Upvotes: 0
Views: 953
Reputation: 52365
Definitely boost::variant. That is what its built for. Here is a small example using your code:
#include <unordered_map>
#include <vector>
#include <boost/variant.hpp>
class UBO
{
using UBOData = boost::variant<float, std::vector<float>>;
std::unordered_map<std::string, UBOData> dataMap;
public:
UBO() : dataMap(){}
UBOData &operator[](const std::string& key)
{
return dataMap[key];
}
};
int main()
{
UBO ubo;
ubo["scale"] = 0.5f;
ubo["translation"] = std::vector<float>{ 0.1f, 0.1f, 0.0f };
ubo["rotation"] = std::vector<float>{ 90.0f, 0.0f, 0.0f, 1.0f };
}
If you want the { 0.1f, 0.1f, 0.0f }
syntax without typing std::vector<float>
, etc, you probably would need some type of proxy that handles initializer lists.
Upvotes: 0
Reputation: 275385
boost::variant
or boost::any
.
If you cannot or will not use boost, read what they did.
I would go with variant
myself.
Upvotes: 2