The new configuration system relieas upon static varibles being
used for declaring what arguments a particular module uses. To
ensure that the destruction order does not matter, we redundantly
store the needed data (the name).
The name of the object (i.e. the section name from the configuration
file), is now stored in the configuration object for that object.
That way, more contextual and hence morfe user friendly errors and
warnings can be generated.
Rename config::Configuration::configure() to
config::Configuration::post_configure(). Latter name makes it
unambiguously clear at what point the function is called.
The configuration mechanism consists of the following concepts:
Specification
Specifies the available configuration parameters of a module,
their names and their types.
Param
Specifies a parameter, its name and its type.
Type
Specifies the type of a configuration parameters; Bool, Size,
Count, etc.
Configuration
Specifies the configuration values of a particular instance of
the module. Configuration walks hand in hand with Specification,
the latter specifies what the former should contain.
A Specification is capable of configuring a Configuration from a
MXS_CONFIG_PARAMETER, checking in the process that all parameters
are of the correct type and that the required parameters are present.
A Specification is capable of persisting itself so that it later
can be read back.
The mechanism is closed for modification but open for extension in
the sense that if a module requires a custom parameter, all it needs
to do is to derive one class from Param and another from Type.
The canonical way for using this mechanism is as follows. Consider
a module xyx that has three parameters; a parameter called
"enabled" that is of boolean type, a parameter called "period"
that is of duration type, and a parameter "cache" that is of
size type. That would be declared as follows:
// xyz.hh
class XYZSession;
class XYZ : public maxscale::Filter<XYZ, XYZSession>
{
public:
static XYZ* create(const char* zName, MXS_CONFIG_PARAMETER* pParams);
private:
XYZ();
static config::Specification s_specification;
static config::ParamBool s_enabled;
static config::ParamDuration<std::chrono::seconds> s_period;
static config::ParamSize s_cache;
config::Configuration m_configuration;
config::Bool m_enabled;
config::Duration<std::chrono::seconds> m_period;
config::Size m_cache;
};
// xyz.cc
config::Specification XYZ::s_specification(MXS_MODULE_NAME);
config::ParamBool XYZ::s_enabled(
&s_specification,
"enabled",
"Specifies whether ... should be enabled or not."
);
config::ParamDuration<std::chrono::seconds> XYZ::s_period(
&s_specification,
"period",
"Specifies the period. Rounded to the nearest second."
);
config::ParamSize XYZ::s_cache(
&s_specification,
"cache",
"Specifies the size of the internal cache."
);
XYZ::XYZ()
: m_configuration(&s_specification)
, m_enabled(&m_configuration, &s_enabled)
, m_period(&m_configuration, &s_period)
, m_cache(&m_configuration, &s_cache)
{
}
XYZ* XYZ::create(const char* zName, MXS_CONFIG_PARAMETER* pParams)
{
XYZ* pXyz = new XYZ;
if (!s_specification.configure(pXyz->m_configuration, pParams))
{
delete pXyz;
pXyz = nullptr;
}
return pXyz;
}