Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to select a code path for different type in a member template function

Tags:

c++

templates

I have a class which returns the specific device based on runtime.

struct ComponentDc;
struct ComponentIc;

typedef Device<ComponentDc> DevComponentDc;
typedef Device<ComponentIc> DevComponentIc;

template<class Component>
class Device{
 Device<Component>* getDevice() { return this; }
 void exec() { }
};

In exec(), I'd like to print "Hello" if the component type is ComponentDc and world if it is ComponentIc. Also, these are the only two types with which Device can be created.

How do I do this ?

like image 273
brainydexter Avatar asked Mar 19 '13 08:03

brainydexter


3 Answers

You have two classic possibilities.

First, use two global function overloads, one for ComponentDc, one for ComponentIc:

void globalExec(ComponentDc) { std::cout << "Hello"; }
void globalExec(ComponentIc) { std::cout << "World"; }

void Device<Component>::exec() { globalExec(Component); }

Second, use traits-class: pure template class with no fields and with different typedefs and only static functions as methods. This class has own specializations for different possible argument types.

template<Component> class DeviceTraits {};

template<> class DeviceTraits<ComponentDc> { 
    static std::string getMessage() { return "Hello"; }
};

template<> class DeviceTraits<ComponentIc> { 
    static std::string getMessage() { return "World"; }
};

void Device<Component>::exec() { 
    std::cout << DeviceTraits<Component>::getMessage(); 
}

The advantage of using traits classes is that you don't have to spoil your global namespace with several functions.

About partially specializing the Device class itself - it is not always possible, and it is considered more convenient to move any template-argument-specific code into traits class.

This is the classic approach used in STL. Alternatively, you can use boost::enable_if or std::enable_if (for the latest compilers).

like image 121
Mikhail Avatar answered Sep 28 '22 10:09

Mikhail


You could instantiate template explicitly:

template<> class Device<ComponentDc> {
    ...
    void exec() { cout << "Hello"; }
};

The same goes for Device<ComponentIc>.

Also, if you want to restrict template parameters to a specific set, you should think of inheritance or composition instead of templates.

like image 24
umi Avatar answered Sep 28 '22 12:09

umi


You can also use boost::enable_if

http://www.boost.org/doc/libs/1_53_0/libs/utility/enable_if.html http://www.boost.org/doc/libs/1_44_0/libs/type_traits/doc/html/boost_typetraits/reference/is_same.html

void Device<Component>::exec(boost::enable_if< boost::is_same<Component,ComponentDc> >* enabler = 0)
{
}

void Device<Component>::exec(boost::enable_if< boost::is_same<Component,ComponentIc> >* enabler = 0)
{
}
like image 42
sylvain.joyeux Avatar answered Sep 28 '22 10:09

sylvain.joyeux