【问题标题】:Can the Proxy pattern accommodate template functions?代理模式可以容纳模板函数吗?
【发布时间】:2013-08-04 21:21:58
【问题描述】:

我有一个带有模板函数的类:

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 的地步,就像在 Proxy design pattern 中一样。


直觉上,我想重构如下(以下代码不正确,但它表达了我的“理想化”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 所需的所有实现细节和文件包含。因此,如果我使用代理模式来掩盖实现细节,使自己远离具体实现,并避免不必要的文件包含,那么我就不走运了。

有没有办法将代理模式或其一些变体应用于具有模板函数的类?或者这在 C++ 中是不可能的吗?


上下文: 目前,我正在尝试为一组具有预先存在的内置日志记录机制的类提供代理访问。我为该日志提供的唯一 API 使用可变参数模板,因此无法预测将使用的参数组合。我希望实现和使用代理的客户端之间的分离尽可能干净,并且我需要最大限度地减少从客户端到实现的依赖关系,但我确实需要它们写入同一个日志。

但是,我对这个问题的兴趣超出了我当前的问题。让我感到困惑的是,模板在主要设计模式中戳出这样一个漏洞,而且我还没有发现任何地方都解决了这个问题。

【问题讨论】:

  • 是的,如果你在编译时就知道你想要什么,那么有一种方法可以做到这一点。这闻起来有点像xy problem,所以你能详细说明你想做什么而不是怎么做吗?如果您无法预测可能需要实例化模板的所有类型,则无法隐藏模板实现,否则您可以显式实例化所需类型的模板。
  • @mars:够公平的;我试图将问题简化为最小形式。我会在某些情况下进行编辑。
  • 对不起,但我没有想出一个解决方案,说明如何使用模板方法编写代理,该模板方法也不会将原始模板代码放在头文件中(预编译的头文件有帮助吗?)。除了您的问题之外,只要模板方法可用或显式实例化,编写代理 template &lt;class Implementation&gt; class proxy {Implementation * m_ptrImpl;...}; 就可以按照您期望的方式工作。 How to achieve virtual template function in c++ 可能很有趣,暗示了访问者模式。
  • 正如@mars 指出的那样,模板要求实现在标头中可见这一事实并不妨碍您实现代理模式。问题是您有一个额外的要求,即使用 pimpl 习惯用法隐藏实现。这可以做到,但需要不同的方法:见stackoverflow.com/questions/17038434/…

标签: c++ templates design-patterns proxy-classes


【解决方案1】:

具有模板化接口的类的包装器/代理将始终要求模板类的定义在调用包装器的代码的头文件中可见。这是因为为模板化接口生成的代码取决于调用它的参数的类型。

如果您坚持使用现有的模板化实现 FooImpl,那么正如 @mars 在 cmets 中所写,您唯一的选择是:

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); }
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    • 2017-07-01
    • 2015-11-25
    • 1970-01-01
    • 2013-12-08
    • 1970-01-01
    • 2011-02-11
    相关资源
    最近更新 更多