I\'m building a class which I want to configure using various parameters which may one of: int
, double
and string
(or const char
The sensible thing to do would be to use Boost.Variant, which is developed by expert C++ programmers and honed and tested by hundreds of projects.
But if that is not an option for you, I see the following alternatives:
Reimplement Boost.Variant yourself. It will be a wonderful learning excercise, but it will take a lot of time to get right (and then more lots of time to fix all the bugs).
Resign on time efficiency, store all the types in a std::string
, and convert them in getters (originally suggested in a comment by @Galik).
Resign on memory efficiency and store all three types in your class.
If you have aversion against Boost specifically, use a different library which provides a variant type (like Qt's QVariant).
Here is a cut down version of something I regularly use to store program configuration properties, maybe you will find it useful:
#include <map>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <initializer_list>
class config
{
// everything is stored internally as std::strings
// usually read from a configuration file
typedef std::map<std::string, std::string> prop_map;
typedef prop_map::const_iterator prop_map_citer;
prop_map props;
public:
// example constructor. Normally I have a method to read the
// values in from a file
config(std::initializer_list<std::pair<std::string, std::string>> list)
{
for(const auto& p: list)
props[p.first] = p.second;
}
// values are converted as they are requested to whatever types
// they need to be. Also a default may be given in case the value
// was missing from the configuration file
template<typename T>
T get(const std::string& s, const T& dflt = T()) const
{
prop_map_citer found = props.find(s);
if(found == props.end())
return dflt;
T t;
std::istringstream(found->second) >> std::boolalpha >> t;
return t;
}
// std::strings need special handling (no conversion)
std::string get(const std::string& s, const std::string& dflt = "") const
{
prop_map_citer found = props.find(s);
return found != props.end() ? found->second : dflt;
}
};
int main()
{
const config cfg =
{
{"OutputFile", "/tmp/out.txt"}
, {"CaptureFPS", "25.0"}
, {"RetryDelaySeconds", "5"}
};
std::string s;
float f;
int i;
s = cfg.get("OutputFile");
f = cfg.get<float>("CaptureFPS");
i = cfg.get<int>("RetryDelaySeconds");
std::cout << "s: " << s << '\n';
std::cout << "f: " << f << '\n';
std::cout << "i: " << i << '\n';
}
If I have understood well, you want a std::map<A, B>
(or, even better, a std::unordered_map<>
since you want hashes) where A
is a string and B
can be a int
, double
or std::string
.
For B
you can use boost::any<>
. Otherwise, if you don't want to use boost, you can use a discriminated union
.
If you want to avoid the use of Boost, you can copy-paste the code of Boost.Variant into your project. It's header-only anyway. Or you can reinvent the wheel by implementing what's commonly called a "discriminated union."