-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
fmt.h
183 lines (152 loc) · 7.87 KB
/
fmt.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#pragma once
#include <string>
#include <string_view>
#include <fmt/format.h>
// This file contains the essentials of fmt support in Drake, mainly
// compatibility code to inter-operate with different versions of fmt.
namespace drake {
#if FMT_VERSION >= 80000 || defined(DRAKE_DOXYGEN_CXX)
/** When using fmt >= 8, this is an alias for
<a href="https://fmt.dev/latest/api.html#compile-time-format-string-checks">fmt::runtime</a>.
When using fmt < 8, this is a no-op. */
inline auto fmt_runtime(std::string_view s) {
return fmt::runtime(s);
}
/** When using fmt >= 8, this is defined to be `const` to indicate that the
`fmt::formatter<T>::format(...)` function should be object-const.
When using fmt < 8, the function signature was incorrect (lacking the const),
so this macro will be empty. */
#define DRAKE_FMT8_CONST const
#else // FMT_VERSION
inline auto fmt_runtime(std::string_view s) {
return s;
}
#define DRAKE_FMT8_CONST
#endif // FMT_VERSION
/** Returns `fmt::to_string(x)` but always with at least one digit after the
decimal point. Different versions of fmt disagree on whether to omit the
trailing ".0" when formatting integer-valued floating-point numbers. */
template <typename T>
std::string fmt_floating_point(T x) {
std::string result = fmt::format("{:#}", x);
if (result.back() == '.') {
result.push_back('0');
}
return result;
}
/** Returns `fmt::("{:?}", x)`, i.e, using fmt's "debug string format"; see
the fmt.dev docs for the '?' presentation type for details. We provide this
wrapper because not all of our supported platforms have a new-enough fmt
to rely on it. On platforms with older fmt, we use a Drake re-implementation
of the feature that does NOT handle unicode correctly. */
std::string fmt_debug_string(std::string_view x);
namespace internal::formatter_as {
/* The DRAKE_FORMATTER_AS macro specializes this for it's format_as types.
This struct is a functor that performs a conversion. See below for details.
@tparam T is the TYPE to be formatted. */
template <typename T>
struct Converter;
/* Provides convenient type shortcuts for the TYPE in DRAKE_FORMATTER_AS. */
template <typename T>
struct Traits {
/* The type being formatted. */
using InputType = T;
/* The format_as functor that provides the alternative object to format. */
using Functor = Converter<T>;
/* The type of the alternative object. */
using OutputType = decltype(Functor::call(std::declval<InputType>()));
/* The fmt::formatter<> for the alternative object. */
using OutputTypeFormatter = fmt::formatter<OutputType>;
};
/* Implements fmt::formatter<TYPE> for DRAKE_FORMATER_AS types. The macro
specializes this for it's format_as types. See below for details.
@tparam T is the TYPE to be formatted. */
template <typename T>
struct Formatter;
} // namespace internal::formatter_as
} // namespace drake
/** Adds a `fmt::formatter<NAMESPACE::TYPE>` template specialization that
formats the `TYPE` by delegating the formatting using a transformed expression,
as if a conversion function like this were interposed during formatting:
@code{cpp}
template <TEMPLATE_ARGS>
auto format_as(const NAMESPACE::TYPE& ARG) {
return EXPR;
}
@endcode
For example, this declaration ...
@code{cpp}
DRAKE_FORMATTER_AS(, my_namespace, MyType, x, x.to_string())
@endcode
... changes this code ...
@code{cpp}
MyType foo;
fmt::print(foo);
@endcode
... to be equivalent to ...
@code{cpp}
MyType foo;
fmt::print(foo.to_string());
@endcode
... allowing user to format `my_namespace::MyType` objects without manually
adding the `to_string` call every time.
This provides a convenient mechanism to add formatters for classes that already
have a `to_string` function, or classes that are just thin wrappers over simple
types (ints, enums, etc.).
Always use this macro in the global namespace, and do not use a semicolon (`;`)
afterward.
@param TEMPLATE_ARGS The optional first argument `TEMPLATE_ARGS` can be used in
case the `TYPE` is templated, e.g., it might commonly be set to `typename T`. It
should be left empty when the TYPE is not templated. In case `TYPE` has multiple
template arguments, note that macros will fight with commas so you should use
`typename... Ts` instead of writing them all out.
@param NAMESPACE The namespace that encloses the `TYPE` being formatted. Cannot
be empty. For nested namespaces, use intemediate colons, e.g., `%drake::common`.
Do not place _leading_ colons on the `NAMESPACE`.
@param TYPE The class name (or struct name, or enum name, etc.) being formatted.
Do not place _leading_ double-colons on the `TYPE`. If the type is templated,
use the template arguments here, e.g., `MyOptional<T>` if `TEMPLATE_ARGS` was
chosen as `typename T`.
@param ARG A placeholder variable name to use for the value (i.e., object)
being formatted within the `EXPR` expression.
@param EXPR An expression to `return` from the format_as function; it can
refer to the given `ARG` name which will be of type `const TYPE& ARG`.
@note In future versions of fmt (perhaps fmt >= 10) there might be an ADL
`format_as` customization point with this feature built-in. If so, then we can
update this macro to use that spelling, and eventually deprecate the macro once
Drake drops support for earlier version of fmt. */
#define DRAKE_FORMATTER_AS(TEMPLATE_ARGS, NAMESPACE, TYPE, ARG, EXPR) \
/* Specializes the Converter<> class template for our TYPE. */ \
namespace drake::internal::formatter_as { \
template <TEMPLATE_ARGS> \
struct Converter<NAMESPACE::TYPE> { \
using InputType = NAMESPACE::TYPE; \
static auto call(const InputType& ARG) { return EXPR; } \
}; \
\
/* Provides the fmt::formatter<TYPE> implementation. */ \
template <TEMPLATE_ARGS> \
struct Formatter<NAMESPACE::TYPE> \
: fmt::formatter<typename Traits<NAMESPACE::TYPE>::OutputType> { \
using MyTraits = Traits<NAMESPACE::TYPE>; \
/* Shadow our base class member function template of the same name. */ \
template <typename FormatContext> \
auto format(const typename MyTraits::InputType& x, \
FormatContext& ctx) DRAKE_FMT8_CONST { \
/* Call the base class member function after laundering the object */ \
/* through the user's provided format_as function. Older versions of */ \
/* fmt have const-correctness bugs, which we can fix with some good */ \
/* old fashioned const_cast-ing here. */ \
using Base = typename MyTraits::OutputTypeFormatter; \
const Base* const self = this; \
return const_cast<Base*>(self)->format(MyTraits::Functor::call(x), ctx); \
} \
}; \
} /* namespace drake::internal::formatter_as */ \
\
/* Specializes the fmt::formatter<> class template for TYPE. */ \
namespace fmt { \
template <TEMPLATE_ARGS> \
struct formatter<NAMESPACE::TYPE> \
: drake::internal::formatter_as::Formatter<NAMESPACE::TYPE> {}; \
} /* namespace fmt */