Support variable number of arguments to inject at runtime (#1580)

Further, switching internal member from array_view to span to
be closer to standard, and more constexpr friendly.

Also adjusting to C++11 template syntax.

Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>
This commit is contained in:
Roland Reichwein 2026-09-01 20:16:24 +02:00 committed by GitHub
parent 06e54f6b23
commit c896d2d7dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 38 additions and 7 deletions

View File

@ -35,7 +35,6 @@ SOFTWARE.
#include "algorithm.h"
#include "array.h"
#include "array_view.h"
#include "error_handler.h"
#include "limits.h"
#include "math.h"
@ -627,7 +626,7 @@ namespace etl
};
template <typename T>
struct has_formatter_parse<T, etl::void_t<decltype(etl::declval<etl::formatter<T>&>().parse(etl::declval<format_parse_context&>()))> >
struct has_formatter_parse<T, etl::void_t<decltype(etl::declval<etl::formatter<T>&>().parse(etl::declval<format_parse_context&>()))>>
: etl::true_type
{
};
@ -642,7 +641,7 @@ namespace etl
template <typename T>
struct has_formatter_format<T, etl::void_t<decltype(etl::declval<etl::formatter<T>&>().format(
etl::declval<const T&>(), etl::declval<etl::basic_format_context<char*, char>&>()))> > : etl::true_type
etl::declval<const T&>(), etl::declval<etl::basic_format_context<char*, char>&>()))>> : etl::true_type
{
};
@ -806,7 +805,7 @@ namespace etl
// Converting constructor for user-defined types that provide an
// etl::formatter<T> specialisation. The value is stored type-erased in a
// handle, matching the std::basic_format_arg behaviour for custom types.
template <typename T, typename = etl::enable_if_t<private_format::is_formattable<T>::value> >
template <typename T, typename = etl::enable_if_t<private_format::is_formattable<T>::value>>
basic_format_arg(const T& v)
: data(handle(v))
{
@ -863,7 +862,7 @@ namespace etl
return _args.get(i);
}
etl::array_view<basic_format_arg<Context>> get()
etl::span<basic_format_arg<Context>> get()
{
return _args;
}
@ -884,6 +883,15 @@ namespace etl
{
}
// non-standard
// Constructs from externally owned storage, for cases where the number of
// arguments is only known at runtime and so no format_arg_store<Context, Args...>
// can be formed. The referenced storage must outlive this object.
explicit basic_format_args(etl::span<basic_format_arg<Context>> args_)
: _args(args_)
{
}
basic_format_args(const basic_format_args<Context>& other)
: _args(other._args)
{
@ -908,7 +916,7 @@ namespace etl
private:
etl::array_view<basic_format_arg<Context>> _args;
etl::span<basic_format_arg<Context>> _args;
};
namespace private_format
@ -2019,7 +2027,7 @@ namespace etl
}
// for user-defined types routed through basic_format_arg::handle
void operator()(typename basic_format_arg<format_context<OutputIt> >::handle h)
void operator()(typename basic_format_arg<format_context<OutputIt>>::handle h)
{
h.format(parse_ctx, fmt_ctx);
}

View File

@ -950,6 +950,29 @@ namespace
CHECK_EQUAL("010", test_format(s, "{:#o}", 8));
}
//*************************************************************************
// Arguments whose count is only known at runtime cannot be held in a
// format_arg_store<Context, Args...>, so basic_format_args can also be built
// directly over caller owned storage.
TEST(test_vformat_to_runtime_sized_args)
{
etl::string<100> s;
using iterator_t = etl::back_insert_iterator<etl::string<100>>;
using arg_t = etl::format_arg<iterator_t>;
etl::array<arg_t, 4> storage = {arg_t(1), arg_t("two"), arg_t(3), arg_t('x')};
// Only the first three of the four slots are used.
etl::vformat_to(etl::back_inserter(s), "{} {} {}", etl::format_args<iterator_t>(etl::span<arg_t>(storage.data(), 3U)));
CHECK_EQUAL("1 two 3", s);
// An empty argument list is also valid.
s.clear();
etl::vformat_to(etl::back_inserter(s), "no args", etl::format_args<iterator_t>(etl::span<arg_t>()));
CHECK_EQUAL("no args", s);
}
//*************************************************************************
TEST(test_format_custom_type)
{