-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstrvecjoin.hpp
More file actions
129 lines (112 loc) · 4.62 KB
/
Copy pathstrvecjoin.hpp
File metadata and controls
129 lines (112 loc) · 4.62 KB
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
// Join all lines in a string vector with a delimiter, zero-copy.
//
// Header-only file.
//
// Copyright (C) 2026, Martin Young <martin_young@live.cn>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//------------------------------------------------------------------------
#pragma once
#include <version>
#include <ostream>
#include <string>
#include <string_view>
#include <vector>
#include <ranges>
#include <concepts>
#include <type_traits>
#include <utility>
//------------------------------------------------------------------------
// Lazy evaluation proxy for range views.
// Bridges ranges with std::ostream and std::string seamlessly without
// unnecessary memory allocations.
//------------------------------------------------------------------------
template <std::ranges::range V>
struct lazy_str_proxy {
V view;
// Scenario A: Zero-copy direct stream output (e.g., std::cerr << proxy)
// Uses a 1KB stack buffer to prevent system call overhead (write storms)
// associated with unbuffered streams like std::cerr.
friend std::ostream& operator<<(std::ostream& os, const lazy_str_proxy& proxy) {
// By-value copy bypasses const-iterator limitations in C++20 views.
// Copying a view is guaranteed to be an O(1) operation.
auto mutable_view = proxy.view;
char buf[1024];
size_t i = 0;
for (char c : mutable_view) {
buf[i++] = c;
if (i == sizeof(buf)) {
os.write(buf, static_cast<std::streamsize>(i));
i = 0;
}
}
if (i > 0) os.write(buf, static_cast<std::streamsize>(i));
return os;
}
// Scenario B: Implicit conversion to std::string
// Entity generation happens only upon explicit demand.
operator std::string() const {
auto mutable_view = view;
std::string s;
for (char c : mutable_view) s.push_back(c);
return s;
}
// Scenario C: Support operator+ for concatenation
// Explicit friend overloads prevent template argument deduction failures.
friend std::string operator+(const lazy_str_proxy& lhs, const std::string& rhs) {
return static_cast<std::string>(lhs) + rhs;
}
friend std::string operator+(const std::string& lhs, const lazy_str_proxy& rhs) {
return lhs + static_cast<std::string>(rhs);
}
friend std::string operator+(const lazy_str_proxy& lhs, const char* rhs) {
return static_cast<std::string>(lhs) + rhs;
}
friend std::string operator+(const char* lhs, const lazy_str_proxy& rhs) {
return lhs + static_cast<std::string>(rhs);
}
};
// Helper function to deduce template types automatically
template <typename V>
inline auto make_lazy_str_proxy(V&& view) {
return lazy_str_proxy<std::remove_cvref_t<V>>{std::forward<V>(view)};
}
//------------------------------------------------------------------------
// strvecjoin: Join vector of strings using a delimiter lazily
//------------------------------------------------------------------------
#ifdef __cpp_lib_ranges_join_with
template <typename T>
concept strvecjoin_Deli =
std::same_as<std::remove_cvref_t<T>, char> ||
std::convertible_to<T, std::string_view>;
template <strvecjoin_Deli T>
inline auto strvecjoin(const std::vector<std::string>& lines, const T& delimiter=" ") {
if constexpr (std::same_as<std::remove_cvref_t<T>, char>) {
return make_lazy_str_proxy(lines | std::views::join_with(delimiter));
} else {
return make_lazy_str_proxy(lines | std::views::join_with(std::string_view{delimiter}));
}
}
#else
// C++20 fallback using compositional ranges
inline auto strvecjoin(const std::vector<std::string>& lines, std::string_view delim=" ") {
const size_t n = lines.size();
auto view = std::views::iota(size_t{0}, n > 0 ? 2 * n - 1 : size_t{0})
| std::views::transform([&lines, delim](size_t i) -> std::string_view {
return (i % 2 == 0) ? std::string_view(lines[i / 2]) : delim;
})
| std::views::join;
return make_lazy_str_proxy(std::move(view));
}
#endif