views:

947

answers:

4

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.

+3  A: 

I would use the curious recuring template pattern, aka Five Point Palm Exploding Alexandrescu Technique:

template <typename Underlying>
class Transmit
{
public:
   void send(...)
   {
      _U.send(...)
   };

private:
    Underlying _U;
};

class Tcp
{
public:
   void send(...) {};
};

class Udp
{
public:
   void send(...) {};
};

There would probably be many more template parameters and sub classes but you get the idea, you can also use static methods.

By the way template code is generally more efficient but also much bigger.

Edouard A.
Interesting, that would relocate the actual protocol specific code to the underlying, and basically negate the need for specialization it would seem.
ApplePieIsGood
How is your solution related to the CRTP? The Curiously Recurring Template Pattern denotes the case where a type inherits a template class using itself as a template parameter: template<typename T> class Base {...}; class Derived : public Base<Derived> {...};
Luc Touraille
It's no CRTP in this example because it's very simple, but the full fledged version will probably be CRTP. We can use the term policy based design if you prefer.
Edouard A.
However I totally agree with the rest of your answer. The code specific to udp or tcp should be encapsulated in classes dedicated to transmission, so that the service is not coupled to a particular protocol.
Luc Touraille
Edouard A.
Edouard, what do you suggest to go further? Roughly speaking?
ApplePieIsGood
I would add more policies to have the generic processing in the classes policies and really have some basic classes for the transmission. For example, error handling could be a policy.
Edouard A.
I agree. If you want the leanest possible code and there's no need to be able to specify what protocol to use at run-time, policy based design is the way to go, +1 from me.
Andreas Magnusson
A: 

I think that the main point in choosing amongst polimorphism or template specialization, in this particular case at least, is if you want to choose which behavior to use at run time or at compile time.
If you want to have a udp or a tcp connection based, for example, on a connection string provided the user, then polimorphism best fits your needs; create a concrete class and then pass it to generic code that handles a pointer to a base interface.
Otherwise, you might consider using templates - I'm not sure if you need template specialization.

Hope this helps :)

Paolo Tedesco
In this case I want to chose at compile time, the system won't change at runtime, and I'm looking to create efficient code, though it was said that the size of the code is probably larger.Also agree that policy based design is the term I was looking for.
ApplePieIsGood
Could you clarify what you mean by "efficient" code? Code size? Execution time? Memory?It seems you're engaged in premature optimization here. Plain old virtual functions will do the job as far as I can see...
Arkadiy
Well code size is probably going to be bigger with the policy based design, I am not as concerned with code size in so far as its not related to any cache fitting issues.
ApplePieIsGood
I'm not totally sold that this is premature optimization. I know that the service is going to be used for super high frequency networking that requires ultra low latency. I know you can argue that the cost of virtual function calls can be seen as relatively small vs. other costs.
ApplePieIsGood
That said, if there is absolutely no need for changing protocol at run time, and the system needs to be designed with a pay for what you use philosophy, policy based design seems warranted.
ApplePieIsGood
+4  A: 

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:

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.

Johannes Schaub - litb
Thanks, what's the purpose of the typedef Transport transport_type; in the first example?
ApplePieIsGood
applepieisgood, it's always good to typedef template parameters, so you can access them from outside the scope of the class. for example, you have std::vector<T>::value_type, std::vector<T>::size_type too. you should also add a typedef Service service_type; to udp and tcp classes.
Johannes Schaub - litb
also, in the sample at hand, suppose you have a send function in the derived class too. calling this->send(....); would recurse infinitely. calling Transport<service>::send(....); is wordy. you could do transport_type::send(...); then
Johannes Schaub - litb
+2  A: 

Templates are not necessary (though a possible solution). This is just dependency injection via templates rather than via a constructor. Personally I would do it via a constructor. But doing it via template gives you the dubious benifit of a cheaper method call (it does not need to be virtual). But also does allow for easier compiler optimization.

Both the udp and tcp objects must still support the same interface.
If you do it via inheritance they must both implement a common interface (virtual base class), it it is done via templates this is not necessary but the compiler will check that they support the same method calls that the Service object requires.

As asked in the original question, I see no explicit need(or benefit) for partial template specialization (in the situation as described).

Template Method

class udp {/*Interface Plop*/static void plop(Message&);};
class tcp {/*Interface Plop*/static void plop(Message&);};
template<typename T>
class Service
{
    public:
        void doPlop(Message& m) { T::plop(m);}
    // Do not actually need to store an object if you make the methods static.
    // Alternatively:
    public:
        void doPlop(Message& m) { protocol.plop(m);}
    private:
        T protocol;
};

Polymorphic Version

class Plop{virtual void plop(Message&) = 0;} // Destruct or omitted for brevity
class upd:public Plop {/*Interface Plop*/void plop(Message&);};
class tcp:public Plop {/*Interface Plop*/void plop(Message&);};
class Service
{
    public:
        Service(Plop& p):protocol(p)  {};
        void doPlop(Message& m) { p->plop(m);}
    private:
        Plop& p;
};
Martin York
Thanks, that does make sense. I agree that specialization is totally unneeded in this case. About the benefits being dubious, I know this is hotly debated, whether eliminating vtable lookups has value over other optimizations, yet I see no benefit to paying for a language feature that I'm not using.
ApplePieIsGood
You are always going to pay for it (the computer gods deman it). The question is where you pay it. You can remove the need for the vtable and the cost of calling via virtual pointer, but you will pay the price somwhere else the only question is where you are paying :-) (not enough context here)
Martin York
The good thing with polymorphism is the price is being payed by the language and not you. With templates you may (as an example) pay with the cost of maintance. As a program you decide.
Martin York
In terms of computing resources, in this case the policy based design will pay less, if we can prove that the effects of the bloated code do not interfere with the performance of the code with regards to being able to fit in cache. There is not enough bloat in this case to worry, so this is cheaper.
ApplePieIsGood
Seriously, regardless of how fast you can invoke the method you're going to be waiting on your network. Even if you have a super ultra-fast network connection a vtable indirection will unnoticeable.
Max Lybbert
The example I gave might be leading some to think this is a simple send/receive method that gets called. There are a few high frequency call sites on this service that are used after data arrives from the network, and there's no reason to pay for a slower alternative when there is no benefit.
ApplePieIsGood