// SPDX-License-Identifier: MIT
#pragma once
#include <any>
#include <functional>
#include <stdexcept>
#include <dso/DSOVisibility.hh>
namespace plugin
{
/**
* @brief Factory class for plugins.
*
* Stores two factory callables:
* - A default factory: creates an instance with no arguments
* - A config factory: creates an instance from a type-erased std::any
*
* The config factory is optional. If not provided, attempting to create
* with a config argument will throw std::runtime_error.
*
* @tparam Base The base class of the plugin hierarchy
*/
template< class Base >
class PLUGIN_EXPORT Creator
{
public:
using DefaultFactory = std::function< Base*( ) >;
using ConfigFactory = std::function< Base*( std::any const& ) >;
Creator( )
: alias_( true )
{
}
/**
* @brief Constructor with default factory only (backward compatible).
*/
Creator( DefaultFactory defaultFn, bool isAlias = false )
: defaultFactory_( std::move( defaultFn ) ),
alias_( isAlias )
{
}
/**
* @brief Constructor with both default and config factories.
*/
Creator( DefaultFactory defaultFn, ConfigFactory configFn, bool isAlias = false )
: defaultFactory_( std::move( defaultFn ) ),
configFactory_( std::move( configFn ) ),
alias_( isAlias )
{
}
Creator( Creator const& ) = default;
Creator( Creator&& ) = default;
Creator& operator=( Creator const& ) = default;
Creator& operator=( Creator&& ) = default;
~Creator( ) = default;
/**
* @brief Create an instance using the default (parameterless) factory.
* @return Pointer to new instance, or nullptr if no default factory is set.
*/
Base* operator( )( )
{
return defaultFactory_ ? defaultFactory_( ) : nullptr;
}
/**
* @brief Create an instance using a type-erased config.
* The config's actual type must match what was registered;
* a mismatch will throw std::bad_any_cast.
*
* @throws std::runtime_error if no config factory was registered
* @throws std::bad_any_cast if the config type doesn't match
*/
Base* operator( )( std::any const& config )
{
if( ! configFactory_ )
{
throw std::runtime_error(
"This plugin was registered without config support. "
"Use the parameterless create() or register with REGISTER_WITH_CONFIG." );
}
return configFactory_( config );
}
bool hasConfigFactory( ) const { return static_cast< bool >( configFactory_ ); }
bool isAlias( ) const { return alias_; }
private:
DefaultFactory defaultFactory_;
ConfigFactory configFactory_;
bool alias_ = true;
};
} // namespace plugin