У меня есть класс с функцией шаблона:
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...*/
}
Теперь я подошел к моменту, когда я хочу получить доступ к Foo через прокси-класс, как в Шаблон прокси-дизайна.
Интуитивно я хотел бы провести рефакторинг следующим образом (следующий код неверен, но он выражает мой «идеализированный» 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); }
};
Но этот код с треском проваливается.
Прежде всего, FooImpl
не может скомпилироваться, так как функции шаблона класса не могут быть виртуальными.
Более того, даже если я поигрался с определением some_template_function
, даже если я зайду так далеко, как переместу его в конкретный класс или какую-то другую махинацию присяжных, это все равно приведет к хаосу с точки зрения наличия прокси-класса в первую очередь, потому что код шаблона должен быть определен в заголовок и в комплекте. Это заставит FooProxy.h
включать FooImpl.h
, а также FooImpl.h
нужны все детали реализации и файл-включения, необходимые для реализации some_template_function
, Так что, если я использую шаблон Proxy для того, чтобы скрыть детали реализации, отдалиться от конкретной реализации и избежать ненужных включений файлов, то мне не повезло.
Есть ли способ применить шаблон Proxy или его разновидность к классу с шаблонными функциями? Или это невозможно в C ++?
Контекст: В данный момент я пытаюсь предоставить прокси-доступ к группе классов, в которой уже есть встроенный механизм ведения журнала. Единственный API, который у меня есть для этого журнала, использует переменные шаблоны, поэтому невозможно предсказать комбинации параметров, с которыми он будет использоваться. Я бы хотел, чтобы разделение между реализацией и клиентом, использующим прокси, было как можно более чистым, и мне нужно минимизировать зависимости от клиента до реализации, но мне нужно, чтобы они записывали в один и тот же журнал.
Тем не менее, я заинтересован в этом вопросе за пределами моей непосредственной проблемы. Меня удивляет, что шаблоны втыкают такую дыру в основной шаблон дизайна, и что я нигде не нашел, чтобы эта проблема решалась.
Оболочка / прокси для класса, который имеет шаблонный интерфейс, всегда будет требовать, чтобы определение класса шаблона было видно в файле заголовка для кода, который вызывает обертку. Это связано с тем, что код, сгенерированный для шаблонного интерфейса, зависит от типов аргументов, с которыми он вызывается.
Если вы застряли с существующей шаблонной реализацией FooImpl
тогда, как пишет @mars в комментариях, ваш единственный вариант:
template <class Implementation>
class FooProxy
{
Implementation * m_ptrImpl;
//...
};
Если вы можете изменить существующую реализацию, идеальным решением будет рефакторинг шаблонных методов и разделение их на два уровня; один слой, который зависит от типов аргументов, и второй слой, который не зависит. Код в существующих методах, который зависит от типов аргументов, должен быть одинаковым во всех реализациях, поэтому этот уровень можно переместить в метод абстрактного класса интерфейса. Оставшийся код, который не зависит от типов аргументов, можно оставить в не шаблонном методе класса реализации, что означает, что детали реализации могут быть скрыты в файле .cpp.
Вот пример, основанный на сценарии журнала, который поддерживает запись произвольных типов:
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); }
};
Других решений пока нет …