I'm having trouble with some features I want to implement in my config class, I have some values with diffrent types and I want to store them in an map. formerly i used ExtendedString class to store all data and convert them using a template function to the wanted type whenever I needed. and then had a map<ExtendedString,vector<ExtendedStri开发者_如何学JAVAng> >
in my config class to store all the keys in config file. here is what my ExtendedString class already look like:
class ExtendedString : public std::string
{
public:
ExtenedString() : public std::string
{
}
template <class T> ExtenededString(T)
{
std::stringstream s;
*this = s.str();
}
template <class T>T as(){
istringstream s(*this);
T temp;
d >> temp;
return temp;
}
}
now i want to parse all the values while loading my config file and also have the feature of converting to any possible type in case of need. for example if i parsed some value into an int and in my runtime i needed that value as string I want to be able to cast it right away, and besides i prefer not using boost in this case, it's one of my base classes and I don't want to link boost to every project i'm developing.
One classic solution is boost::any
, or a massive union
. But I think both of those approaches cause more trouble than they solve. What's wrong with creating, say, a Config
class that has member for each configuration option?
I'd recommend using boost::any
, as it sounds like it does what you are looking for. However, if you really don't want to use it, you might be able to handle it something like this. It may not be the best way to do it (its the first thing that came into my head) - basically its storing the value in the type you had it as originally, and as a string. If you try and get
it as the original type, it returns that value, otherwise, it will use a std::stringstream
to try and convert it.
Note: This doesn't handle copying/assignment, change d_data
to your shared pointer of choice to handle that.
#include <string>
#include <sstream>
#include <iostream>
class ConfigValue
{
class HolderBase
{
protected:
virtual void writeValueToStream(std::ostream& os) const = 0;
public:
virtual ~HolderBase() { }
template <class Type>
Type getAs() const
{
std::stringstream ss;
writeValueToStream(ss);
Type temp;
ss >> temp;
return temp;
}
};
template <class Type>
class Holder : public HolderBase
{
Type d_value;
protected:
void writeValueToStream(std::ostream& os) const
{
os << d_value;
}
public:
Holder(const Type& value)
: d_value(value)
{
std::ostringstream oss;
oss << value;
}
Type get() const
{
return d_value;
}
};
HolderBase *d_data;
public:
template <class Type>
ConfigValue(const Type& value)
: d_data(new Holder<Type>(value))
{
}
~ConfigValue()
{
delete d_data;
}
template <class Type>
Type get() const
{
if (Holder<Type> *holder = dynamic_cast<Holder<Type>*>(d_data))
{
return holder->get();
}
else
{
return d_data->getAs<Type>();
}
}
};
int main()
{
ConfigValue cv = 10;
std::cout << cv.get<std::string>() << std::endl;
return 0;
}
Instead of storing them all as strings, you could write a "Value" class and then write subclasses for each kind of value.
精彩评论