The INLINE test in the source tree, tests/inline/, is also the example of header-only modules.

Two INLINE modules

# INLINE: header only; several languages (the shared locale matcher), a
# format, every artefact that has a definition.
$MODULE alpha INLINE
$NAMESPACE inl
$INCLUDE <iomanip>
$LANGUAGE en
$STRING ENABLE
$STREAMABLE ENABLE
$THROW ENABLE
$EXCEPTION INHERITED std::exception
$MESSAGE Alpha
@ n int
"alpha {$n;std::setw(3);fill('0')}"
[C.utf8]
"alpha C.utf8 {$n}"
# A second INLINE module in the same translation unit: its helpers must not
# collide with alpha's.
$MODULE beta INLINE
$NAMESPACE inl
$LANGUAGE en
$STRING ENABLE
$SYSLOG ENABLE
$MESSAGE Beta
@ who std::string const &
"beta {$who}"
[C.utf8]
"beta C.utf8 {$who}"

and an ordinary one:

# An ordinary module (source file) next to the INLINE ones.
$MODULE gamma
$NAMESPACE inl
$LANGUAGE en
$STRING ENABLE
$MESSAGE Gamma
@ n long
"gamma {$n}"
[C.utf8]
"gamma C.utf8 {$n}"

alpha.hh and beta.hh hold every definition; there is no alpha.cpp. Both need the locale matcher and the value appender; they share one copy through namespace mstring_detail_v1, whose blocks are include-guarded. gamma.cpp keeps its own in an unnamed mstring_local_v1, so the three coexist.

Two translation units

// A second translation unit including the INLINE headers: the definitions
// must link without duplicates.
#include "alpha.hh"
#include "beta.hh"

std::string Other( )
{
  return inl::AlphaStr( 5 ) + " / " + inl::BetaStr( "other" );
}
#include "alpha.hh"
#include "beta.hh"
#include "gamma.hh"

#include <iostream>
#include <locale>

std::string Other( );

int main( )
{
  std::locale const utf8( "C.utf8" );

  std::cout << inl::AlphaStr( 7 ) << '\n'
            << inl::AlphaStr( 7, utf8 ) << '\n'
            << inl::AlphaStreamable( 8 ) << '\n'
            << inl::BetaStr( "ada", utf8 ) << '\n'
            << inl::GammaStr( 9L, utf8 ) << '\n'
            << Other( ) << '\n';

  try
  {
    inl::ThrowAlpha( 1 );
  }
  catch ( std::exception const &e )
  {
    std::cout << e.what( ) << '\n';
  }
}

Both main.cpp and other.cpp include alpha.hh and beta.hh: the inline definitions link once.

alpha 007
alpha C.utf8 7
alpha 008
beta C.utf8 ada
gamma C.utf8 9
alpha 005 / beta other
alpha 001

CMake

mstring_generate(GEN alpha.msg INLINE)
mstring_generate(GEN beta.msg INLINE)
mstring_generate(GEN gamma.msg)
add_executable(app main.cpp other.cpp ${GEN})

INLINE tells mstring_generate() not to wait for a source file.

What a call costs

From bench/ (gcc 11, -O2): a two-parameter string function 94 ns, a constant text 40 ns, a five-language message 134 ns — see Performance.