Skip to content

Commit 275fe47

Browse files
committed
feat!: use C++20 for std::format
1 parent f3a559d commit 275fe47

File tree

9 files changed

+329
-288
lines changed

9 files changed

+329
-288
lines changed

include/optree/exceptions.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ limitations under the License.
1818
#pragma once
1919

2020
#include <cstddef> // std::size_t
21+
#include <format> // std::format
2122
#include <source_location> // std::source_location
22-
#include <sstream> // std::ostringstream
2323
#include <stdexcept> // std::logic_error
2424
#include <string> // std::string, std::char_traits
2525
#include <string_view> // std::string_view
@@ -48,14 +48,14 @@ class InternalError : public std::logic_error {
4848
explicit InternalError(
4949
const std::string_view &message,
5050
const std::source_location &source_location = std::source_location::current())
51-
: std::logic_error{[&message, &source_location]() -> std::string {
52-
std::ostringstream oss{};
53-
oss << message << " (in function `" << source_location.function_name() << "` at file "
54-
<< RelpathFromProjectRoot(source_location) << ":" << source_location.line() << ":"
55-
<< source_location.column() << ")\n\n"
56-
<< "Please file a bug report at https://github.com/metaopt/optree/issues.";
57-
return oss.str();
58-
}()} {}
51+
: std::logic_error{
52+
std::format("{} (in function `{}` at file {}:{}:{})\n\n"
53+
"Please file a bug report at https://github.com/metaopt/optree/issues.",
54+
message,
55+
source_location.function_name(),
56+
RelpathFromProjectRoot(source_location),
57+
source_location.line(),
58+
source_location.column())} {}
5959
};
6060

6161
#define VA_FUNC2_(__0, __1, NAME, ...) NAME

include/optree/pytypes.h

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ limitations under the License.
1818
#pragma once
1919

2020
#include <exception> // std::rethrow_exception, std::current_exception
21+
#include <format> // std::format, std::format_to, std::formatter
2122
#include <string> // std::string
2223
#include <type_traits> // std::is_base_of_v
2324
#include <unordered_map> // std::unordered_map
@@ -49,6 +50,29 @@ inline Py_ALWAYS_INLINE std::string PyRepr(const std::string &string) {
4950
return static_cast<std::string>(py::repr(py::str(string)));
5051
}
5152

53+
template <typename CharT>
54+
struct std::formatter<py::handle, CharT> {
55+
template <class ParseContext>
56+
constexpr ParseContext::iterator parse(const ParseContext &context) {
57+
return context.begin();
58+
}
59+
template <class FormatContext>
60+
FormatContext::iterator format(const py::handle &object, FormatContext &context) const {
61+
return std::format_to(context.out(), "{}", PyRepr(object));
62+
}
63+
};
64+
template <typename CharT>
65+
struct std::formatter<py::object, CharT> {
66+
template <class ParseContext>
67+
constexpr ParseContext::iterator parse(const ParseContext &context) {
68+
return context.begin();
69+
}
70+
template <class FormatContext>
71+
FormatContext::iterator format(const py::object &object, FormatContext &context) const {
72+
return std::format_to(context.out(), "{}", PyRepr(object));
73+
}
74+
};
75+
5276
// The maximum size of the type cache.
5377
constexpr py::ssize_t MAX_TYPE_CACHE_SIZE = 4096;
5478

@@ -173,31 +197,31 @@ inline Py_ALWAYS_INLINE void DictSetItem(const py::handle &dict,
173197

174198
inline Py_ALWAYS_INLINE void AssertExactList(const py::handle &object) {
175199
if (!PyList_CheckExact(object.ptr())) [[unlikely]] {
176-
throw py::value_error("Expected an instance of list, got " + PyRepr(object) + ".");
200+
throw py::value_error(std::format("Expected an instance of list, got {}.", object));
177201
}
178202
}
179203
inline Py_ALWAYS_INLINE void AssertExactTuple(const py::handle &object) {
180204
if (!PyTuple_CheckExact(object.ptr())) [[unlikely]] {
181-
throw py::value_error("Expected an instance of tuple, got " + PyRepr(object) + ".");
205+
throw py::value_error(std::format("Expected an instance of tuple, got {}.", object));
182206
}
183207
}
184208
inline Py_ALWAYS_INLINE void AssertExactDict(const py::handle &object) {
185209
if (!PyDict_CheckExact(object.ptr())) [[unlikely]] {
186-
throw py::value_error("Expected an instance of dict, got " + PyRepr(object) + ".");
210+
throw py::value_error(std::format("Expected an instance of dict, got {}.", object));
187211
}
188212
}
189213

190214
inline Py_ALWAYS_INLINE void AssertExactOrderedDict(const py::handle &object) {
191215
if (!py::type::handle_of(object).is(PyOrderedDictTypeObject)) [[unlikely]] {
192-
throw py::value_error("Expected an instance of collections.OrderedDict, got " +
193-
PyRepr(object) + ".");
216+
throw py::value_error(
217+
std::format("Expected an instance of collections.OrderedDict, got {}.", object));
194218
}
195219
}
196220

197221
inline Py_ALWAYS_INLINE void AssertExactDefaultDict(const py::handle &object) {
198222
if (!py::type::handle_of(object).is(PyDefaultDictTypeObject)) [[unlikely]] {
199-
throw py::value_error("Expected an instance of collections.defaultdict, got " +
200-
PyRepr(object) + ".");
223+
throw py::value_error(
224+
std::format("Expected an instance of collections.defaultdict, got {}.", object));
201225
}
202226
}
203227

@@ -206,16 +230,16 @@ inline Py_ALWAYS_INLINE void AssertExactStandardDict(const py::handle &object) {
206230
py::type::handle_of(object).is(PyOrderedDictTypeObject) ||
207231
py::type::handle_of(object).is(PyDefaultDictTypeObject))) [[unlikely]] {
208232
throw py::value_error(
209-
"Expected an instance of dict, collections.OrderedDict, or collections.defaultdict, "
210-
"got " +
211-
PyRepr(object) + ".");
233+
std::format("Expected an instance of dict, collections.OrderedDict, "
234+
"or collections.defaultdict, got {}.",
235+
object));
212236
}
213237
}
214238

215239
inline Py_ALWAYS_INLINE void AssertExactDeque(const py::handle &object) {
216240
if (!py::type::handle_of(object).is(PyDequeTypeObject)) [[unlikely]] {
217-
throw py::value_error("Expected an instance of collections.deque, got " + PyRepr(object) +
218-
".");
241+
throw py::value_error(
242+
std::format("Expected an instance of collections.deque, got {}.", object));
219243
}
220244
}
221245

@@ -298,23 +322,24 @@ inline Py_ALWAYS_INLINE bool IsNamedTuple(const py::handle &object) {
298322
}
299323
inline Py_ALWAYS_INLINE void AssertExactNamedTuple(const py::handle &object) {
300324
if (!IsNamedTupleInstance(object)) [[unlikely]] {
301-
throw py::value_error("Expected an instance of collections.namedtuple, got " +
302-
PyRepr(object) + ".");
325+
throw py::value_error(
326+
std::format("Expected an instance of collections.namedtuple, got {}.", object));
303327
}
304328
}
305329
inline py::tuple NamedTupleGetFields(const py::handle &object) {
306330
py::handle type;
307331
if (PyType_Check(object.ptr())) [[unlikely]] {
308332
type = object;
309333
if (!IsNamedTupleClass(type)) [[unlikely]] {
310-
throw py::type_error("Expected a collections.namedtuple type, got " + PyRepr(object) +
311-
".");
334+
throw py::type_error(
335+
std::format("Expected a collections.namedtuple type, got {}.", object));
312336
}
313337
} else [[likely]] {
314338
type = py::type::handle_of(object);
315339
if (!IsNamedTupleClass(type)) [[unlikely]] {
316-
throw py::type_error("Expected an instance of collections.namedtuple type, got " +
317-
PyRepr(object) + ".");
340+
throw py::type_error(
341+
std::format("Expected an instance of collections.namedtuple type, got {}.",
342+
object));
318343
}
319344
}
320345
return EVALUATE_WITH_LOCK_HELD(py::getattr(type, Py_Get_ID(_fields)), type);
@@ -401,8 +426,8 @@ inline Py_ALWAYS_INLINE bool IsStructSequence(const py::handle &object) {
401426
}
402427
inline Py_ALWAYS_INLINE void AssertExactStructSequence(const py::handle &object) {
403428
if (!IsStructSequenceInstance(object)) [[unlikely]] {
404-
throw py::value_error("Expected an instance of PyStructSequence type, got " +
405-
PyRepr(object) + ".");
429+
throw py::value_error(
430+
std::format("Expected an instance of PyStructSequence type, got {}.", object));
406431
}
407432
}
408433
inline py::tuple StructSequenceGetFieldsImpl(const py::handle &type) {
@@ -439,13 +464,13 @@ inline py::tuple StructSequenceGetFields(const py::handle &object) {
439464
if (PyType_Check(object.ptr())) [[unlikely]] {
440465
type = object;
441466
if (!IsStructSequenceClass(type)) [[unlikely]] {
442-
throw py::type_error("Expected a PyStructSequence type, got " + PyRepr(object) + ".");
467+
throw py::type_error(std::format("Expected a PyStructSequence type, got {}.", object));
443468
}
444469
} else [[likely]] {
445470
type = py::type::handle_of(object);
446471
if (!IsStructSequenceClass(type)) [[unlikely]] {
447-
throw py::type_error("Expected an instance of PyStructSequence type, got " +
448-
PyRepr(object) + ".");
472+
throw py::type_error(
473+
std::format("Expected an instance of PyStructSequence type, got {}.", object));
449474
}
450475
}
451476

@@ -496,8 +521,9 @@ inline void TotalOrderSort(py::list &list) { // NOLINT[runtime/references]
496521
const auto sort_key_fn = py::cpp_function([](const py::object &obj) -> py::tuple {
497522
const py::handle cls = py::type::handle_of(obj);
498523
const py::str qualname{EVALUATE_WITH_LOCK_HELD(
499-
PyStr(py::getattr(cls, Py_Get_ID(__module__))) + "." +
500-
PyStr(py::getattr(cls, Py_Get_ID(__qualname__))),
524+
std::format("{}.{}",
525+
PyStr(py::getattr(cls, Py_Get_ID(__module__))),
526+
PyStr(py::getattr(cls, Py_Get_ID(__qualname__)))),
501527
cls)};
502528
return py::make_tuple(qualname, obj);
503529
});

src/optree.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717

1818
#include "optree/optree.h"
1919

20+
#include <format> // std::format
2021
#include <functional> // std::{not_,}equal_to, std::less{,_equal}, std::greater{,_equal}
2122
#include <memory> // std::unique_ptr
2223
#include <optional> // std::optional, std::nullopt
@@ -48,8 +49,8 @@ void BuildModule(py::module_ &mod) { // NOLINT[runtime/references]
4849

4950
GetCxxModule(mod);
5051

51-
mod.doc() = "Optimized PyTree Utilities. (C extension module built from " +
52-
std::string(RelpathFromProjectRoot()) + ")";
52+
mod.doc() = std::format("Optimized PyTree Utilities. (C extension module built from {})",
53+
RelpathFromProjectRoot());
5354
mod.attr("Py_TPFLAGS_BASETYPE") = py::int_(Py_TPFLAGS_BASETYPE);
5455

5556
// NOLINTNEXTLINE[bugprone-macro-parentheses]

src/registry.cpp

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ limitations under the License.
1515
================================================================================
1616
*/
1717

18+
#include <format> // std::format
1819
#include <memory> // std::make_shared
1920
#include <sstream> // std::ostringstream
2021
#include <string> // std::string
@@ -42,8 +43,9 @@ template <bool NoneIsLeaf>
4243
registration->type = py::reinterpret_borrow<py::object>(cls);
4344
EXPECT_TRUE(
4445
registry.m_registrations.emplace(cls, std::move(registration)).second,
45-
"PyTree type " + PyRepr(cls) +
46-
" is already registered in the global namespace.");
46+
std::format("PyTree type {} is already registered "
47+
"in the global namespace.",
48+
cls));
4749
if (sm_builtins_types.emplace(cls).second) [[likely]] {
4850
cls.inc_ref();
4951
}
@@ -72,8 +74,8 @@ template <bool NoneIsLeaf>
7274
const py::object &path_entry_type,
7375
const std::string &registry_namespace) {
7476
if (sm_builtins_types.find(cls) != sm_builtins_types.end()) [[unlikely]] {
75-
throw py::value_error("PyTree type " + PyRepr(cls) +
76-
" is a built-in type and cannot be re-registered.");
77+
throw py::value_error(
78+
std::format("PyTree type {} is a built-in type and cannot be re-registered.", cls));
7779
}
7880

7981
PyTreeTypeRegistry * const registry = Singleton<NoneIsLeaf>();
@@ -85,55 +87,56 @@ template <bool NoneIsLeaf>
8587
registration->path_entry_type = py::reinterpret_borrow<py::object>(path_entry_type);
8688
if (registry_namespace.empty()) [[unlikely]] {
8789
if (!registry->m_registrations.emplace(cls, std::move(registration)).second) [[unlikely]] {
88-
throw py::value_error("PyTree type " + PyRepr(cls) +
89-
" is already registered in the global namespace.");
90+
throw py::value_error(
91+
std::format("PyTree type {} is already registered in the global namespace.", cls));
9092
}
9193
if (IsStructSequenceClass(cls)) [[unlikely]] {
9294
PyErr_WarnEx(PyExc_UserWarning,
93-
("PyTree type " + PyRepr(cls) +
94-
" is a class of `PyStructSequence`, "
95-
"which is already registered in the global namespace. "
96-
"Override it with custom flatten/unflatten functions.")
95+
std::format("PyTree type {} is a class of `PyStructSequence`, "
96+
"which is already registered in the global namespace. "
97+
"Override it with custom flatten/unflatten functions.",
98+
cls)
9799
.c_str(),
98100
/*stack_level=*/2);
99101
} else if (IsNamedTupleClass(cls)) [[unlikely]] {
100102
PyErr_WarnEx(PyExc_UserWarning,
101-
("PyTree type " + PyRepr(cls) +
102-
" is a subclass of `collections.namedtuple`, "
103-
"which is already registered in the global namespace. "
104-
"Override it with custom flatten/unflatten functions.")
103+
std::format("PyTree type {} is a subclass of `collections.namedtuple`, "
104+
"which is already registered in the global namespace. "
105+
"Override it with custom flatten/unflatten functions.",
106+
cls)
105107
.c_str(),
106108
/*stack_level=*/2);
107109
}
108110
} else [[likely]] {
109111
if (!registry->m_named_registrations
110112
.emplace(std::make_pair(registry_namespace, cls), std::move(registration))
111113
.second) [[unlikely]] {
112-
std::ostringstream oss{};
113-
oss << "PyTree type " << PyRepr(cls) << " is already registered in namespace "
114-
<< PyRepr(registry_namespace) << ".";
115-
throw py::value_error(oss.str());
114+
throw py::value_error(
115+
std::format("PyTree type {} is already registered in namespace {}.",
116+
cls,
117+
PyRepr(registry_namespace)));
116118
}
117119
if (IsStructSequenceClass(cls)) [[unlikely]] {
118-
std::ostringstream oss{};
119-
oss << "PyTree type " << PyRepr(cls)
120-
<< " is a class of `PyStructSequence`, "
121-
"which is already registered in the global namespace. "
122-
"Override it with custom flatten/unflatten functions in namespace "
123-
<< PyRepr(registry_namespace) << ".";
124-
PyErr_WarnEx(PyExc_UserWarning,
125-
oss.str().c_str(),
126-
/*stack_level=*/2);
120+
PyErr_WarnEx(
121+
PyExc_UserWarning,
122+
123+
std::format("PyTree type {} is a class of `PyStructSequence`, "
124+
"which is already registered in the global namespace. "
125+
"Override it with custom flatten/unflatten functions in namespace {}.",
126+
cls,
127+
PyRepr(registry_namespace))
128+
.c_str(),
129+
/*stack_level=*/2);
127130
} else if (IsNamedTupleClass(cls)) [[unlikely]] {
128-
std::ostringstream oss{};
129-
oss << "PyTree type " << PyRepr(cls)
130-
<< " is a subclass of `collections.namedtuple`, "
131-
"which is already registered in the global namespace. "
132-
"Override it with custom flatten/unflatten functions in namespace "
133-
<< PyRepr(registry_namespace) << ".";
134-
PyErr_WarnEx(PyExc_UserWarning,
135-
oss.str().c_str(),
136-
/*stack_level=*/2);
131+
PyErr_WarnEx(
132+
PyExc_UserWarning,
133+
std::format("PyTree type {} is a subclass of `collections.namedtuple`, "
134+
"which is already registered in the global namespace. "
135+
"Override it with custom flatten/unflatten functions in namespace {}.",
136+
cls,
137+
PyRepr(registry_namespace))
138+
.c_str(),
139+
/*stack_level=*/2);
137140
}
138141
}
139142
}
@@ -166,8 +169,8 @@ template <bool NoneIsLeaf>
166169
const py::object &cls,
167170
const std::string &registry_namespace) {
168171
if (sm_builtins_types.find(cls) != sm_builtins_types.end()) [[unlikely]] {
169-
throw py::value_error("PyTree type " + PyRepr(cls) +
170-
" is a built-in type and cannot be unregistered.");
172+
throw py::value_error(
173+
std::format("PyTree type {} is a built-in type and cannot be unregistered.", cls));
171174
}
172175

173176
PyTreeTypeRegistry * const registry = Singleton<NoneIsLeaf>();

0 commit comments

Comments
 (0)