1

I wrote a Pipe and Filter based architecture. To avoid confusion the Filter's are called "Stages" in my code. Here's the basic idea :

enter image description here

I want other developers to have the possibility to implement their own Stage class and then I can add it into the list of Stages that already exist at run-time.

I've been reading around for a while and it seems like their are restrictions to dynamic code loading. My current Stage class looks like this :

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 is simply an alias for float. This class only holds references to pipes it is connected to(mInputs and mOutputs), it doesn't deal with any algorithmic activity. I want to expose as little as I can for ease of use to external developers. Right now this class only relies on the Pipe header and some basic config file. Most examples dealing with loading DLL's propose a class with only pure virtual functions and barely any member variables. I'm not sure what I should do.

Christophe
  • 54,708
  • 5
  • 52
  • 107
Patrick.SE
  • 3,876
  • 5
  • 28
  • 41
  • 1
    If I understand well, you goal is to offer Stage as a DLL and user could import it and derive it further. Is this your inted ? – Christophe Mar 04 '15 at 22:45
  • It's not entirely clear what your question is. But I'll point out the increasing popularity in "data pipes" to use [something like ZeroMQ](http://zeromq.org/). Compared to "plugins", this lets you define things in a way that they can be in-process, out-of-process, or even have the pieces running at different network locations in different languages. If you find you're designing a pipe interface and expecting different authors to wire things together, it might be worth a look for your purpose. (But I don't know your purpose.) – HostileFork says dont trust SE Mar 04 '15 at 22:51
  • @Christophe Yes, indeed. – Patrick.SE Mar 05 '15 at 00:47

1 Answers1

0

I understand you want to have stage in a DLL and have user users derive their work on your DLL.

Scenario 1: consumer and DLL build with same compiler and same standard library

If the consumer uses the same compiler, compatible compiling options, and both, sides use the same shared standard library (default with MSVC), then you solution should work as is

(See related SO question.)

Scenario 2: consumer and DLL build with same compiler but different libraries

If one side uses a different standard library or linking option thereof (for example if you use statically linked library for your DLL and shared library for consumer), then you have to make sure that all objects are ALWAYS created/released on the same side (because DLL and application would each use their own alloaction functions with different memory pools).

This will be very difficult because of:

  • inheritance of data
  • virtual destructor
  • storage management of standard containers (which would be different in DLL and consumer, despite the impression the source code might give that it's the same)

The first step in the right direction would then be to isolate all the data into the private section and ensure clean access via getters and setters. Fortunately, this kind of design is a sound design approach for inheritance, so it's worth to use it even if you don't need it.

Scenario 3: different compilers or incompatible compiling options

If you use different compilers, or incompatible compiling options then the real problems start:

  • you can't rely on the assumption that both sides have the same understanding of memory layout. So the read/write of members might occur at different locations; a huge mess ! This is why so many DLL classes have no data member. Many also use PIMPL idiom to hide a private memory layout. But PIMPL in this case of inheritance is very similar to using private data (*this would then be the implicit pointer to the private implementation)
  • the compiler/linker use "mangled" function names. Different compilers might use different mangling, and wouldn't understand each other's symbol definitions (i.e. the client would'nt find SetOutputPipe() despite it's there). This is why most DLL put all member functions as virtual : the functions are called via an offset in a vtable, which uses fortunately the same layout accross compilers in practice.
  • finally, different compilers could use different calling conventions. But I think in practice on well established platforms, this shouldn't be a major risk

In this answer to a question about DLLs with different compilers (also without inheritance), I've provided some additional explanations and references that could be relevant for such a mixed scenario.

Again using private member data isntead of protected would put you on a safer side. Exposing getters/setters (whether protected or public) using an extern "C" linkage specifier would avoid name mangling issues for non virtual functions.

Final remark:

When exposing classes in libraries, extra-care should be taken to design the class, making data private. This good practice is worth an extra thought, what ever scenario you're in.

Community
  • 1
  • 1
Christophe
  • 54,708
  • 5
  • 52
  • 107