Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

regression in 'constexpr' expansion of 'fmt::v8::basic_format_string...' #2898

Closed
RalphSteinhagen opened this issue May 17, 2022 · 4 comments
Labels

Comments

@RalphSteinhagen
Copy link

Hi, first of all: thanks for the great library! 👍

While bumping versions, I may have found a regression starting from v8.0.1 (v7.1.3 tested OK).
The following code snippet (see also compiler-explorer)

#include <iostream>
#include <string>
#include <fmt/format.h>

template<bool strict = true>
std::string err_format(const char *fmt,  const auto &...errorArgs) {
    if constexpr (strict) {
        return fmt::format(fmt, errorArgs...);
    }
    return "do something else";
}

int main() {
    std::cout << err_format("exception: {} {}!", "Hello", "World");
}

yields the following compile-time error:

<source>: In instantiation of 'std::string err_format(const char*, const auto:14& ...) [with bool strict = true; auto:14 = {char [6], char [6]}; std::string = std::__cxx11::basic_string<char>]':
<source>:14:28:   required from here
<source>:8:27:   in 'constexpr' expansion of 'fmt::v8::basic_format_string<char, const char (&)[6], const char (&)[6]>(fmt)'
<source>:8:27: error: 'fmt' is not a constant expression
    8 |         return fmt::format(fmt, errorArgs...);

Tested this also with older gcc and clang versions with similar results.

Any ideas, hints or work-around suggestions would be much welcome! Thanks in advance!

@vitaut
Copy link
Contributor

vitaut commented May 17, 2022

{fmt} 8.x requires format strings to be known at compile time by default. Quoting release notes:

  • Enabled compile-time format string check by default. For example (godbolt):

    #include <fmt/core.h>
    
    int main() {
      fmt::print("{:d}", "I am not a number");
    }

    gives a compile-time error on compilers with C++20 consteval support (gcc 10+, clang 11+) because d is not a valid format specifier for a string.

    To pass a runtime string wrap it in fmt::runtime:

    fmt::print(fmt::runtime("{:d}"), "I am not a number");

@RalphSteinhagen
Copy link
Author

RalphSteinhagen commented May 19, 2022

@vitaut I understand the compile-time format evaluation/validateion.

I believe the above issue is slightly different w.r.t. that both the format and arguments are constant and could be evaluated during compile time (ie. effectively constexpr) but because the fmt is passed as a function parameter it is not detected as strictly constexpr.

Isn't there a way to have a fall-back in case the strict compile-time evaluation fails. In the example above, the function err_format cannot be constexpr because std::string does not allow constexpr allocation/initialisation across function boundaries.

Is the behaviour you quoted compatible with the defined behaviour for std::format(...)?

@vitaut
Copy link
Contributor

vitaut commented May 19, 2022

To make this work (with or without strict) you'll need to pass the format string as fmt::format_string:

template<typename... T>
std::string err_format(fmt::format_string<T...> fmt, T&&... errorArgs) {
  return fmt::format(fmt, std::forward<T>(errorArgs)...);
}

Is the behaviour you quoted compatible with the defined behaviour for std::format(...)?

Yes.

@RalphSteinhagen
Copy link
Author

@vitaut thanks for the help and work-arounds! Much appreciated! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants