libpqxx
The C++ client library for PostgreSQL
separated_list.hxx
1 /* Helper similar to Python's `str.join()`.
2  *
3  * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/separated_list instead.
4  *
5  * Copyright (c) 2000-2025, Jeroen T. Vermeulen.
6  *
7  * See COPYING for copyright license. If you did not receive a file called
8  * COPYING with this source code, please notify the distributor of this
9  * mistake, or contact the author.
10  */
11 #ifndef PQXX_H_SEPARATED_LIST
12 #define PQXX_H_SEPARATED_LIST
13 
14 #if !defined(PQXX_HEADER_PRE)
15 # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
16 #endif
17 
18 #include <algorithm>
19 #include <numeric>
20 
21 #include "pqxx/strconv.hxx"
22 
23 // C++20: Simplify using std::ranges::range.
24 // C++20: Optimise buffer allocation using random_access_range/iterator.
25 // C++23: Use std::join_with().
26 // TODO: Or just use std formatting?
27 // TODO: Can we pass separators at compile time?
28 namespace pqxx
29 {
34 
36 
44 template<typename ITER, typename ACCESS>
45 [[nodiscard]] inline std::string
46 separated_list(std::string_view sep, ITER begin, ITER end, ACCESS access)
47 {
48  if (end == begin)
49  return {};
50  auto next{begin};
51  ++next;
52  if (next == end)
53  return to_string(access(begin));
54 
55  // From here on, we've got at least 2 elements -- meaning that we need sep.
56  using elt_type = strip_t<decltype(access(begin))>;
57  using traits = string_traits<elt_type>;
58 
59  std::size_t budget{0};
60  for (ITER cnt{begin}; cnt != end; ++cnt)
61  budget += traits::size_buffer(access(cnt));
62  budget +=
63  static_cast<std::size_t>(std::distance(begin, end)) * std::size(sep);
64 
65  std::string result;
66  result.resize(budget);
67 
68  char *const data{result.data()};
69  char *here{data};
70  char *stop{data + budget};
71  here = traits::into_buf(here, stop, access(begin)) - 1;
72  for (++begin; begin != end; ++begin)
73  {
74  here += sep.copy(here, std::size(sep));
75  here = traits::into_buf(here, stop, access(begin)) - 1;
76  }
77  result.resize(static_cast<std::size_t>(here - data));
78  return result;
79 }
80 
81 
83 template<typename ITER>
84 [[nodiscard]] inline std::string
85 separated_list(std::string_view sep, ITER begin, ITER end)
86 {
87  return separated_list(sep, begin, end, [](ITER i) { return *i; });
88 }
89 
90 
91 // C++20: Use a concept.
93 template<typename CONTAINER>
94 [[nodiscard]] inline auto
95 separated_list(std::string_view sep, CONTAINER const &c)
96  /*
97  Always std::string; necessary because SFINAE doesn't work with the
98  contents of function bodies, so the check for iterability has to be in
99  the signature.
100  */
101  -> typename std::enable_if<
102  (not std::is_void<decltype(std::begin(c))>::value and
103  not std::is_void<decltype(std::end(c))>::value),
104  std::string>::type
105 {
106  return separated_list(sep, std::begin(c), std::end(c));
107 }
108 
109 
111 template<
112  typename TUPLE, std::size_t INDEX = 0, typename ACCESS,
113  typename std::enable_if<
114  (INDEX == std::tuple_size<TUPLE>::value - 1), int>::type = 0>
115 [[nodiscard]] inline std::string separated_list(
116  std::string_view /* sep */, TUPLE const &t, ACCESS const &access)
117 {
118  return to_string(access(&std::get<INDEX>(t)));
119 }
120 
121 template<
122  typename TUPLE, std::size_t INDEX = 0, typename ACCESS,
123  typename std::enable_if<
124  (INDEX < std::tuple_size<TUPLE>::value - 1), int>::type = 0>
125 [[nodiscard]] inline std::string
126 separated_list(std::string_view sep, TUPLE const &t, ACCESS const &access)
127 {
128  std::string out{to_string(access(&std::get<INDEX>(t)))};
129  out.append(sep);
130  out.append(separated_list<TUPLE, INDEX + 1>(sep, t, access));
131  return out;
132 }
133 
134 template<
135  typename TUPLE, std::size_t INDEX = 0,
136  typename std::enable_if<
137  (INDEX <= std::tuple_size<TUPLE>::value), int>::type = 0>
138 [[nodiscard]] inline std::string
139 separated_list(std::string_view sep, TUPLE const &t)
140 {
141  // TODO: Optimise allocation.
142  return separated_list(sep, t, [](TUPLE const &tup) { return *tup; });
143 }
145 } // namespace pqxx
146 #endif
std::string separated_list(std::string_view sep, ITER begin, ITER end, ACCESS access)
Represent sequence of values as a string, joined by a given separator.
Definition: separated_list.hxx:46
std::size_t size_buffer(TYPE const &...value) noexcept
Estimate how much buffer space is needed to represent values as a string.
Definition: strconv.hxx:526
Result set containing data returned by a query or command.
Definition: result.hxx:91
PQXX_LIBEXPORT std::string to_string(field const &value)
Convert a field to a string.
The home of all libpqxx classes, functions, templates, etc.
Definition: array.cxx:26
Traits class for use in string conversions.
Definition: strconv.hxx:153
std::remove_cv_t< std::remove_reference_t< TYPE >> strip_t
Remove any constness, volatile, and reference-ness from a type.
Definition: types.hxx:80