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 Config — Square( 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 throughstd::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()
MODULE — dlopen-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.

