plugin's DSO loader, dsold, is a shared library with its own exception base class — and it generates its exceptions with mstring, at build time.
The base class
dsold's fedem::exception::Exception derives from std::exception, implements what() and description(), and leaves the text to a pure virtual method:
class PLUGIN_EXPORT Exception : public std::exception
{
public:
virtual std::string const description( ) const noexcept;
virtual std::string whatStr( ) const noexcept = 0;
char const* what( ) const noexcept override;
...
};
DSOExceptions.msg
# SPDX-License-Identifier: MIT
#
# DSOExceptions.msg — the exceptions dsold throws.
#
# mstring generates DSOExceptions.hh / DSOExceptions.cpp from this file at
# build time, into <build>/src/dso/generated/dso/ (see CMakeLists.txt next to
# it); the header is installed with the public headers. Edit this file, not
# the generated code.
$MODULE DSOExceptions
$NAMESPACE fedem :: exception
$EXPORT PLUGIN_EXPORT
$INCLUDE "Exception.hh"
$INCLUDE <dso/DSOVisibility.hh>
$LANGUAGE C
# Every exception derives from fedem::exception::Exception, whose what() and
# description() are built on the pure virtual whatStr( ) implemented here.
# A parameter is also the name of its getter: FileNotFound::missingFilename( ).
$EXCEPTION ENABLE NOPREFIX NOPOSTFIX
$EXCEPTION INHERITED PUBLIC Exception
$EXCEPTION OVERRIDE whatStr
# The DSO — or a library it needs — could not be found or loaded.
$MESSAGE FileNotFound
@ missingFilename std::string const &
@ reason std::string const &
|The file '{$missingFilename}' is not found.
|{$reason}
# DSOLoader::loadVerified( ): the DSO's trust level is below the required
# minimum (REJECTED, UNKNOWN or UNSIGNED, depending on minTrust).
$MESSAGE SignatureRejected
@ soPath std::string const &
@ reason std::string const &
"DSO signature check failed for \"{$soPath}\": {$reason}"
$EXPORT PLUGIN_EXPORT— the classes are part of the library's interface.$EXCEPTION … OVERRIDE whatStr— each implements the base'swhatStr()instead of generating its ownWhat()/what().NOPREFIX NOPOSTFIX— the classes are calledFileNotFoundandSignatureRejected; the parameter names are the getters:ex.missingFilename( ),ex.reason( ).
class PLUGIN_EXPORT FileNotFound
: public Exception
{
std::locale internal_locale;
std::decay_t< std::string const & > member_missingFilename;
std::decay_t< std::string const & > member_reason;
public:
FileNotFound( std::string const & missingFilename, std::string const & reason,
std::locale const &loc );
FileNotFound( std::string const & missingFilename, std::string const & reason );
virtual ~FileNotFound( );
std::string whatStr( ) const noexcept override;
std::decay_t< std::string const & > const &missingFilename( ) const;
std::decay_t< std::string const & > const &reason( ) const;
};
ExceptionMessages.msg
The texts Exception::description() is assembled from — private to the library, so no $EXPORT:
# SPDX-License-Identifier: MIT
#
# ExceptionMessages.msg — the texts Exception::description( ) is built from.
#
# mstring generates ExceptionMessages.hh / ExceptionMessages.cpp from this
# file at build time (see CMakeLists.txt next to it). The header is private
# to dsold.
$MODULE ExceptionMessages
$NAMESPACE fedem :: exception
$LANGUAGE C
$STRING ENABLE NOPREFIX NOPOSTFIX
$MESSAGE descriptionHead
@ message std::string const &
|Exception
| Message : {$message}
# Only in debug builds (see Exception::description( )).
$MESSAGE descriptionOptional
@ file std::string const &
@ lineNo unsigned long int
| Filename : '{$file}'
| Line Number : '{$lineNo}'
The build
find_package(mstring 1.3 REQUIRED)
mstring_generate(DSOLOADER_GENERATED DSOExceptions.msg
OUTPUT_DIRECTORY ${DSOLOADER_GENERATED_DIR}/dso)
mstring_generate(DSOLOADER_GENERATED ExceptionMessages.msg
OUTPUT_DIRECTORY ${DSOLOADER_GENERATED_DIR}/dso)
add_library(dsold SHARED DSOLoader.cpp Exception.cpp ${DSOLOADER_GENERATED})
The generated DSOExceptions.hh is installed with the public headers; plugin's conanfile.py has tool_requires("mstring/[>=1.3 <2]"), and plugin's consumers — FFS among them — never need mstring.
Before mstring 1.3 these files had been generated once and then edited by hand until the message files no longer described them. $EXPORT and $EXCEPTION OVERRIDE exist so they could be generated again.

