Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is partial class template specialization the answer to this design problem?

Say you have a class who's job it is to connect to a remote server. I want to abstract this class to provide two versions, one that connects through UDP and the other through TCP. I want to build the leanest runtime code possible and instead of using polymorphism I am considering templates. Here is what I'm envisioning but I'm not sure it's the best way of doing this:

class udp {};
class tcp {};

template<class T,typename X>
class service
{
private:
  // Make this private so this non specialized version can't be used
   service();
};

template<typename X>
class service<udp, X>
{
private:
   udp _udp;
   X _x;
};

template<typename X>
class service<tcp, X>
{
private:
   tcp _tcp;
   X _x;
};

So the end benefit is that the genericness of T is still available, but the very different code required to setup a UDP or TCP connection has been specialized. I suppose you could put it both into one class, or provide another class that adheres to some pure virtual interface for setting up the network connection, like IConnectionManager.

But this does leave the problem of the code for the generic T now having to be written in and maintained in both specialized versions, where they are ultimately the same. How best to address this? I have a feeling I am going about this all wrong.

like image 835
ApplePieIsGood Avatar asked Dec 10 '08 14:12

ApplePieIsGood


People also ask

What is class template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

At which situation we need template specialization?

Is it possible to use different code only when sort() is called for char data type? It is possible in C++ to get a special behavior for a particular data type. This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming.


1 Answers

This can be best done using a policy for the transport protocol:

template<typename Transport>
class service : Transport {
public:
    typedef Transport transport_type;

    // common code
    void do_something() { 
        this->send(....);
    }
};

class tcp {
public:
    void send(....) {

    }
};

class udp {
public:
    void send(....) {

    }
};

typedef service<tcp> service_tcp;
typedef service<udp> service_udp;

Note that this is also polymorphic. It's called compile time polymorphism. Putting the policy into a base class will benefit from the Empty-Base-Class-Optimization. That is, your base class does not need to take any space. Putting the policy as a member has the other drawback that you always have to delegate stuff to that member, which can become annoying with time. The book Modern C++ Design describes this pattern in-depth.

Ideally, the transport protocol doesn't need to know anything about the protocol above it. But if for some reason you have to get some information about it, you can use the crtp pattern wiki:

template<template<typename Service> class Transport>
class service : Transport<service> {

    // since we derive privately, make the transport layer a friend of us, 
    // so that it can cast its this pointer down to us. 
    friend class Transport<service>;

public:
    typedef Transport<service> transport_type;

    // common code
    void do_something() { 
        this->send(....);
    }
};

template<typename Service>
class tcp {
public:
    void send(....) {

    }
};

template<typename Service>
class udp {
public:
    void send(....) {

    }
};

typedef service<tcp> service_tcp;
typedef service<udp> service_udp;

You don't have to put your templates into headers. If you explicitly instantiate them, you will gain faster compilation times, as much fewer code has to be included. Put this into service.cpp:

template class service<tcp>;
template class service<udp>;

Now, code that uses service does not need to know about the template code of service, since that code is already generated into the object file of service.cpp.

like image 130
Johannes Schaub - litb Avatar answered Sep 21 '22 05:09

Johannes Schaub - litb