Skip to content

Using FMT_COMPILE with format specifiers for fmt::join gives compilation errors about constness. #2719

Description

@richardebeling

I'm trying to format binary data into its hexadecimal representation, using fmt::join with FMT_COMPILE.

From the documentation, I'd expect this program to do the job:

#include "fmt/core.h"
#include "fmt/compile.h"

int main() {
    unsigned char data[] = {0x1, 0x2};
    auto s = fmt::format(FMT_COMPILE("{:02x}"), fmt::join(data, ""));
}

However, starting at v8.0.0, it gives the following error when compiling:
error on g++ (Ubuntu 11.2.0-7ubuntu2) 11.2.0:

$ g++ -std=gnu++20 -o main test.cpp
In file included from test.cpp:2:
fmt/compile.h: In instantiation of ‘constexpr OutputIt fmt::v8::detail::spec_field<Char, T, N>::format(OutputIt, const Args& ...) const [with OutputIt = std::back_insert_iterator<std::__cxx11::basic_string<char> >; Args = {fmt::v8::join_view<unsigned int*, unsigned int*, char>}; Char = char; T = fmt::v8::join_view<unsigned int*, unsigned int*, char>; int N = 0]’:
fmt/compile.h:546:3:   required from ‘std::__cxx11::basic_string<Char> fmt::v8::format(const CompiledFormat&, const Args& ...) [with CompiledFormat = fmt::v8::detail::spec_field<char, fmt::v8::join_view<unsigned int*, unsigned int*, char>, 0>; Args = {fmt::v8::join_view<unsigned int*, unsigned int*, char>}; Char = char; typename std::enable_if<fmt::v8::detail::is_compiled_format<T>::value, int>::type <anonymous> = 0]’
fmt/compile.h:579:18:   required from ‘std::__cxx11::basic_string<typename S::char_type> fmt::v8::format(const S&, Args&& ...) [with S = main()::<lambda()>::FMT_COMPILE_STRING; Args = {fmt::v8::join_view<unsigned int*, unsigned int*, char>}; typename std::enable_if<fmt::v8::detail::is_compiled_string<S>::value, int>::type <anonymous> = 0; typename S::char_type = char; typename Context::char_type = char]’
test.cpp:6:25:   required from here
fmt/compile.h:313:22: error: passing ‘const fmt::v8::formatter<fmt::v8::join_view<unsigned int*, unsigned int*, char>, char, void>’ as ‘this’ argument discards qualifiers [-fpermissive]
  313 |     return fmt.format(get_arg_checked<T, N>(args...), ctx);
      |            ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from fmt/compile.h:11,
                 from test.cpp:2:
fmt/format.h:2821:8: note:   in call to ‘decltype (ctx.out()) fmt::v8::formatter<fmt::v8::join_view<It, Sentinel, Char>, Char>::format(const fmt::v8::join_view<It, Sentinel, Char>&, FormatContext&) [with FormatContext = fmt::v8::basic_format_context<std::back_insert_iterator<std::__cxx11::basic_string<char> >, char>; It = unsigned int*; Sentinel = unsigned int*; Char = char; decltype (ctx.out()) = std::back_insert_iterator<std::__cxx11::basic_string<char> >]’
 2821 |   auto format(const join_view<It, Sentinel, Char>& value, FormatContext& ctx)
      |        ^~~~~~

error on clang++ 13 (Ubuntu clang version 13.0.0-2):

$ clang++ -std=gnu++20 -o main test.cpp
In file included from test.cpp:2:
./fmt/compile.h:313:16: error: no matching member function for call to 'format'
    return fmt.format(get_arg_checked<T, N>(args...), ctx);
           ~~~~^~~~~~
./fmt/compile.h:546:6: note: in instantiation of function template specialization 'fmt::detail::spec_field<char, fmt::join_view<unsigned int *, unsigned int *>, 0>::format<std::back_insert_iterator<std::basic_string<char>>, fmt::join_view<unsigned int *, unsigned int *>>' requested here
  cf.format(std::back_inserter(s), args...);
     ^
./fmt/compile.h:579:12: note: in instantiation of function template specialization 'fmt::format<fmt::detail::spec_field<char, fmt::join_view<unsigned int *, unsigned int *>, 0>, fmt::join_view<unsigned int *, unsigned int *>, char, 0>' requested here
    return format(compiled, std::forward<Args>(args)...);
           ^
test.cpp:6:19: note: in instantiation of function template specialization 'fmt::format<FMT_COMPILE_STRING, fmt::join_view<unsigned int *, unsigned int *>, 0>' requested here
    auto s = fmt::format(FMT_COMPILE("{:02x}"), fmt::join(data, ""));
                  ^
./fmt/format.h:2821:8: note: candidate function template not viable: 'this' argument has type 'const formatter<fmt::join_view<unsigned int *, unsigned int *>, char>', but method is not marked const
  auto format(const join_view<It, Sentinel, Char>& value, FormatContext& ctx)
       ^
1 error generated.

On 7.1.3, it worked fine. When removing the compilation, it also works fine. When removing the format specifier ({:02x} -> {}), it also works

The changelogs for version 8.0.0 included this note:

Format string compilation now requires format functions of formatter specializations for user-defined types to be const:

template <> struct fmt::formatter<my_type>: formatter<string_view> {
  template <typename FormatContext>
  auto format(my_type obj, FormatContext& ctx) const {  // Note const here.
    // ...
  }
};

However, from what I understand, I'm not using a user defined formatter.

Annotating the format method in fmt/format.h:2821 with the const qualifier (however little sense this might make) makes the compilation pass.

Am I doing something wrong, or is this a bug?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions