libpqxx 7.7.5
util.hxx
1/* Various utility definitions for libpqxx.
2 *
3 * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/util instead.
4 *
5 * Copyright (c) 2000-2023, 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_UTIL
12#define PQXX_H_UTIL
13
14#if !defined(PQXX_HEADER_PRE)
15# error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
16#endif
17
18#include <cassert>
19#include <cctype>
20#include <cstdio>
21#include <functional>
22#include <iterator>
23#include <limits>
24#include <memory>
25#include <stdexcept>
26#include <string>
27#include <string_view>
28#include <type_traits>
29#include <typeinfo>
30#include <utility>
31#include <vector>
32
33#if __has_include(<version>)
34# include <version>
35#endif
36
37#include "pqxx/except.hxx"
38#include "pqxx/internal/encodings.hxx"
39#include "pqxx/types.hxx"
40#include "pqxx/version.hxx"
41
42
44namespace pqxx
45{}
46
47#include <pqxx/internal/libpq-forward.hxx>
48
49
50// C++23: Retire wrapper.
51#if defined(PQXX_HAVE_UNREACHABLE)
53# define PQXX_UNREACHABLE std::unreachable()
54#else
55# define PQXX_UNREACHABLE assert(false)
56#endif
57
58
60namespace pqxx::internal
61{
62
63// C++20: Retire wrapper.
65template<typename LEFT, typename RIGHT>
66inline constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept
67{
68#if defined(PQXX_HAVE_CMP)
69 return std::cmp_less(lhs, rhs);
70#else
71 // We need a variable just because lgtm.com gives off a false positive
72 // warning when we compare the values directly. It considers that a
73 // "self-comparison."
74 constexpr bool left_signed{std::is_signed_v<LEFT>};
75 if constexpr (left_signed == std::is_signed_v<RIGHT>)
76 return lhs < rhs;
77 else if constexpr (std::is_signed_v<LEFT>)
78 return (lhs <= 0) ? true : (std::make_unsigned_t<LEFT>(lhs) < rhs);
79 else
80 return (rhs <= 0) ? false : (lhs < std::make_unsigned_t<RIGHT>(rhs));
81#endif
82}
83
84
85// C++20: Retire wrapper.
87template<typename LEFT, typename RIGHT>
88inline constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
89{
90#if defined(PQXX_HAVE_CMP)
91 return std::cmp_greater(lhs, rhs);
92#else
93 return cmp_less(rhs, lhs);
94#endif
95}
96
97
98// C++20: Retire wrapper.
100template<typename LEFT, typename RIGHT>
101inline constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept
102{
103#if defined(PQXX_HAVE_CMP)
104 return std::cmp_less_equal(lhs, rhs);
105#else
106 return not cmp_less(rhs, lhs);
107#endif
108}
109
110
111// C++20: Retire wrapper.
113template<typename LEFT, typename RIGHT>
114inline constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept
115{
116#if defined(PQXX_HAVE_CMP)
117 return std::cmp_greater_equal(lhs, rhs);
118#else
119 return not cmp_less(lhs, rhs);
120#endif
121}
122
123
125
128[[nodiscard]] inline std::string cat2(std::string_view x, std::string_view y)
129{
130 std::string buf;
131 auto const xs{std::size(x)}, ys{std::size(y)};
132 buf.resize(xs + ys);
133 x.copy(std::data(buf), xs);
134 y.copy(std::data(buf) + xs, ys);
135 return buf;
136}
137} // namespace pqxx::internal
138
139
140namespace pqxx
141{
142using namespace std::literals;
143
145template<typename... T> inline constexpr void ignore_unused(T &&...) noexcept
146{}
147
148
150
153template<typename TO, typename FROM>
154inline TO check_cast(FROM value, std::string_view description)
155{
156 static_assert(std::is_arithmetic_v<FROM>);
157 static_assert(std::is_arithmetic_v<TO>);
158 static_assert(std::is_integral_v<FROM> == std::is_integral_v<TO>);
159
160 // The rest of this code won't quite work for bool, but bool is trivially
161 // convertible to other arithmetic types as far as I can see.
162 if constexpr (std::is_same_v<FROM, bool>)
163 return static_cast<TO>(value);
164
165 // Depending on our "if constexpr" conditions, this parameter may not be
166 // needed. Some compilers will warn.
167 ignore_unused(description);
168
169 using from_limits = std::numeric_limits<decltype(value)>;
170 using to_limits = std::numeric_limits<TO>;
171 if constexpr (std::is_signed_v<FROM>)
172 {
173 if constexpr (std::is_signed_v<TO>)
174 {
175 if (value < to_limits::lowest())
176 throw range_error{internal::cat2("Cast underflow: "sv, description)};
177 }
178 else
179 {
180 // FROM is signed, but TO is not. Treat this as a special case, because
181 // there may not be a good broader type in which the compiler can even
182 // perform our check.
183 if (value < 0)
185 "Casting negative value to unsigned type: "sv, description)};
186 }
187 }
188 else
189 {
190 // No need to check: the value is unsigned so can't fall below the range
191 // of the TO type.
192 }
193
194 if constexpr (std::is_integral_v<FROM>)
195 {
196 using unsigned_from = std::make_unsigned_t<FROM>;
197 using unsigned_to = std::make_unsigned_t<TO>;
198 constexpr auto from_max{static_cast<unsigned_from>((from_limits::max)())};
199 constexpr auto to_max{static_cast<unsigned_to>((to_limits::max)())};
200 if constexpr (from_max > to_max)
201 {
202 if (internal::cmp_greater(value, to_max))
203 throw range_error{internal::cat2("Cast overflow: "sv, description)};
204 }
205 }
206 else if constexpr ((from_limits::max)() > (to_limits::max)())
207 {
208 if (value > (to_limits::max)())
209 throw range_error{internal::cat2("Cast overflow: ", description)};
210 }
211
212 return static_cast<TO>(value);
213}
214
215
237inline PQXX_PRIVATE void check_version() noexcept
238{
239 // There is no particular reason to do this here in @ref connection, except
240 // to ensure that every meaningful libpqxx client will execute it. The call
241 // must be in the execution path somewhere or the compiler won't try to link
242 // it. We can't use it to initialise a global or class-static variable,
243 // because a smart compiler might resolve it at compile time.
244 //
245 // On the other hand, we don't want to make a useless function call too
246 // often for performance reasons. A local static variable is initialised
247 // only on the definition's first execution. Compilers will be well
248 // optimised for this behaviour, so there's a minimal one-time cost.
249 static auto const version_ok{internal::PQXX_VERSION_CHECK()};
250 ignore_unused(version_ok);
251}
252
253
255
257struct PQXX_LIBEXPORT thread_safety_model
258{
260 bool safe_libpq = false;
261
263
269 bool safe_kerberos = false;
270
272 std::string description;
273};
274
275
277[[nodiscard]] PQXX_LIBEXPORT thread_safety_model describe_thread_safety();
278
279
280#if defined(PQXX_HAVE_CONCEPTS)
281# define PQXX_POTENTIAL_BINARY_ARG pqxx::potential_binary
282#else
283# define PQXX_POTENTIAL_BINARY_ARG typename
284#endif
285
286
288
305template<PQXX_POTENTIAL_BINARY_ARG TYPE>
306std::basic_string_view<std::byte> binary_cast(TYPE const &data)
307{
308 static_assert(sizeof(value_type<TYPE>) == 1);
309 return {
310 reinterpret_cast<std::byte const *>(
311 const_cast<strip_t<decltype(*std::data(data))> const *>(
312 std::data(data))),
313 std::size(data)};
314}
315
316
317#if defined(PQXX_HAVE_CONCEPTS)
318template<typename CHAR>
319concept char_sized = (sizeof(CHAR) == 1);
320# define PQXX_CHAR_SIZED_ARG char_sized
321#else
322# define PQXX_CHAR_SIZED_ARG typename
323#endif
324
326
333template<PQXX_CHAR_SIZED_ARG CHAR, typename SIZE>
334std::basic_string_view<std::byte> binary_cast(CHAR const *data, SIZE size)
335{
336 static_assert(sizeof(CHAR) == 1);
337 return {
338 reinterpret_cast<std::byte const *>(data),
339 check_cast<std::size_t>(size, "binary data size")};
340}
341
342
344constexpr oid oid_none{0};
345} // namespace pqxx
346
347
349
358namespace pqxx::internal
359{
360using namespace std::literals;
361
362
364
368template<typename CHAR> inline constexpr bool is_digit(CHAR c) noexcept
369{
370 return (c >= '0') and (c <= '9');
371}
372
373
375
377[[nodiscard]] std::string
378describe_object(std::string_view class_name, std::string_view name);
379
380
382
394 void const *old_guest, std::string_view old_class, std::string_view old_name,
395 void const *new_guest, std::string_view new_class,
396 std::string_view new_name);
397
398
400
404 void const *old_guest, std::string_view old_class, std::string_view old_name,
405 void const *new_guest, std::string_view new_class,
406 std::string_view new_name);
407
408
410
413inline constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
414{
415 return 2 + (2 * binary_bytes) + 1;
416}
417
418
420
422inline constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
423{
424 return (escaped_bytes - 2) / 2;
425}
426
427
428// TODO: Use actual binary type for "data".
430
435void PQXX_LIBEXPORT
436esc_bin(std::basic_string_view<std::byte> binary_data, char buffer[]) noexcept;
437
438
440std::string PQXX_LIBEXPORT
441esc_bin(std::basic_string_view<std::byte> binary_data);
442
443
445void PQXX_LIBEXPORT
446unesc_bin(std::string_view escaped_data, std::byte buffer[]);
447
448
450std::basic_string<std::byte>
451 PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data);
452
453
455template<typename T> auto ssize(T const &c)
456{
457#if defined(__cpp_lib_ssize) && __cplusplus >= __cpp_lib_ssize
458 return std::ssize(c);
459#else
460 using signed_t = std::make_signed_t<decltype(std::size(c))>;
461 return static_cast<signed_t>(std::size(c));
462#endif // __cpp_lib_ssize
463}
464
465
467
471template<typename RETURN, typename... ARGS>
472std::tuple<ARGS...> args_f(RETURN (&func)(ARGS...));
473
474
476
480template<typename RETURN, typename... ARGS>
481std::tuple<ARGS...> args_f(std::function<RETURN(ARGS...)> const &);
482
483
485
489template<typename CLASS, typename RETURN, typename... ARGS>
490std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...));
491
492
494
498template<typename CLASS, typename RETURN, typename... ARGS>
499std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...) const);
500
501
503
509template<typename CALLABLE>
510auto args_f(CALLABLE const &f)
511 -> decltype(member_args_f(&CALLABLE::operator()));
512
513
515template<typename CALLABLE>
516using args_t = decltype(args_f(std::declval<CALLABLE>()));
517
518
520
523template<typename... TYPES>
524std::tuple<strip_t<TYPES>...> strip_types(std::tuple<TYPES...> const &);
525
526
528template<typename... TYPES>
529using strip_types_t = decltype(strip_types(std::declval<TYPES...>()));
530} // namespace pqxx::internal
531#endif
The home of all libpqxx classes, functions, templates, etc.
Definition array.hxx:27
std::basic_string_view< std::byte > binary_cast(TYPE const &data)
Cast binary data to a type that libpqxx will recognise as binary.
Definition util.hxx:306
strip_t< decltype(*std::begin(std::declval< CONTAINER >()))> value_type
The type of a container's elements.
Definition types.hxx:107
void check_version() noexcept
Definition util.hxx:237
thread_safety_model describe_thread_safety()
Describe thread safety available in this build.
Definition util.cxx:33
std::remove_cv_t< std::remove_reference_t< TYPE > > strip_t
Remove any constness, volatile, and reference-ness from a type.
Definition types.hxx:91
constexpr void ignore_unused(T &&...) noexcept
Suppress compiler warning about an unused item.
Definition util.hxx:145
constexpr oid oid_none
The "null" oid.
Definition util.hxx:344
TO check_cast(FROM value, std::string_view description)
Cast a numeric value to another type, or throw if it underflows/overflows.
Definition util.hxx:154
Internal items for libpqxx' own use. Do not use these yourself.
Definition composite.hxx:83
void unesc_bin(std::string_view escaped_data, std::byte buffer[])
Reconstitute binary data from its escaped version.
Definition util.cxx:158
int PQXX_VERSION_CHECK() noexcept
Library version check stub.
Definition version.cxx:23
void esc_bin(std::basic_string_view< std::byte > binary_data, char buffer[]) noexcept
Hex-escape binary data into a buffer.
Definition util.cxx:126
constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
Compute buffer size needed to escape binary data for use as a BYTEA.
Definition util.hxx:413
decltype(args_f(std::declval< CALLABLE >())) args_t
A callable's parameter types, as a tuple.
Definition util.hxx:516
std::tuple< ARGS... > member_args_f(RETURN(CLASS::*)(ARGS...))
Helper for determining a member function's parameter types.
constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept
Same as std::cmp_less, or a workaround where that's not available.
Definition util.hxx:66
void check_unique_unregister(void const *old_guest, std::string_view old_class, std::string_view old_name, void const *new_guest, std::string_view new_class, std::string_view new_name)
Like check_unique_register, but for un-registering a guest.
Definition util.cxx:78
void check_unique_register(void const *old_guest, std::string_view old_class, std::string_view old_name, void const *new_guest, std::string_view new_class, std::string_view new_name)
Check validity of registering a new "guest" in a "host.".
Definition util.cxx:61
std::tuple< strip_t< TYPES >... > strip_types(std::tuple< TYPES... > const &)
Helper: Apply strip_t to each of a tuple type's component types.
std::string describe_object(std::string_view class_name, std::string_view name)
Describe an object for humans, based on class name and optional name.
Definition util.cxx:51
std::tuple< ARGS... > args_f(RETURN(&func)(ARGS...))
Helper for determining a function's parameter types.
constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater, or workaround if not available.
Definition util.hxx:88
constexpr bool is_digit(CHAR c) noexcept
A safer and more generic replacement for std::isdigit.
Definition util.hxx:368
decltype(strip_types(std::declval< TYPES... >())) strip_types_t
Take a tuple type and apply strip_t to its component types.
Definition util.hxx:529
constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater_equal, or workaround if not available.
Definition util.hxx:114
std::string cat2(std::string_view x, std::string_view y)
Efficiently concatenate two strings.
Definition util.hxx:128
constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_less_equal, or workaround if not available.
Definition util.hxx:101
auto ssize(T const &c)
Transitional: std::ssize(), or custom implementation if not available.
Definition util.hxx:455
constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
Compute binary size from the size of its escaped version.
Definition util.hxx:422
Something is out of range, similar to std::out_of_range.
Definition except.hxx:202
Descriptor of library's thread-safety model.
Definition util.hxx:258
std::string description
A human-readable description of any thread-safety issues.
Definition util.hxx:272