Broes De Cat
Broes De Cat

Reputation: 554

How to use policy-templates if the types of two templates are linked?

I'm currently writing a class which allows getting and setting interal program options and it should be quite flexible and easy to use. Specifically, an option is identified by an enum type and a value type, which have a one-on-one relationship. For example, an enum IntType will contains options which have an int type.

I had in mind the following code, but have no idea how to get it working or whether I'm trying to use templates in a way i shouldn't.

enum IntType {OPTION1, OPTION2}
enum StringType { OPTION3, OPTION4}

template<class T, class T2>
class Policy{
public:
    T2 getValue(const T& a);
    void setValue(const std::string& name, const T2& a);
    ...
}

class A: public Policy<IntType, int>, public Policy<Stringtype, std::string>, ...{
    ...
}

Each enum constant has one associated string representation, which is constant, but options are also taken as string input into the program, so I have to be able to deduce from a string which option I should change.

But obviously, this code cannot be used to directly call set or get values without qualifying its full template specialization. So

A* a = ...
a->setValue("intoption", 5);

will not work.

Any pointers on what I should use to get this working?

A partial answer on how to derive at compile time that OPTION1 maps to int and IntType, ... would also be great.

Thanks in advance, Broes

Upvotes: 1

Views: 135

Answers (2)

iammilind
iammilind

Reputation: 70000

Since you are filling the data at runtime, templates are not viable for this design. Runtime polymorphism with virtual function will be a good choice. For example,

class Options; // Say this is the class of interest

class DataType {
public:
  virtual Options& getOptions () = 0;
};

class IntType : public DataType {
public:
  Options& getOptions ();  // implement for 'int' type
};

class StringType : public DataType {
public:
  Options& getOptions ();    // implement for 'std::string' type
};

Now, class A should contain a pointer to DataType;

class A {
  DataType *pData;
public:
  void setValue (DataType *p) { pData = p; }
...
};

Usage:

A *a = ...;
a->setValue(new IntType); // take care of heap allocation / stack allocation

Upvotes: 0

Matthieu M.
Matthieu M.

Reputation: 299960

It is not necessary to pass both the Enum and the type. You can deduce the enum value from the type itself thanks to a traits class:

template <typename T>
struct PolicyTraits;

template <>
struct PolicyTraits<int> { static Enum const value = IntType; }

// ... and so on ...

Your selection is obviously a bit more difficult. For templates to work correctly you need selection based on compile constants, be they constants or types. This requires the names of your options to be constants.

A revised implementation would thus be:

template<class Name, class Type> 
class Policy{ 
public: 
    Type getValue(Name);
    void setValue(Name, Type const&);
    ...
}

This can be used as:

struct IntOption {};

class A: public Policy<IntOption, int> {};

int main() {
  A a;
  a.setValue(IntOption(), 3);
}

Also, you might be interested in looking up How Boost does it and perhaps use their library.

Upvotes: 2

Related Questions