Fmt: Allow specifying named arguments as brace initializer lists

Created on 26 Feb 2018  路  11Comments  路  Source: fmtlib/fmt

It would be extremely convenient if we could write

fmt::format("{foo} {bar}", {"foo", 123}, {"bar", "..."});

Please consider it.

enhancement help wanted

Most helpful comment

@rokups, the Twitter crowd prefers your proposed API (https://twitter.com/vzverovich/status/970309902374617090):

screenshot 2018-03-07 05 30 17_preview

All 11 comments

Interesting idea. Does this API have any advantage over the currently supported UDL-based one?

fmt::format("{foo} {bar}", "foo"_a=123, "bar"_a="...");

I would argue it is more self-explanatory and intuitive.

The thing about UDLs is that you generally have to write at least one using somewhere, which might be something that you may not want to do when having a single call to fmt::format in the middle of a function.

Seems reasonable. PRs are welcome =).

@rokups, the Twitter crowd prefers your proposed API (https://twitter.com/vzverovich/status/970309902374617090):

screenshot 2018-03-07 05 30 17_preview

One small concern I have with this API is that it can be a bit confusing when the arguments are strings (i.e. it'd be harder to tell from a glance in {"foo", "bar"} what is the name and what is the arg, especially if there is also {'bar", "foo"}), but it's nothing critical (it's not hard to remember the order once you've used it).

How is it hard? Pairs are always defined as {"key", "value"}. Maybe it is hard for one that does not know the language, but then again in such case everything is hard in c++.

You can use fmt::arg:

fmt::format("{foo} {bar}", fmt::arg("foo", 123), fmt::arg("bar", "..."));

This problem(enhancement) is actually harder than it seems.

The templated arg function (in core.h) takes in a string view and a templated variable, and constructs a class called named_arg.

The problem is that initializer lists need to construct an object, but the object, like the arg function, also need to be templated, and it seems like you can鈥檛 deduce templates in function parameters, so you need to construct the object before passing it to the function, which is not optimal.

basically

template<typename T>
struct obj {
    string_view view;
    T val;
    obj(const string_view& _view, const T& _val) : view(_view), val(_val) {}
    auto make_arg(){/*make named arg*/}
};

template<typename String>
print(String str, const obj& ...args) {
// obj needs to be templated, but the template cannot be deduced from a function parameter
// there is also (I think) no way of having an array of different data types without destroying the type
}

fmt::print("{name}", {"name", "val"});

I'm fairly sure that this is impossible, I worked very hard trying to do something similar for a logging framework I was involved in and it just didn't work. Initializer lists can't be forwarded, so variadics (which generally depend on forwarding) and initializer lists don't mix. I tried to pull the variadic template as a template parameter in, but that didn't work for reasons that are unclear to me: http://coliru.stacked-crooked.com/a/6561625a609d82b9. Anyhow, I mostly commented so I can be on this thread, so that if someone succeeds I can learn something :-).

It looks like this is impossible unfortunately, so closing for now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

beojan picture beojan  路  5Comments

wkenyon picture wkenyon  路  3Comments

stryku picture stryku  路  4Comments

dalle picture dalle  路  3Comments

ugenehan picture ugenehan  路  3Comments