Avinash
Avinash

Reputation: 13257

template parameter at run time c++

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

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

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

Matthieu M.
Matthieu M.

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:

  • Let us suppose that all Property<T> classes inherit from PropertyBase
  • Let us suppose that you initialize them by parsing the type from a string

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

Related Questions