从外部代码加载阶段

时间:2015-03-04 22:07:03

标签: c++ inheritance dll

我写了一个基于管道和过滤器的架构。为避免混淆,过滤器被称为"阶段"在我的代码中。这是基本的想法:

enter image description here

我希望其他开发人员可以实现自己的Stage类,然后我可以将它添加到运行时已存在的Stages列表中。

我已经阅读了一段时间,似乎他们对动态代码加载有限制。我目前的Stage类看起来像这样:

class Stage
{
public:
    void Process();
    const uint16_t InputCount();
    const uint16_t OutputCount();

    void SetOutputPipe(size_t idx, Pipe<AmplitudeVal> *outputPipe);
    void SetInputPipe(size_t idx, Pipe<AmplitudeVal> *inputPipe);

protected:
    Stage(const uint16_t inputCount, const uint16_t outputCount);
    virtual void init() {}; 
    virtual bool work() = 0;
    virtual void finish() {};

protected:
    const uint16_t mInputCount;
    const uint16_t mOutputCount;
    std::vector< Pipe<AmplitudeVal>* > mInputs;
    std::vector< Pipe<AmplitudeVal>* > mOutputs;
};

AmplitudeVal只是float的别名。此类仅保存对其连接的管道(mInputs和mOutputs)的引用,它不处理任何算法活动。为了方便外部开发人员使用,我想尽可能少地公开。现在这个类只依赖于Pipe标头和一些基本的配置文件。大多数处理加载DLL的例子都提出了一个只有纯虚函数而几乎没有任何成员变量的类。我不知道该怎么做。

1 个答案:

答案 0 :(得分:0)

我知道您希望在DLL中拥有阶段并让用户用户在您的DLL上派生他们的工作。

场景1:使用相同编译器和相同标准库构建消费者和DLL

如果消费者使用相同的编译器,兼容的编译选项,并且两者都使用相同的共享标准库(默认使用MSVC),那么解决方案应该按原样工作

(请参阅related SO question。)

场景2:使用相同的编译器但不同的库构建消费者和DLL

如果一方使用不同的标准库或其链接选项(例如,如果您使用静态链接库为您的DLL和共享库供消费者使用),那么您必须确保始终在所有对象上创建/发布同一方(因为DLL和应用程序都会使用自己的alloaction函数和不同的内存池)。

由于以下原因,这将非常困难:

  • 数据继承
  • 虚拟析构函数
  • 标准容器的存储管理(在DLL和消费者中会有所不同,尽管源代码可能会给出它相同的印象)

正确方向的第一步是将所有数据隔离到私有部分,并确保通过getter和setter进行干净访问。幸运的是,这种设计是继承的合理设计方法,因此即使您不需要它也值得使用它。

场景3:不同的编译器或不兼容的编译选项

如果您使用不同的编译器或不兼容的编译选项,那么真正的问题就会出现:

  • 你不能依赖于双方对内存布局有相同理解的假设。因此,成员的读/写可能发生在不同的位置;一团糟!这就是为什么这么多DLL类没有数据成员的原因。许多人还使用PIMPL idiom来隐藏私有内存布局。但是在这种继承情况下的PIMPL与使用私有数据非常相似(*this将成为私有实现的隐式指针)
  • 编译器/链接器使用“损坏的”函数名称。不同的编译器可能使用不同的修改,并且不会理解彼此的符号定义(即,客户端不会找到SetOutputPipe(),尽管它在那里)。这就是为什么大多数DLL将所有成员函数都设置为虚拟的原因:函数通过vtable中的偏移来调用,幸运的是,在实践中使用相同的布局。
  • 最后,不同的编译器可以使用不同的调用约定。但我认为在完善的平台上实践,这不应该是一个主要的风险

this answer中关于具有不同编译器(也没有继承)的DLL的问题,我提供了一些可能与这种混合场景相关的其他解释和参考。

再次使用private成员数据而不是受保护会让您更安全。使用extern "C"链接说明符公开getter / setter(无论是受保护的还是公共的)将避免非虚函数的名称重整问题。

最后评论:

在库中暴露类时,应特别注意设计类,使数据私有化。这种良好的做法值得额外考虑,你所处的场景。