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

enum class support #391

Closed
daviswalker opened this issue Sep 28, 2016 · 17 comments
Closed

enum class support #391

daviswalker opened this issue Sep 28, 2016 · 17 comments

Comments

@daviswalker
Copy link

Hello - this library is a godsend. Thank you. I'm working in a project that uses a number of enum classes, e.g:

enum class ErrorCode : int {
SOME_ERROR;
}

I'm interested in formatting these objects as ints, but the built-in support wants to treat them as custom objects. I can't change these classes to add ostream support. What is the easiest way to do this? I tried adding a templatized report_arg overload, which works if I add it to the C file generating the log statement, but not if I add it the header from which I'm including format.h

template<class T,class=typename std::enable_if< std::is_enum::value >::type>
void format_arg(fmt::BasicFormatter &f, const char *, const T &e) {
f.writer() << (int)e;
}

Any suggestions would be very appreciated.

Dave

@daviswalker
Copy link
Author

Similar question is how best to add formatting support for a templated type like std::optional from the standard library that doesn't support the << operator.

Thanks again.

@vitaut
Copy link
Contributor

vitaut commented Sep 29, 2016

I'm glad you like the library =).

To format a enum class you'll have to provide an overloaded operator<< because there is no implicit conversion to int there:

enum class ErrorCode : int {
  SOME_ERROR
};

std::ostream &operator<<(std::ostream& os, ErrorCode ec) {
  return os << static_cast<int>(ec);
}

int main() {
  fmt::print("{}", ErrorCode::SOME_ERROR);
}

If you don't want to overload operator<<, you can provide a format_arg function instead but it is more evolved and not yet documented:

template <typename ArgFormatter>
void format_arg(fmt::BasicFormatter<char, ArgFormatter> &f,
                const char *&format_str, ErrorCode e) {
  f.writer() << (int)e;
}

There is an example here: https://github.com/fmtlib/fmt/blob/master/fmt/time.h#L18.

The same can be done for std::optional.

@daviswalker
Copy link
Author

daviswalker commented Sep 30, 2016

Okay got it thanks! Is it possible to template this? e.g.

template<typename T> 
std::ostream &operator<<(std::ostream& os, optional<T> o) {
    if (o) {
        return os << o;
    } else {
        return os << "<nullopt>";
    }
}

When I use this with a stringstream it compiles fine, e.g.:

std::optional<string> test = nullopt;
stringstream ss;
ss << test;

But if I do the same thing with fmt::format it doesn't resolve the template.

@vitaut
Copy link
Contributor

vitaut commented Sep 30, 2016

It should be possible to do that, but keep in mind that overloaded operator<< should either be declared before it is used in fmt/ostream.h or placed in the std namespace for ADL.

@daviswalker
Copy link
Author

Yes indeed - include order was my issue. Thank you for pointing me in the right direction.

Montellese referenced this issue in xbmc/xbmc Feb 2, 2020
Updates most dependencies to latest version
Fixes the store build by fixing python
Enables pillow and pycryptodome again on windows
Replaces easyhook with detours
@Paxxi
Copy link

Paxxi commented Feb 5, 2020

We wanted to have something that "just work" for the whole project so based on the advice in this thread we ended up with this.

#include <ostream>
#include <type_traits>

// clang-format off
// This is required to format enum classes with fmt as they're not implicitly
// convertible to int.
// This operator has to be declared before including format.h and ostream.h.
// We keep clang format disabled to avoid reordering and breaking things
// https://github.com/fmtlib/fmt/issues/391
template<typename T, typename std::enable_if_t<std::is_enum<T>::value, int> = 0>
std::ostream& operator<<(std::ostream& os, const T& value)
{
  return os << static_cast<int>(value);
}

#include <fmt/format.h>
#include <fmt/ostream.h>
// clang-format on

Put this in a header file like enum_format.hpp and include that instead of fmt/format.h and enums should just work everywhere in the project.

@vitaut
Copy link
Contributor

vitaut commented Feb 5, 2020

In recent versions of {fmt} enum classes are formatted as integers by default (https://godbolt.org/z/3Z9sj4):

#include <fmt/core.h>

enum class ErrorCode : int {
  SOME_ERROR
};

int main() {
  fmt::print("{}", ErrorCode::SOME_ERROR); // prints 0
}

@dominictobias
Copy link

dominictobias commented Feb 14, 2022

@vitaut unfortunately it didn't work for me with 8.1.1 or the most recent commit as of this post (ba6f89c)

In order to avoid these errors:

***/build/_deps/fmt-src/include/fmt/core.h:1706:3: error: static_assert failed due to requirement 'formattable' "Cannot format an argument. To make type T formattable provide a formatter<T> specialization: https://fmt.dev/latest/api.html#udt"
  static_assert(
  ^
***/build/_deps/fmt-src/include/fmt/core.h:1832:23: note: in instantiation of function template specialization 'fmt::detail::make_arg<true, fmt::basic_format_context<fmt::appender, char>, fmt::detail::type::custom_type, OrderSide &, 0>' requested here
        data_{detail::make_arg<
                      ^
***/build/_deps/fmt-src/include/fmt/core.h:1851:10: note: in instantiation of function template specialization 'fmt::format_arg_store<fmt::basic_format_context<fmt::appender, char>, unsigned long long, OrderSide>::format_arg_store<const unsigned long long &, OrderSide &>' requested here
  return {std::forward<Args>(args)...};
         ^
***/build/_deps/fmt-src/include/fmt/core.h:3191:28: note: in instantiation of function template specialization 'fmt::make_format_args<fmt::basic_format_context<fmt::appender, char>, const unsigned long long &, OrderSide &>' requested here
  const auto& vargs = fmt::make_format_args(args...);
                           ^
***/src/orderbook/side.cc:7:10: note: in instantiation of function template specialization 'fmt::print<const unsigned long long &, OrderSide &>' requested here
    fmt::print("Level price: {}, side: {}\n", price, level.side);
         ^
1 error generated.

I followed the link in the error and wrote a template which fixed the error:

enum class OrderStatus {
  open,
  partial_fill,
  filled,
  cancelled,
};

template <> struct fmt::formatter<OrderStatus> : formatter<string_view> {
  template <typename FormatContext> auto format(OrderStatus order_status, FormatContext &ctx) {
    string_view name = "unknown";
    switch (order_status) {
    case OrderStatus::open:
      name = "open";
      break;
    case OrderStatus::partial_fill:
      name = "partial_fill";
      break;
    case OrderStatus::filled:
      name = "filled";
      break;
    case OrderStatus::cancelled:
      name = "cancelled";
      break;
    }
    return formatter<string_view>::format(name, ctx);
  }
};

That's very verbose to write for each enum though and I would be ok if int worked out the box 🤔

@vitaut
Copy link
Contributor

vitaut commented Feb 14, 2022

Scoped enum are not implicitly convertible to integers in C++ but the current master supports a simplified API if you want to make an enum formattable as an underlying (integral) type:

auto format_as(OrderStatus s) { return fmt::underlying(s); }

@pagogc
Copy link

pagogc commented Mar 10, 2022

Same Problem as [DominicTobias] with 8.1.1

But i have implemented the operator<< for my Enum-Class (and it works correctly before)
std::ostream& operator<<(std::ostream& os, KonfigSetWatchType ec) { return os << static_cast<int>(ec); }

So not possible anymore?

MSVC 17.1.0, -std=C++20

@vitaut
Copy link
Contributor

vitaut commented Mar 10, 2022

ostream operator<< is supported: https://fmt.dev/dev/api.html#ostream-api

@pagogc
Copy link

pagogc commented Mar 23, 2022

Thanks, i missed the template specialization.
Now it works as expected.

Thanks again for this amazing, performant lib of yours!

@aserebryakov
Copy link

@vitaut,

ostream operator<< is supported: https://fmt.dev/dev/api.html#ostream-api

Just in case I want write some template function like:

template <typename T>
std::string toString(const T& value) {
    return fmt::format("{}", value);
}

that should accept enum class. Should I provide the formatter specialization per type?

@vitaut
Copy link
Contributor

vitaut commented May 30, 2022

You can either provide formatter specializations or use format_as mentioned above.

@Inori
Copy link

Inori commented Jun 29, 2022

@vitaut hi, is there any way to turn on the implicit conversion from enum to underlying integer?
I'm working on a pretty big project and trying to convert old format code to {fmt}.
Manually write formatters or write many format_as would be a pain.

@vitaut
Copy link
Contributor

vitaut commented Jun 29, 2022

@Inori, no but you can define a single format_as for all enums in your namespace.

@Inori
Copy link

Inori commented Jun 30, 2022

@Inori, no but you can define a single format_as for all enums in your namespace.

Ok,thanks for the reply.

geissonator pushed a commit to openbmc/phosphor-debug-collector that referenced this issue Jun 8, 2023
gcc13 based build failed with below errors.
error: static assertion failed: Cannot format an argument.
  To make type T formattable provide a formatter<T> specialization:
  https://fmt.dev/latest/api.html#udt

Problem reported during enum type input is similar to
fmtlib/fmt#391
Fix is to static_cast the "enum" type to "underlying_type" in
fmt::format function.

Problem related file descriptor input is to static_cast enum
to "underlying_type".

Change-Id: I587e4abfb4e188d40a0e4bfbdd57e6da6a77616d
Signed-off-by: Jayanth Othayoth <[email protected]>
geissonator pushed a commit to openbmc/openpower-proc-control that referenced this issue Jun 8, 2023
gcc13 based build failed with below error.
  error: static assertion failed: Cannot format an argument.
  To make type T formattable provide a formatter<T> specialization:
  https://fmt.dev/latest/api.html#udt

Problem looks similar to fmtlib/fmt#391

Fix is to convert the "enum" type to underlying_type in fmt::format function.

Change-Id: I155fc854428492462dfec3b3818d08daa16e36bf
Signed-off-by: Jayanth Othayoth <[email protected]>
rfrandse pushed a commit to ibm-openbmc/phosphor-debug-collector that referenced this issue Aug 11, 2023
gcc13 based build failed with below errors.
error: static assertion failed: Cannot format an argument.
  To make type T formattable provide a formatter<T> specialization:
  https://fmt.dev/latest/api.html#udt

Problem reported during enum type input is similar to
fmtlib/fmt#391
Fix is to static_cast the "enum" type to "underlying_type" in
fmt::format function.

Problem related file descriptor input is to static_cast enum
to "underlying_type".

Change-Id: I587e4abfb4e188d40a0e4bfbdd57e6da6a77616d
Signed-off-by: Jayanth Othayoth <[email protected]>
rfrandse pushed a commit to ibm-openbmc/phosphor-debug-collector that referenced this issue Aug 11, 2023
gcc13 based build failed with below errors.
error: static assertion failed: Cannot format an argument.
  To make type T formattable provide a formatter<T> specialization:
  https://fmt.dev/latest/api.html#udt

Problem reported during enum type input is similar to
fmtlib/fmt#391
Fix is to static_cast the "enum" type to "underlying_type" in
fmt::format function.

Problem related file descriptor input is to static_cast enum
to "underlying_type".

Change-Id: I587e4abfb4e188d40a0e4bfbdd57e6da6a77616d
Signed-off-by: Jayanth Othayoth <[email protected]>
baibaichen added a commit to baibaichen/velox that referenced this issue Oct 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants