42 [[nodiscard]] std::string
format(std::string_view message, Args&&... args)
const {
43 static_assert(
sizeof...(Args) <= 99,
"Message templates support at most 99 parameters");
45 const std::array<std::string,
sizeof...(Args)> parameters{
46 parameter_string(std::forward<Args>(args))...
48 std::array<bool,
sizeof...(Args)> used{};
50 result.reserve(message.size());
52 for (std::size_t position = 0; position < message.size();) {
53 const auto next = position + 1;
54 if (message[position] !=
'%' || next == message.size()) {
55 result += message[position++];
58 if (message[next] ==
'%') {
63 if (message[next] <
'0' || message[next] >
'9') {
64 result += message[position++];
67 if (message[next] ==
'0') {
68 throw std::invalid_argument(
"Message placeholders must start with %1 through %99");
71 std::size_t index = 0;
73 while (position < message.size() && message[position] >=
'0' && message[position] <=
'9') {
74 index = index * 10 +
static_cast<std::size_t
>(message[position++] -
'0');
75 if (index > 99)
throw std::invalid_argument(
"Message placeholder exceeds %99");
77 if (index > parameters.size()) {
78 throw std::invalid_argument(
"Message placeholder has no corresponding parameter");
80 result += parameters[index - 1];
81 used[index - 1] =
true;
83 for (
bool referenced : used) {
84 if (!referenced)
throw std::invalid_argument(
"Message template leaves a parameter unused");