C++ 从外部代码加载阶段

C++ 从外部代码加载阶段,c++,inheritance,dll,C++,Inheritance,Dll,我写了一个基于管道和过滤器的架构。为了避免混淆,过滤器在我的代码中被称为stage。基本思路如下: 我希望其他开发人员能够实现他们自己的Stage类,然后我可以将其添加到运行时已经存在的Stage列表中 我已经阅读了一段时间了,它们似乎是对动态代码加载的限制。我现在的舞台课是这样的: class Stage { public: void Process(); const uint16_t InputCount(); const uint16_t OutputCount()

我写了一个基于管道和过滤器的架构。为了避免混淆,过滤器在我的代码中被称为stage。基本思路如下:

我希望其他开发人员能够实现他们自己的Stage类,然后我可以将其添加到运行时已经存在的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的别名。这个类只保存对它连接到输入和输出的管道的引用,它不处理任何算法活动。为了便于外部开发人员使用,我希望尽可能少地公开。现在这个类只依赖于管道头和一些基本配置文件。大多数处理加载DLL的示例都提出了一个只有纯虚拟函数且几乎没有任何成员变量的类。我不确定该怎么办。

我知道您希望在DLL中有stage,并让用户在您的DLL上派生他们的工作

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

如果使用者使用相同的编译器、兼容的编译选项,并且双方都使用MSVC的相同共享标准库默认值,则解决方案应按原样工作

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

如果一方使用不同的标准库或其链接选项,例如,如果您对DLL使用静态链接库,对使用者使用共享库,然后,您必须确保所有对象始终在同一侧创建/释放,因为DLL和应用程序将各自使用具有不同内存池的alloaction函数

这将非常困难,因为:

数据的继承 虚拟析构函数 标准容器的存储管理在DLL和使用者中是不同的,尽管源代码可能给人的印象是相同的 正确方向的第一步是将所有数据隔离到私有部分,并确保通过getter和setter进行干净的访问。幸运的是,这种设计是一种完善的继承设计方法,因此即使您不需要它,也值得使用它

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

如果使用不同的编译器或不兼容的编译选项,则真正的问题开始出现:

你不能相信双方对内存布局的理解是一致的。因此,成员的读/写可能发生在不同的位置;一团糟!这就是为什么这么多DLL类没有数据成员。许多还用于隐藏私有内存布局。但是这种继承情况下的PIMPL与使用私有数据非常相似,这将是指向私有实现的隐式指针 编译器/链接器使用损坏的函数名。不同的编译器可能会使用不同的损坏,并且无法理解彼此的符号定义,也就是说,尽管存在SetOutputPipe,客户端仍无法找到它。这就是为什么大多数DLL将所有成员函数都设置为虚拟函数的原因:这些函数是通过vtable中的偏移量调用的,幸运的是,vtable实际上使用了相同的布局accross编译器。 最后,不同的编译器可以使用不同的调用约定。但我认为,在成熟的平台上,这不应该是一个重大风险 关于使用不同编译器但又没有继承的DLL的问题,我提供了一些与这种混合场景相关的额外解释和参考

再次使用私有成员数据代替受保护的数据将使您处于更安全的一方。使用外部C链接说明符公开受保护或公共的getter/setter可以避免非虚拟函数的名称混乱问题

最后一句话:


在库中公开类时,应特别注意设计类,使数据私有化。无论您处于何种情况,这种良好的实践都值得额外考虑

若我理解的很好,你们的目标是提供一个DLL的阶段,用户可以导入它,并进一步派生它。这是你的名字吗?你的问题不太清楚。但我要指出,数据管道的使用越来越普及。与插件相比,这允许您以一种方式定义事物,即它们可以是进程内的,也可以是进程外的,甚至可以让片段以不同的语言在不同的网络位置运行。如果你发现你正在设计一个管道接口,并希望不同的作者将这些东西连接在一起,那么就你的目的而言,值得一看。但我不知道你的目的。@Christophe是的,的确。@Pat对不起,已经很晚了!我已经编辑并完成了这些信息。