我写了一个基于管道和过滤器的架构。为避免混淆,过滤器被称为"阶段"在我的代码中。这是基本的想法:
我希望其他开发人员可以实现自己的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的例子都提出了一个只有纯虚函数而几乎没有任何成员变量的类。我不知道该怎么做。
答案 0 :(得分:0)
我知道您希望在DLL中拥有阶段并让用户用户在您的DLL上派生他们的工作。
场景1:使用相同编译器和相同标准库构建消费者和DLL
如果消费者使用相同的编译器,兼容的编译选项,并且两者都使用相同的共享标准库(默认使用MSVC),那么解决方案应该按原样工作
(请参阅related SO question。)
场景2:使用相同的编译器但不同的库构建消费者和DLL
如果一方使用不同的标准库或其链接选项(例如,如果您使用静态链接库为您的DLL和共享库供消费者使用),那么您必须确保始终在所有对象上创建/发布同一方(因为DLL和应用程序都会使用自己的alloaction函数和不同的内存池)。
由于以下原因,这将非常困难:
正确方向的第一步是将所有数据隔离到私有部分,并确保通过getter和setter进行干净访问。幸运的是,这种设计是继承的合理设计方法,因此即使您不需要它也值得使用它。
场景3:不同的编译器或不兼容的编译选项
如果您使用不同的编译器或不兼容的编译选项,那么真正的问题就会出现:
*this
将成为私有实现的隐式指针)SetOutputPipe()
,尽管它在那里)。这就是为什么大多数DLL将所有成员函数都设置为虚拟的原因:函数通过vtable中的偏移来调用,幸运的是,在实践中使用相同的布局。 在this answer中关于具有不同编译器(也没有继承)的DLL的问题,我提供了一些可能与这种混合场景相关的其他解释和参考。
再次使用private
成员数据而不是受保护会让您更安全。使用extern "C"
链接说明符公开getter / setter(无论是受保护的还是公共的)将避免非虚函数的名称重整问题。
最后评论:
在库中暴露类时,应特别注意设计类,使数据私有化。这种良好的做法值得额外考虑,你所处的场景。