microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
variant.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
7// std-interop-file: opt-in formatter and view wrappers for std::variant.
8
12#include "../microfmt.hpp"
13#include <string_view>
14#include <type_traits>
15#include <variant>
16
17namespace microfmt {
18
19// -----------------------------------------------------------------------------
20// Non-Owning Variant View Wrapper
21// -----------------------------------------------------------------------------
27template <typename... Ts> class RELOCO_POINTER variant_view {
28public:
32 using variant_type = std::variant<Ts...>;
33
41 constexpr explicit variant_view(const variant_type &var RELOCO_LIFETIMEBOUND, microfmt::string_view prefix = "",
42 microfmt::string_view suffix = "", bool show_index = false) noexcept
43 : var_(var), prefix_(prefix), suffix_(suffix), show_index_(show_index) {}
44
45 constexpr explicit variant_view(variant_type &&, microfmt::string_view = "", microfmt::string_view = "",
46 bool = false) = delete;
47
48 constexpr explicit variant_view(const variant_type &&, microfmt::string_view = "", microfmt::string_view = "",
49 bool = false) = delete;
50
55 [[nodiscard]] constexpr const variant_type &get() const noexcept { return *var_; }
60 [[nodiscard]] constexpr microfmt::string_view prefix() const noexcept { return prefix_; }
65 [[nodiscard]] constexpr microfmt::string_view suffix() const noexcept { return suffix_; }
70 [[nodiscard]] constexpr bool show_index() const noexcept { return show_index_; }
71
72private:
74 value_ref<const variant_type> var_;
76 microfmt::string_view prefix_;
78 microfmt::string_view suffix_;
80 bool show_index_;
81};
82
83// -----------------------------------------------------------------------------
84// View Factory Helpers (microfmt::as_variant)
85// -----------------------------------------------------------------------------
92template <typename... Ts>
93[[nodiscard]] constexpr auto as_variant(const std::variant<Ts...> &var RELOCO_LIFETIMEBOUND) noexcept {
94 return variant_view<Ts...>(var, "", "", false);
95}
96
97template <typename... Ts> [[nodiscard]] constexpr auto as_variant(std::variant<Ts...> &&) noexcept = delete;
98
99template <typename... Ts> [[nodiscard]] constexpr auto as_variant(const std::variant<Ts...> &&) noexcept = delete;
100
109template <typename... Ts>
110[[nodiscard]] constexpr auto as_variant(const std::variant<Ts...> &var RELOCO_LIFETIMEBOUND,
111 microfmt::string_view prefix, microfmt::string_view suffix) noexcept {
112 return variant_view<Ts...>(var, prefix, suffix, false);
113}
114
115template <typename... Ts>
116[[nodiscard]] constexpr auto as_variant(std::variant<Ts...> &&, microfmt::string_view,
117 microfmt::string_view) noexcept = delete;
118
119template <typename... Ts>
120[[nodiscard]] constexpr auto as_variant(const std::variant<Ts...> &&, microfmt::string_view,
121 microfmt::string_view) noexcept = delete;
122
130template <typename... Ts>
131[[nodiscard]] constexpr auto as_debug_variant(const std::variant<Ts...> &var RELOCO_LIFETIMEBOUND,
132 bool show_index = false) noexcept {
133 return variant_view<Ts...>(var, "variant", "", show_index);
134}
135
136template <typename... Ts>
137[[nodiscard]] constexpr auto as_debug_variant(std::variant<Ts...> &&, bool = false) noexcept = delete;
138
139template <typename... Ts>
140[[nodiscard]] constexpr auto as_debug_variant(const std::variant<Ts...> &&, bool = false) noexcept = delete;
141
142// -----------------------------------------------------------------------------
143// Formatter for variant_view targeting const microfmt::sink&
144// -----------------------------------------------------------------------------
153template <typename... Ts> struct formatter<variant_view<Ts...>> {
157 char spec_mode{'\0'};
161 microfmt::string_view elem_spec{""};
162
167 constexpr void parse(format_parse_context &ctx) noexcept {
168 auto spec = ctx.spec();
169 if (!spec.empty() && (spec.front() == '?' || spec.front() == 'i' || spec.front() == '#')) {
170 spec_mode = spec.front();
171 spec.remove_prefix(1);
172 }
173 elem_spec = spec;
174 }
175
181 void format(const variant_view<Ts...> &view, const sink &out) const noexcept {
182 const auto &var = view.get();
183
184 if (var.valueless_by_exception()) {
185 out.write("valueless");
186 return;
187 }
188
189 bool print_parens = false;
190 if (spec_mode == '?' || (view.prefix() == "variant" && !view.show_index() && spec_mode != 'i')) {
191 out.write("variant(");
192 print_parens = true;
193 } else if (spec_mode == 'i' || spec_mode == '#' || view.show_index()) {
194 microfmt::format_to(out, "variant[{}]", var.index());
195 out.write("(");
196 print_parens = true;
197 } else if (!view.prefix().empty()) {
198 out.write(view.prefix());
199 }
200
201 std::visit(
202 [this, &out](const auto &val) {
203 using ValueType = std::decay_t<decltype(val)>;
205 format_parse_context elem_ctx(elem_spec);
206 element_fmt.parse(elem_ctx);
207 element_fmt.format(val, out);
208 },
209 var);
210
211 if (print_parens) {
212 out.write(")");
213 } else if (!view.suffix().empty()) {
214 out.write(view.suffix());
215 }
216 }
217};
218
219// -----------------------------------------------------------------------------
220// Formatter for std::variant<Ts...>
221// -----------------------------------------------------------------------------
227template <typename... Ts> struct formatter<std::variant<Ts...>> {
231 formatter<variant_view<Ts...>> view_formatter_{};
232
237 constexpr void parse(format_parse_context &ctx) noexcept { view_formatter_.parse(ctx); }
238
244 void format(const std::variant<Ts...> &var, const sink &out) const noexcept {
245 view_formatter_.format(as_variant(var), out);
246 }
247};
248
252template <> struct formatter<std::monostate> {
256 constexpr void parse(format_parse_context &) noexcept {}
257
262 void format(std::monostate, const sink &out) const noexcept { out.write("null"); }
263};
264
265} // namespace microfmt
Definition microfmt.hpp:881
Non-owning view over a std::variant with optional decorations.
Definition variant.hpp:27
constexpr const variant_type & get() const noexcept
Returns the referenced variant.
Definition variant.hpp:55
constexpr variant_view(const variant_type &&, microfmt::string_view="", microfmt::string_view="", bool=false)=delete
std::variant< Ts... > variant_type
The wrapped std::variant type.
Definition variant.hpp:32
constexpr microfmt::string_view suffix() const noexcept
Returns the suffix decoration.
Definition variant.hpp:65
constexpr microfmt::string_view prefix() const noexcept
Returns the prefix decoration.
Definition variant.hpp:60
constexpr bool show_index() const noexcept
Reports whether the alternative index is displayed.
Definition variant.hpp:70
constexpr variant_view(const variant_type &var RELOCO_LIFETIMEBOUND, microfmt::string_view prefix="", microfmt::string_view suffix="", bool show_index=false) noexcept
Constructs the view.
Definition variant.hpp:41
constexpr variant_view(variant_type &&, microfmt::string_view="", microfmt::string_view="", bool=false)=delete
Definition assert.hpp:19
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
RELOCO_CONSTEXPR20 void format_to(const sink &out, microfmt::string_view fmt, const Args &...args) noexcept
Definition microfmt.hpp:1766
constexpr auto as_debug_variant(const std::variant< Ts... > &var RELOCO_LIFETIMEBOUND, bool show_index=false) noexcept
Wraps a variant for debug output (variant(index, ...)).
Definition variant.hpp:131
constexpr auto as_variant(const std::variant< Ts... > &var RELOCO_LIFETIMEBOUND) noexcept
Wraps a variant for plain formatting.
Definition variant.hpp:93
Drop-in replacement for a formatter<T> underlying_formatter; member used by container/wrapper formatt...
Definition microfmt.hpp:1433
constexpr void parse(format_parse_context &) noexcept
No-op parse; monostate accepts no format specifier.
Definition variant.hpp:256
void format(std::monostate, const sink &out) const noexcept
Writes the literal null.
Definition variant.hpp:262
constexpr void parse(format_parse_context &ctx) noexcept
Parses the specifier for the wrapped view formatter.
Definition variant.hpp:237
void format(const std::variant< Ts... > &var, const sink &out) const noexcept
Renders the variant's active alternative.
Definition variant.hpp:244
void format(const variant_view< Ts... > &view, const sink &out) const noexcept
Renders the active alternative with the configured decorations.
Definition variant.hpp:181
constexpr void parse(format_parse_context &ctx) noexcept
Parses the leading mode and the element specifier.
Definition variant.hpp:167
Definition microfmt.hpp:985