Reputation: 13257
I wrote a C++ template class, but I do not have the type to instantiate the class. Types are stored in a string format. So I have to do something like this:
if ( propType == "char") {
Property<char> pChar = ...
} else if ( propType == "int") {
Property<int> pChar = ...
} if ( propType == "double") {
Property<double> pChar = ...
}
I am not liking this if-else-
loop, is there any way to avoid this, or any other solution to such problem?
Upvotes: 0
Views: 149
Reputation: 275330
There are a couple of considerations here.
Suppose you want to parse the file from disk, then based off of the type on disk you want to create an object. You handle these objects in a mostly indistinguishable way. There are a finite list of types you are working with.
The answer I'd use for this would be a boost::variant
to store the data, and a map
(like Matthiew's answer) that maps the name of the type to a reader (or parser) for the type. The reader then returns a boost::variant<int, double, char, string, etc>
.
Code then interacts with the variant in a pseudo uniform way. Helper functions use boost functions to call functors to interact with the variant
.
Ie, something like this:
typedef boost::variant<int, double> myVariant;
typedef std::function< myVariant( input_stream_type& ) > ValueParser;
ValueParser GetParser( std::string typename );
// ...
struct DoTypeSpecificWork
{
typedef void result_type;
void operator()( int ) { /* ... int code goes here */ }
void operator()( double ) { /* ... double code goes here */ }
};
ValueParser parser = GetParser( propType );
myVariant var = parser( input_stream );
boost::variant::apply_visitor( DoTypeSpecificWork(), var );
Another option is to have a base PropertyBase
class that has abstract interfaces that are type agnostic. Then Property<T>
child classes that implement those abstract interfaces for each type. Creating those Property<T>
child classes could be done directly (forcing the parser to know about your Property
class), or indirectly (ie, you take a variant
and produce an appropriate Property<T>
, which decouples to parsing code from your type abstraction).
Basically, you need to decide between type erasure, type abstraction and template based programming to deal with multiple types. They all have their own advantages.
Upvotes: 1
Reputation: 299770
Many ways, but it's impossible to choose a good one without knowing how you use it in your particular case.
As a demonstration:
Property<T>
classes inherit from PropertyBase
Here is some code then:
using PropertyPtr = std::unique_ptr<PropertyBase>;
using Parser = std::function<PropertyPtr(std::string const&, std::string const&)>;
template <typename T>
PropertyPtr parse(std::string const& type, std::string const& value) {
T v = boost::lexical_cast<T>(value);
return PropertyPtr(new Property<T>(std::move(v)));
}
std::map<std::string, Parser> const parsers = {
std::make_pair("char", parse<char>),
std::make_pair("int", parse<int>),
std::make_pair("double", parse<double>)
};
void dummy(std::string const& type, std::string const& value) {
auto const it = parsers.find(type);
assert(it == parsers.end() && "No parser");
auto const& parser = it->second;
PropertyPtr property = parser(type, value);
// do something with property
}
Hope this helps.
Upvotes: 4