Question

I have a class with a template function:

Foo.h:

class Foo {
public:
    int some_function();
    bool some_other_function(int a, const Bar& b) const;

    template<typename T>
    int some_template_function(const T& arg);
};

template<typename T>
int Foo::some_template_function(const T& arg){
   /*...generic implementation...*/
}

Now I've come to a point where I want to be able to access Foo via a proxy class, as in the Proxy design pattern.


Intuitively, I'd like to refactor as follows (the following code is incorrect, but it expresses my "idealized" API):

FooInterface.h:

class FooInterface {
public:
    virtual int some_function()=0;
    virtual bool some_other_function(int a, const Bar& b) const=0;

    template<typename T>
    virtual int some_template_function(const T& arg)=0;
};

FooImpl.h:

#include "FooInterface.h"

/** Implementation of the original Foo class **/
class FooImpl : public FooInterface {
public:
    int some_function();
    bool some_other_function(int a, const Bar& b) const;

    template<typename T>
    int some_template_function(const T& arg);
};

template<typename T>
int FooImpl::some_template_function(const T& arg){
   /*...generic implementation...*/
}

FooProxy.h:

#include "FooInterface.h"

class FooProxy : public FooInterface{
protected:
    FooInterface* m_ptrImpl; // initialized somewhere with a FooImpl*; unimportant in the context of this question
public:
    int some_function()
        { return m_ptrImpl->some_function(); }
    bool some_other_function(int a, const Bar& b) const
        { return m_ptrImpl->some_other_function(a,b); }

    template<typename T>
    int some_template_function(const T& arg)
        { return m_ptrImpl->some_template_function(arg); }
};

But this code fails miserably.

First and foremost, FooImpl can't compile, since class template functions can't be virtual.

What's more, even if I played around with the definition of some_template_function, even if I go as far as relocating it into a concrete class or some other jury-rigging, it's still going to wreak havoc with the whole point of having a proxy class in the first place, because template code needs to be defined in the header and included. That would force FooProxy.h to include FooImpl.h, and FooImpl.h needs all the implementation details and file-includes necessary to implement some_template_function. So if I'm using the Proxy pattern in order to obscure implementation details, distance myself from a concrete implementation, and avoid unnecessary file-includes, then I'm out of luck.

Is there a way to apply the Proxy pattern, or some variation thereof, to a class with template functions? Or is is this impossible in C++?


Context: At the moment, I'm trying to provide proxy access to a group of classes which have a preexisting, built-in logging mechanism. The only API I have for this log uses variadic templates, so it's impossible to predict the parameter combinations it'll be used with. I'd like the separation between the implementation and the client using the proxy to be as clean as possible, and I need to minimize dependencies from the client to the implementation, but I do need them to write to the same log.

However, I am interested in this issue beyond my immediate problem. It's puzzling to me that templates poke such a hole into a major design pattern, and that I haven't found this issue addressed anywhere.

Was it helpful?

Solution

A wrapper/proxy for a class which has a templated interface will always require that the definition of the template class be visible in a header file to the code that calls the wrapper. This is because the code generated for the templated interface depends on the types of the arguments it is called with.

If you're stuck with the existing templated implementation FooImpl, then as @mars writes in the comments, your only option is:

template <class Implementation>
class FooProxy
{
    Implementation * m_ptrImpl;
    //...
};

If you can change the existing implementation, the ideal solution would be to refactor the templated methods and split them into two layers; one layer that depends on the argument types, and a second layer that does not. The code in the existing methods that depends on the argument types should be identical in all implementations, so this layer can be moved into a method of the abstract interface class. The remaining code that does not depends on the argument types can be left in a non-templated method of the implementation class, meaning the implementation details can be hidden in the .cpp file.

Here's an example, based on the scenario of a log that supports writing arbitrary types:

LogInterface.h

class LogInterface {
public:
    template<typename T>
    void write(const T& arg)
    {
        // converts from 'T' to array of characters.
        // calls non-template 'write' as many times as necessary.
    }

    virtual void write(const char* p, std::size_t n)=0;
};

LogImpl.h

#include "LogInterface.h"

/** Implementation of the original Log class **/
class LogImpl : public LogInterface {
public:
    void write(const char* p, std::size_t n);
};

LogProxy.h

#include "LogInterface.h"

class LogProxy : public LogInterface{
protected:
    LogInterface* m_ptrImpl; // initialized somewhere with a LogImpl*
public:
    void write(const char* p, std::size_t n)
        { m_ptrImpl->write(p, n); }
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top