Lagrange
Loading...
Searching...
No Matches
join.h
1/*
2 * Copyright 2026 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
27
28// fmt is reachable whenever spdlog isn't running in pure-std::format mode.
29#if !defined(SPDLOG_USE_STD_FORMAT)
30// clang-format off
31 #include <lagrange/utils/warnoff.h>
32 #include <spdlog/fmt/fmt.h>
33 #include <lagrange/utils/warnon.h>
34// clang-format on
35#endif
36
37// std::format is available when explicitly selected for spdlog or when the standard library
38// exposes the C++20 header.
39#if defined(SPDLOG_USE_STD_FORMAT) || defined(__cpp_lib_format)
40 #include <format>
41#endif
42
43#include <iterator>
44#include <string_view>
45#include <tuple>
46#include <type_traits>
47#include <utility>
48
49namespace lagrange {
50
52namespace fmt_detail {
53
55template <typename R>
56using range_value_t =
57 std::remove_cv_t<std::remove_reference_t<decltype(*std::begin(std::declval<const R&>()))>>;
58
63template <typename Range>
64struct range_join_view
65{
66 const Range* range;
67 std::string_view sep;
68};
69
71template <typename Tuple>
72struct tuple_join_view
73{
74 const Tuple* tuple;
75 std::string_view sep;
76};
77
80template <typename Range, typename FormatContext, typename Func>
81auto format_range(const range_join_view<Range>& jv, FormatContext& ctx, Func&& format_elem)
82{
83 auto out = ctx.out();
84 bool first = true;
85 for (const auto& elem : *jv.range) {
86 if (!first) {
87 for (auto c : jv.sep) *out++ = c;
88 ctx.advance_to(out);
89 }
90 out = format_elem(elem, ctx);
91 first = false;
92 }
93 return out;
94}
95
96template <typename Tuple, typename FormatContext, typename Func>
97auto format_tuple(const tuple_join_view<Tuple>& jv, FormatContext& ctx, Func&& format_elem)
98{
99 auto out = ctx.out();
100 bool first = true;
101 std::apply(
102 [&](const auto&... elems) {
103 (
104 [&](const auto& elem) {
105 if (!first) {
106 for (auto c : jv.sep) *out++ = c;
107 ctx.advance_to(out);
108 }
109 out = format_elem(elem, ctx);
110 first = false;
111 }(elems),
112 ...);
113 },
114 *jv.tuple);
115 return out;
116}
117
118} // namespace fmt_detail
120
126template <typename Range>
127auto join(const Range& r, std::string_view sep)
128{
129 return fmt_detail::range_join_view<Range>{&r, sep};
130}
131
133template <typename... Ts>
134auto join(const std::tuple<Ts...>& t, std::string_view sep)
135{
136 return fmt_detail::tuple_join_view<std::tuple<Ts...>>{&t, sep};
137}
138
140template <typename T, typename U>
141auto join(const std::pair<T, U>& p, std::string_view sep)
142{
143 return fmt_detail::tuple_join_view<std::pair<T, U>>{&p, sep};
144}
145
146} // namespace lagrange
147
148// std::formatter specializations. Required when std::format is selected at the call site, which
149// can happen via ADL even if `lagrange::format` resolves to `fmt::format` — `fmt::v12::join_view`
150// template arguments pull `std::` into the candidate set. Delegates the format spec to the
151// element formatter so that e.g. `format("{:.3g}", join(vec, ", "))` formats each float with
152// `.3g` precision.
153#if defined(SPDLOG_USE_STD_FORMAT) || defined(__cpp_lib_format)
154
155template <typename Range>
156struct std::formatter<lagrange::fmt_detail::range_join_view<Range>, char>
157{
158 using value_type = lagrange::fmt_detail::range_value_t<Range>;
159 std::formatter<value_type, char> m_elem;
160
161 constexpr auto parse(std::format_parse_context& ctx) { return m_elem.parse(ctx); }
162
163 auto format(const lagrange::fmt_detail::range_join_view<Range>& jv, std::format_context& ctx)
164 const
165 {
166 return lagrange::fmt_detail::format_range(
167 jv,
168 ctx,
169 [this](const auto& elem, std::format_context& c) { return m_elem.format(elem, c); });
170 }
171};
172
174template <typename Tuple>
175struct std::formatter<lagrange::fmt_detail::tuple_join_view<Tuple>, char>
176{
177 constexpr auto parse(std::format_parse_context& ctx)
178 {
179 if (ctx.begin() != ctx.end() && *ctx.begin() != '}') {
180 throw std::format_error("format spec not supported for tuple/pair join");
181 }
182 return ctx.begin();
183 }
184
185 auto format(const lagrange::fmt_detail::tuple_join_view<Tuple>& jv, std::format_context& ctx)
186 const
187 {
188 return lagrange::fmt_detail::format_tuple(jv, ctx, [](const auto& elem, auto& c) {
189 return std::format_to(c.out(), "{}", elem);
190 });
191 }
192};
193
194#endif // defined(SPDLOG_USE_STD_FORMAT) || defined(__cpp_lib_format)
195
196// fmt::formatter specializations — required for callers routing through {fmt} (e.g. spdlog
197// logger calls when SPDLOG_USE_STD_FORMAT is not set, or when `lagrange::format` resolves to
198// `fmt::format`).
199#if !defined(SPDLOG_USE_STD_FORMAT)
200
201template <typename Range>
202struct fmt::formatter<lagrange::fmt_detail::range_join_view<Range>, char>
203{
204 using value_type = lagrange::fmt_detail::range_value_t<Range>;
205 fmt::formatter<value_type, char> m_elem;
206
207 template <typename ParseContext>
208 constexpr auto parse(ParseContext& ctx)
209 {
210 return m_elem.parse(ctx);
211 }
212
213 template <typename FormatContext>
214 auto format(const lagrange::fmt_detail::range_join_view<Range>& jv, FormatContext& ctx) const
215 {
216 return lagrange::fmt_detail::format_range(
217 jv,
218 ctx,
219 [this](const auto& elem, FormatContext& c) { return m_elem.format(elem, c); });
220 }
221};
222
223template <typename Tuple>
224struct fmt::formatter<lagrange::fmt_detail::tuple_join_view<Tuple>, char>
225{
226 template <typename ParseContext>
227 constexpr auto parse(ParseContext& ctx)
228 {
229 if (ctx.begin() != ctx.end() && *ctx.begin() != '}') {
230 throw fmt::format_error("format spec not supported for tuple/pair join");
231 }
232 return ctx.begin();
233 }
234
235 template <typename FormatContext>
236 auto format(const lagrange::fmt_detail::tuple_join_view<Tuple>& jv, FormatContext& ctx) const
237 {
238 return lagrange::fmt_detail::format_tuple(jv, ctx, [](const auto& elem, auto& c) {
239 return fmt::format_to(c.out(), "{}", elem);
240 });
241 }
242};
243
244#endif // !defined(SPDLOG_USE_STD_FORMAT)
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition range.h:176
Main namespace for Lagrange.
auto join(const Range &r, std::string_view sep)
Join all elements of r into a formattable view separated by sep.
Definition join.h:127