fmtlib / fmt

A modern formatting library
https://fmt.dev
Other
19.84k stars 2.42k forks source link

How do you forward fmt::format_string without explicitly indicating it in the interface? #3956

Closed sycc90 closed 1 month ago

sycc90 commented 1 month ago

I'm trying to create a generic function that wraps the creation of some objects, this needs to forward any arguments to some other object but I'm having trouble when any of those objects needs compile-time-checked format strings for fmtlib.

I am aware of how to create wrapper functions that directly take a format string, such as template<class... Args> void foo(fmt::format_string<Args...>, Args&&...). However, in this case I need to signature to be valid when calling without a format string as well.

Something like this is what I'm trying to get:

template<class T, class... Args> T create(Args&&... args) {
    return T(std::forward<Args>(args)...);
}

I can't just use fmt::format_string<Args...> as the first argument of the function because some objects may not have such constructor.

I have tried creating a second overload:

template<class T, class... Args> T create(fmt::format_string<Args...> f, Args&&... args) {
    return T(f, std::forward<Args>(args)...);
}

but obviously that fails since the compiler can't correctly determine which one to use. For example if I'm trying to create an object that takes a std::string_view as constructor argument.

Here's an example in godbolt: https://godbolt.org/z/WEKn1MMj3

Is there any way to solve this problem? Or am I trying to do something that is just impossible?

vitaut commented 1 month ago

To make compile-time checks work format_string must appear at the outermost level (see https://vitaut.net/posts/2021/safe-formatting-api/ for an explanation), so a single function with forwarding won't work. You could probably constrain the first overload to not participate in overload resolution using SFINAE when the first argument is convertible to format_string.