What I would like to do (in C++) is create a ‘Parameter’ data type which has a value, min, and max. I would then like to create a container for these types.
E.g. I have the following code:
template <typename T>
class ParamT {
public:
ParamT() {
}
ParamT(T _value):value(_value) {
}
ParamT(T _value, T _vmin, T _vmax):value(_value), vmin(_vmin), vmax(_vmax) {
}
void setup(T vmin, T vmax) {
this->vmin = vmin;
this->vmax = vmax;
}
void setup(T value, T vmin, T vmax) {
setup(vmin, vmax);
setValue(value);
}
T operator=(const T & value) {
setValue(value);
}
void setValue(T v) {
value = v;
}
T getValue() {
return value;
}
operator T() {
return getValue();
}
protected:
T value;
T vmin;
T vmax;
};
typedef ParamT<int> Int;
typedef ParamT<float> Float;
typedef ParamT<bool> Bool;
In an ideal world my Api would be something like:
std::map<string, Param> params;
params["speed"] = PFloat(3.0f, 2.1f, 5.0f);
params["id"] = PInt(0, 1, 5);
or
params["speed"].setup(3.0f, 2.1f, 5.0f);
params["id"].setup(0, 1, 5);
and writing to them:
params["speed"] = 4.2f;
params["id"] = 1;
or
params["speed"].setValue(4.2f);
params["id].setValue(1);
and reading:
float speed = params["speed"];
int id = params["id"];
or
float speed = params["speed"].getValue();
int id = params["id"].getValue();
Of course in the code above, ParamT has no base class so I cannot create a map. But even if I create a base class for it which ParamT extends, I obviously cannot have different getValues() which return different types. I thought about many solutions, including setValueI(int i), setValuef(float f), int getValueI(), float getValueF(), or a map for ints, a map for floats etc. But all seem very unclean. Is it possible in C++ to implement the above API?
At the moment I am only concerned with simple types like int, float, bool etc. But I would like to extend this to vectors (my own) and potentially more.
It’s a tough concept to implement in C++, as you’re seeing. I’m always a proponent of using the Boost library, which has already solved it for you. You can typedef the complex boost variant template class to something more usable in your specific domain, so
Now, to add another type to your variant (eg, long, std::string, whatever) you can just modify the typedef of ParamT; The catch, here, is that the burden of checking the types is on you – it’ll throw an exception if you store a float and try to receive an int, but there’s no compile-time safety.
If you want to get really crazy, you can implement an overloaded cast operator on a proxy object….
You’d return this from a non-templated value() function in Param, instead of the variant itself. Now you can assign a value without the template call..
Though fair warning, you’re stepping into meta-programming hell here. Here thar be dragons. And as always, this is not a complete solution, just a pointer. YMMV.
Heres a roughly working version showing how to use it, and the failures that are easy to hit.