2010-04-20 61 views
0

所以我有一个我正在写的基于插件的系统。用户可以创建一个Plugin类的子类,然后它将在运行时加载并与系统的其他部分集成。当一个插件从系统运行时,它在一组插件的上下文中运行,我称其为Session。将变量实例静默地绑定到C++中的类?

我的问题是,在用户插件中,可以使用两个名为pf_ostream和pf_istream的流类来读/写数据到系统。我希望以某种方式将插件实例的会话变量绑定到pf_ostream和pf_istream,以便当用户实例化这些类时,它已经绑定到会话中(基本上我不希望他们看到会话内部)

我可能只是这样做有一个宏,包装调用构造函数,如:

#define MAKE_OSTREAM = pf_ostream_int(this->session) 

但我认为有可能是一个更好的办法。我在使用包装pf_ostream的Plugin内部的嵌套类时看过,但它看起来嵌套类不能以闭包方式访问封闭类变量。

有谁知道一个干净的方式来做到这一点?

回答

0

您可以在构建过程中为嵌套类提供对其父类的引用。

class plugin_base { 
protected: 
    plugin_base(): _istream(*this) {} 
    class istream { 
    public: 
     istream(plugin_base& p): _plugin(p) {} 
     ... 
    private: 
     plugin_base& _plugin; 
    }; 
    istream _istream; 
}; 

class plugin: public plugin_base { 
    ... 
}; 

plugin_base的构造函数将在每次实例化其子类时由编译器自动调用。

+0

你能举一个例子来说明这是怎么完成的吗? – 2010-04-20 16:05:46

+0

嗯,看着它,它看起来像你仍然需要显式传递一个指向封闭类的指针。我正在寻找通过内部类构造函数不可见的内容。 – 2010-04-20 16:13:39

+0

在我的系统中,外部类是实例化为插件的实际类的父类,因此用户必须明确地传递插件引用。 – 2010-04-20 16:20:19

0

可以使用具有Session的接口类。下面是一些耐人寻味:

class IContext 
{ 
public: 
    virtual ~IContext() {} 
    virtual Session* session() = 0; 
}; 


class Plugin : public IContext 
{ 
public: 
    Plugin(Session* session) : session_(session) {} 
    virtual ~Plugin() {} 
    virtual Session* session() {return session_;} 

protected: 
    Session* session_; 
}; 


class MyPlugin : public Plugin 
{ 
public: 
    MyPlugin(Session* session) : Plugin(session) {} 
    virtual ~MyPlugin() {} 
    void print() 
    { 
     pf_ostream pfos(this); 
     pfos << "Hello world!\n"; 
    } 
}; 


class pf_ostream 
{ 
public: 
    pf_ostream(IContext* context) : session_(context->session()) {} 
    // ... 

private: 
    Session* session_; 
    // ... 
}; 

注意插件作家现在如何使用pf_ostream pfos(this),而不是pf_ostream pfos(this->session),现在无视Sessionpf_ostream也不知道Plugin及其后代。

IContext可能不是这里最好的名字。也许IHasSession? (IHasCheezburger ???)

+0

我喜欢它,但用户仍然必须显式地将此指针传递给pf_ostream对象,如果可能的话,我希望尽可能避免这种情况。有可能没有一个好办法,我可能只需要用某种工厂模式来实例化流... – 2010-04-20 17:28:12

+0

我认为大多数有经验的C++程序员习惯于这种事情。任何完成GUI编程的人都习惯将某种上下文(窗口句柄,设备上下文)传递给API函数。我个人更喜欢传递一个'this'指针而不是调用一个魔法宏。 :-) – 2010-04-20 17:55:52

+0

是的,不幸的是,最有可能编写插件的人不是主要的软件工程师,所以我可以做任何事情来让事情“为他们工作”是一个优点。我可能只是将我的流包装在Plugin中的一些成员函数中用于实例化。 – 2010-04-20 18:04:26

1

下面是根据您在其中一条评论中提到的工厂构思提出的另一个答案。它采用Facade模式集中在绑定到一个Session一个System类的系统设备创建:

#include <boost/shared_ptr.hpp> 

class System 
{ 
public: 
    typedef boost::shared_ptr<pf_ostream> pf_ostream_ptr; 
    typedef boost::shared_ptr<pf_istream> pf_istream_ptr; 
    // Other system facilities... 

    System(Session* session) : session_(session) {} 

    pf_ostream_ptr makeOstream() 
     {return pf_ostream_ptr(new pf_ostream(session_));} 

    pf_istream_ptr makeIstream() 
     {return pf_istream_ptr(new pf_istream(session_));} 

private: 
    Session* session_; 
}; 


class Plugin 
{ 
public: 
    Plugin(System* system) : system_(system) {} 

    System& system() {return *system_;} 

private: 
    System* system_; 
}; 


class MyPlugin : public Plugin 
{ 
public: 
    MyPlugin(System* system) : Plugin(system) {} 

    void print() 
    { 
     pf_ostream_ptr pfos(system().makeOstream()); 
     *pfos << "Hello World!\n"; 
     // pfos will be deleted automatically at the end of this scope 
    } 
}; 

如果你有很多的系统设施,你应该明智地使用着的声明,以避免长时间的编译时间。此解决方案的缺点是将您的系统设施集中在一个依赖关系“焦点”(System类)中。如果System类在使用更多或更少系统设施的其他应用程序中重新使用,则可能需要更改该类。

编辑:

这里是你如何运用代理模式(以及PIMPL方法)来获取值语义的引用计数的流类:

#include <boost/shared_ptr.hpp> 

class pf_ostream 
{ 
public: 
    pf_ostream(Session* session); 
    pf_ostream& operator<<(int rhs); 
    // Use of default copy-constuctor and assignment operator 
    // will work fine because of shared_ptr. 

private: 
    struct Impl; 
    boost::shared_ptr<Impl> pimpl_; 
}; 

// In cpp file 

struct pf_ostream::Impl 
{ 
    Impl(Session* session) : session(session) {} 
    void insert(int rhs) {/*...*/} 
    Session* session; 
}; 

pf_ostream::pf_ostream(Session* session) : pimpl_(new Impl(session)) {} 

pf_ostream& pf_ostream::operator<<(int rhs) {pimpl_.insert(rhs); return *this;} 

用户将必须意识到代理对象的副本将引用相同的实际流。希望这可以帮助。

+0

我喜欢它,我可能会为我的流定义复制语义并按值返回,以便用户可以执行下列操作: pf_ostream outs = make_ostream(); 它会避免内存管理的东西。 – 2010-04-20 19:45:18

+0

流的问题是复制语义没有意义(我在考虑std :: iostreams)。例如,如果您有一个正在访问文件的文件流,则当流对象作为参数传递时,您不希望获得副本。但是,您可以使用代理模式。你最终会得到堆栈分配的流封装类,它将调用转发给真正的堆分配流。你可以在代理类中使用boost :: shared_ptr来进行内存管理。请参阅http://en.wikipedia.org/wiki/Proxy_pattern。 – 2010-04-20 20:04:29

+0

编辑答案显示我代表的意思的示例。 – 2010-04-20 20:22:49