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's whatStr() instead of generating its own What() / what().
  • NOPREFIX NOPOSTFIX — the classes are called FileNotFound and SignatureRejected; 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.