Each plugin is a class deriving from Shape, in its own .cpp, compiled to its own MODULE library. This page shows all three.

Square — plain REGISTER

// Square.cpp  → libSquare.so
#include "Square.hh"
#include <plugin/PluginRegister.hh>
#include "Shape.hh"

namespace plugin { namespace example {

    Square::Square()                     { size = 0; }
    Square::Square( unsigned int a )     { size = a; }
    Square::Square( Square const& o )    { size = o.size; }

    void         Square::setSize( unsigned int v )       { size = v; }
    unsigned int Square::getSize() const                 { return size; }
    void         Square::printOn( std::ostream& s ) const { s << "Square a:" << size << '\n'; }

    REGISTER( Square, Shape );

}}

REGISTER(Square, Shape) registers a default factory under "Square". Shape::create("Square")new Square(). There is no ConfigSquare( unsigned int ) exists but the catalog never calls it; the driver uses setSize after creation.

Circle — REGISTERWITHCONFIG

// Circle.hh
class Circle : public Shape
{
public:
    struct Config { unsigned int r = 0; };

    Circle();
    explicit Circle( Config const& cfg );
    Circle( Circle const& object );
    // ... overrides ...
private:
    unsigned int radius;
};
// Circle.cpp  → libCircle.so
Circle::Circle()                     : radius( 0 )            {}
Circle::Circle( Config const& cfg )  : radius( cfg.r )        {}
Circle::Circle( Circle const& o )    : Shape( o ), radius( o.radius ) {}

void Circle::printOn( std::ostream& s ) const { s << "Circle r:" << radius << '\n'; }

REGISTER_WITH_CONFIG( Circle, Shape, Circle::Config );

REGISTER_WITH_CONFIG registers both factories under "Circle":

  • Shape::create("Circle")Circle() (radius 0);
  • Shape::create("Circle", Circle::Config{42})Circle(Config{42}) (radius 42), the config type-checked through std::any.

Note the copy constructor forwards to Shape(o) — the CRTP base has state to copy (none here, but the discipline matters once a base gains members).

Ellipse — two config fields

// Ellipse.hh
struct Config { unsigned int a = 0; unsigned int b = 0; };
// Ellipse.cpp  → libEllipse.so
Ellipse::Ellipse( Config const& cfg ) : semiAxisA( cfg.a ), semiAxisB( cfg.b ) {}

void Ellipse::setSize( unsigned int v ) { semiAxisA = v; semiAxisB = v; }
void Ellipse::printOn( std::ostream& s ) const
    { s << "Ellipse a:" << semiAxisA << " b:" << semiAxisB << '\n'; }

REGISTER_WITH_CONFIG( Ellipse, Shape, Ellipse::Config );

Shape::create("Ellipse", Ellipse::Config{10, 20})Ellipse a:10 b:20. setSize(v) sets both axes.

The CMake targets

foreach( shape Circle Square Ellipse )
    add_library( ${shape} MODULE ${shape}.cpp ${shape}.hh )
    set_target_properties( ${shape} PROPERTIES
        VERSION 1.0.0 SOVERSION 1 POSITION_INDEPENDENT_CODE ON )
    target_link_libraries( ${shape} Shape )
endforeach()

MODULEdlopen-only, never linked. Each links Shape (for the base class and its operator<<). On Linux CMake names them libCircle.so etc., which is why the driver sets DSOLoader::prefix("lib").

Next: The driver.