Botan 3.13.0
Crypto and TLS for C&
strong_type.h
Go to the documentation of this file.
1/**
2 * A wrapper class to implement strong types
3 * (C) 2022 Jack Lloyd
4 * 2022 René Meusel - Rohde & Schwarz Cybersecurity
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8
9#ifndef BOTAN_STRONG_TYPE_H_
10#define BOTAN_STRONG_TYPE_H_
11
12#include <botan/concepts.h>
13#include <iosfwd>
14#include <span>
15#include <string>
16
17namespace Botan {
18
19template <typename T, typename Tag, typename... Capabilities>
20class Strong;
21
22/**
23 * Trait that detects whether the given types are a Strong<> instantiation
24 */
25template <typename... Ts>
26struct is_strong_type : std::false_type {};
27
28/// @copydoc is_strong_type
29template <typename... Ts>
30struct is_strong_type<Strong<Ts...>> : std::true_type {};
31
32template <typename... Ts>
34
35namespace concepts {
36
37template <typename T>
38concept streamable = requires(std::ostream& os, T a) { os << a; };
39
40template <class T>
42
43template <class T>
45
46template <class T>
47concept integral_strong_type = strong_type<T> && std::integral<typename T::wrapped_type>;
48
49template <class T>
50concept unsigned_integral_strong_type = strong_type<T> && std::unsigned_integral<typename T::wrapped_type>;
51
52template <typename T, typename Capability>
53concept strong_type_with_capability = T::template has_capability<Capability>();
54
55} // namespace concepts
56
57/**
58 * Added as an additional "capability tag" to enable arithmetic operators with
59 * plain numbers for Strong<> types that wrap a number.
60 */
62
63namespace detail {
64
65/**
66 * Checks whether the @p CapabilityT is included in the @p Tags type pack.
67 */
68template <typename CapabilityT, typename... Tags>
69constexpr bool has_capability = (std::is_same_v<CapabilityT, Tags> || ...);
70
71/**
72 * Storage for the wrapped value of a strong type, and access to it via get()
73 */
74template <typename T>
76 private:
77 T m_value;
78
79 public:
80 /// The type wrapped by this strong type
81 using wrapped_type = T;
82
83 public:
84 /// Default constructor, value initializes the wrapped value
85 Strong_Base() = default;
86
87 /// Copy constructor
88 Strong_Base(const Strong_Base&) = default;
89
90 /// Move constructor
91 Strong_Base(Strong_Base&&) noexcept = default;
92
93 /// Copy assignment
94 /// @return reference to this
95 Strong_Base& operator=(const Strong_Base&) = default;
96
97 /// Move assignment
98 /// @return reference to this
99 Strong_Base& operator=(Strong_Base&&) noexcept = default;
100
101 ~Strong_Base() = default;
102
103 /// Wrap the given value
104 /// @param v the value to wrap
105 constexpr explicit Strong_Base(T v) : m_value(std::move(v)) {}
106
107 /// Access the wrapped value
108 /// @return reference to the wrapped value
109 constexpr T& get() & { return m_value; }
110
111 /// Access the wrapped value
112 /// @return const reference to the wrapped value
113 constexpr const T& get() const& { return m_value; }
114
115 /// Access the wrapped value
116 /// @return rvalue reference to the wrapped value
117 constexpr T&& get() && { return std::move(m_value); }
118
119 /// Access the wrapped value
120 /// @return const rvalue reference to the wrapped value
121 constexpr const T&& get() const&& { return std::move(m_value); }
122};
123
124/**
125 * Adds functionality to Strong_Base depending on the wrapped type
126 *
127 * The primary template adds nothing; the specializations below expose
128 * container and contiguous container operations where applicable.
129 */
130template <typename T>
131class Strong_Adapter : public Strong_Base<T> {
132 public:
134};
135
136template <std::integral T>
137class Strong_Adapter<T> : public Strong_Base<T> {
138 public:
140};
141
142/**
143 * Forwards the container interface of the wrapped type
144 */
145template <concepts::container T>
147 public:
148 /// The element type of the wrapped container
149 using value_type = typename T::value_type;
150
151 /// The size type of the wrapped container
152 using size_type = typename T::size_type;
153
154 /// The iterator type of the wrapped container
155 using iterator = typename T::iterator;
156
157 /// The const iterator type of the wrapped container
158 using const_iterator = typename T::const_iterator;
159
160 public:
162
163 /// Create a container holding the given number of default constructed elements
164 /// @param size the number of elements
168
169 /// Create a container from the elements of an iterator range
170 /// @param begin start of the range
171 /// @param end one past the end of the range
172 template <typename InputIt>
174
175 public:
176 /// Iterate the wrapped container
177 /// @return an iterator to the first element
178 decltype(auto) begin() noexcept(noexcept(this->get().begin())) { return this->get().begin(); }
179
180 /// Iterate the wrapped container
181 /// @return a const iterator to the first element
182 decltype(auto) begin() const noexcept(noexcept(this->get().begin())) { return this->get().begin(); }
183
184 /// Iterate the wrapped container
185 /// @return an iterator one past the last element
186 decltype(auto) end() noexcept(noexcept(this->get().end())) { return this->get().end(); }
187
188 /// Iterate the wrapped container
189 /// @return a const iterator one past the last element
190 decltype(auto) end() const noexcept(noexcept(this->get().end())) { return this->get().end(); }
191
192 /// Iterate the wrapped container
193 /// @return a const iterator to the first element
194 decltype(auto) cbegin() noexcept(noexcept(this->get().cbegin())) { return this->get().cbegin(); }
195
196 /// Iterate the wrapped container
197 /// @return a const iterator to the first element
198 decltype(auto) cbegin() const noexcept(noexcept(this->get().cbegin())) { return this->get().cbegin(); }
199
200 /// Iterate the wrapped container
201 /// @return a const iterator one past the last element
202 decltype(auto) cend() noexcept(noexcept(this->get().cend())) { return this->get().cend(); }
203
204 /// Iterate the wrapped container
205 /// @return a const iterator one past the last element
206 decltype(auto) cend() const noexcept(noexcept(this->get().cend())) { return this->get().cend(); }
207
208 /// Query the size of the wrapped container
209 /// @return the number of elements
210 size_type size() const noexcept(noexcept(this->get().size())) { return this->get().size(); }
211
212 /// Query whether the wrapped container is empty
213 /// @return true if the container holds no elements
214 bool empty() const noexcept(noexcept(this->get().empty()))
215 requires(concepts::has_empty<T>)
216 {
217 return this->get().empty();
218 }
219
220 /// Change the number of elements held
221 /// @param size the new number of elements
222 void resize(size_type size) noexcept(noexcept(this->get().resize(size)))
224 {
225 this->get().resize(size);
226 }
227
228 /// Preallocate storage for the given number of elements
229 /// @param size the number of elements to reserve capacity for
230 void reserve(size_type size) noexcept(noexcept(this->get().reserve(size)))
232 {
233 this->get().reserve(size);
234 }
235
236 /// Element access
237 /// @param i the index of the element
238 /// @return const reference to the element at index i
239 template <typename U>
240 decltype(auto) operator[](U&& i) const noexcept(noexcept(this->get().operator[](i))) {
241 return this->get()[std::forward<U>(i)];
242 }
243
244 /// Element access
245 /// @param i the index of the element
246 /// @return reference to the element at index i
247 template <typename U>
248 decltype(auto) operator[](U&& i) noexcept(noexcept(this->get().operator[](i))) {
249 return this->get()[std::forward<U>(i)];
250 }
251};
252
253template <concepts::container T>
254class Strong_Adapter<T> : public Container_Strong_Adapter_Base<T> {
255 public:
256 using Container_Strong_Adapter_Base<T>::Container_Strong_Adapter_Base;
257};
258
259template <concepts::contiguous_container T>
260class Strong_Adapter<T> : public Container_Strong_Adapter_Base<T> {
261 public:
262 using pointer = typename T::pointer;
263 using const_pointer = typename T::const_pointer;
264
265 public:
267
268 explicit Strong_Adapter(std::span<const typename Container_Strong_Adapter_Base<T>::value_type> span) :
269 Strong_Adapter(T(span.begin(), span.end())) {}
270
271 // Disambiguates the usage of string literals, otherwise:
272 // Strong_Adapter(std::span<>) and Strong_Adapter(const char*)
273 // would be ambiguous.
274 explicit Strong_Adapter(const char* str)
275 requires(std::same_as<T, std::string>)
276 : Strong_Adapter(std::string(str)) {}
277
278 public:
279 decltype(auto) data() noexcept(noexcept(this->get().data())) { return this->get().data(); }
280
281 decltype(auto) data() const noexcept(noexcept(this->get().data())) { return this->get().data(); }
282};
283
284} // namespace detail
285
286/**
287 * Strong types can be used as wrappers around common types to provide
288 * compile time semantics. They usually contribute to more maintainable and
289 * less error-prone code especially when dealing with function parameters.
290 *
291 * Internally, this provides adapters so that the wrapping strong type behaves
292 * as much as the underlying type as possible and desirable.
293 *
294 * This implementation was inspired by:
295 * https://stackoverflow.com/a/69030899
296 */
297template <typename T, typename TagTypeT, typename... Capabilities>
298class Strong final : public detail::Strong_Adapter<T> {
299 public:
301
302 /**
303 * Check whether this strong type was declared with the given capability tag
304 * @return true if CapabilityT is one of this type's Capabilities
305 */
306 template <typename CapabilityT>
307 constexpr static bool has_capability() {
308 return (std::is_same_v<CapabilityT, Capabilities> || ...);
309 }
310
311 private:
312 using Tag = TagTypeT;
313};
314
315/**
316 * @brief Generically unwraps a strong type to its underlying type.
317 *
318 * If the provided type is not a strong type, it is returned as is.
319 *
320 * @note This is meant as a helper for generic code that needs to deal with both
321 * wrapped strong types and bare objects. Use the ordinary `get()` method
322 * if you know that you are dealing with a strong type.
323 *
324 * @param t value to be unwrapped
325 * @return the unwrapped value
326 */
327template <typename T>
328[[nodiscard]] constexpr decltype(auto) unwrap_strong_type(T&& t) {
330 // If the parameter type isn't a strong type, return it as is.
331 return std::forward<T>(t);
332 } else {
333 // Unwrap the strong type and return the underlying value.
334 return std::forward<T>(t).get();
335 }
336}
337
338/**
339 * @brief Wraps a value into a caller-defined (strong) type.
340 *
341 * If the provided object @p t is already of type @p T, it is returned as is.
342 *
343 * @note This is meant as a helper for generic code that needs to deal with both
344 * wrapped strong types and bare objects. Use the ordinary constructor if
345 * you know that you are dealing with a bare value type.
346 *
347 * @param t value to be wrapped
348 * @return the wrapped value
349 */
350template <typename T, typename ParamT>
351 requires std::constructible_from<T, ParamT> ||
352 (concepts::strong_type<T> && std::constructible_from<typename T::wrapped_type, ParamT>)
353[[nodiscard]] constexpr decltype(auto) wrap_strong_type(ParamT&& t) {
354 if constexpr(std::same_as<std::remove_cvref_t<ParamT>, T>) {
355 // Noop, if the parameter type already is the desired return type.
356 return std::forward<ParamT>(t);
357 } else if constexpr(std::constructible_from<T, ParamT>) {
358 // Implicit conversion from the parameter type to the return type.
359 return T{std::forward<ParamT>(t)};
360 } else {
361 // Explicitly calling the wrapped type's constructor to support
362 // implicit conversions on types that mark their constructors as explicit.
363 static_assert(concepts::strong_type<T> && std::constructible_from<typename T::wrapped_type, ParamT>);
364 return T{typename T::wrapped_type{std::forward<ParamT>(t)}};
365 }
366}
367
368namespace detail {
369
370/**
371 * Resolves to the type wrapped by a strong type, or to T itself if T is
372 * not a strong type
373 */
374template <typename T>
376 /// The resolved type
377 using type = T;
378};
379
380/// @copydoc wrapped_type_helper
381template <concepts::strong_type T>
383 /// The resolved type
384 using type = typename T::wrapped_type;
385};
386
387} // namespace detail
388
389/**
390 * @brief Extracts the wrapped type from a strong type.
391 *
392 * If the provided type is not a strong type, it is returned as is.
393 *
394 * @note This is meant as a helper for generic code that needs to deal with both
395 * wrapped strong types and bare objects. Use the ordinary `::wrapped_type`
396 * declaration if you know that you are dealing with a strong type.
397 */
398template <typename T>
400
401/**
402 * Write the wrapped value to an output stream
403 * @param os the output stream
404 * @param v the strong type to write
405 * @return reference to the output stream
406 */
407template <typename T, typename... Tags>
409decltype(auto) operator<<(std::ostream& os, const Strong<T, Tags...>& v) {
410 return os << v.get();
411}
412
413/**
414 * Compare for equality
415 * @param lhs the first operand (strong type)
416 * @param rhs the second operand (strong type)
417 * @return true if lhs and rhs are equal
418 */
419template <typename T, typename... Tags>
420 requires(std::equality_comparable<T>)
421bool operator==(const Strong<T, Tags...>& lhs, const Strong<T, Tags...>& rhs) {
422 return lhs.get() == rhs.get();
423}
424
425/**
426 * Three-way comparison
427 * @param lhs the first operand (strong type)
428 * @param rhs the second operand (strong type)
429 * @return the ordering of lhs relative to rhs
430 */
431template <typename T, typename... Tags>
432 requires(std::three_way_comparable<T>)
434 return lhs.get() <=> rhs.get();
435}
436
437/**
438 * Three-way comparison
439 * @param a the first operand (plain number)
440 * @param b the second operand (strong type)
441 * @return the ordering of a relative to b
442 */
443template <std::integral T1, std::integral T2, typename... Tags>
445 return a <=> b.get();
446}
447
448/**
449 * Three-way comparison
450 * @param a the first operand (strong type)
451 * @param b the second operand (plain number)
452 * @return the ordering of a relative to b
453 */
454template <std::integral T1, std::integral T2, typename... Tags>
456 return a.get() <=> b;
457}
458
459/**
460 * Compare for equality
461 * @param a the first operand (plain number)
462 * @param b the second operand (strong type)
463 * @return true if a and b are equal
464 */
465template <std::integral T1, std::integral T2, typename... Tags>
467 return a == b.get();
468}
469
470/**
471 * Compare for equality
472 * @param a the first operand (strong type)
473 * @param b the second operand (plain number)
474 * @return true if a and b are equal
475 */
476template <std::integral T1, std::integral T2, typename... Tags>
478 return a.get() == b;
479}
480
481/**
482 * Add the wrapped values
483 * @param a the left hand operand (plain number)
484 * @param b the right hand operand (strong type)
485 * @return the result, wrapped in the strong type
486 */
487template <std::integral T1, std::integral T2, typename... Tags>
488 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
489constexpr decltype(auto) operator+(T1 a, Strong<T2, Tags...> b) {
490 return Strong<T2, Tags...>(a + b.get());
491}
492
493/**
494 * Add the wrapped values
495 * @param a the left hand operand (strong type)
496 * @param b the right hand operand (plain number)
497 * @return the result, wrapped in the strong type
498 */
499template <std::integral T1, std::integral T2, typename... Tags>
500 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
501constexpr decltype(auto) operator+(Strong<T1, Tags...> a, T2 b) {
502 return Strong<T1, Tags...>(a.get() + b);
503}
504
505/**
506 * Add the wrapped values
507 * @param a the left hand operand (strong type)
508 * @param b the right hand operand (strong type)
509 * @return the result, wrapped in the strong type
510 */
511template <std::integral T, typename... Tags>
512constexpr decltype(auto) operator+(Strong<T, Tags...> a, Strong<T, Tags...> b) {
513 return Strong<T, Tags...>(a.get() + b.get());
514}
515
516/**
517 * Subtract the wrapped values
518 * @param a the left hand operand (plain number)
519 * @param b the right hand operand (strong type)
520 * @return the result, wrapped in the strong type
521 */
522template <std::integral T1, std::integral T2, typename... Tags>
523 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
524constexpr decltype(auto) operator-(T1 a, Strong<T2, Tags...> b) {
525 return Strong<T2, Tags...>(a - b.get());
526}
527
528/**
529 * Subtract the wrapped values
530 * @param a the left hand operand (strong type)
531 * @param b the right hand operand (plain number)
532 * @return the result, wrapped in the strong type
533 */
534template <std::integral T1, std::integral T2, typename... Tags>
535 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
536constexpr decltype(auto) operator-(Strong<T1, Tags...> a, T2 b) {
537 return Strong<T1, Tags...>(a.get() - b);
538}
539
540/**
541 * Subtract the wrapped values
542 * @param a the left hand operand (strong type)
543 * @param b the right hand operand (strong type)
544 * @return the result, wrapped in the strong type
545 */
546template <std::integral T, typename... Tags>
547constexpr decltype(auto) operator-(Strong<T, Tags...> a, Strong<T, Tags...> b) {
548 return Strong<T, Tags...>(a.get() - b.get());
549}
550
551/**
552 * Multiply the wrapped values
553 * @param a the left hand operand (plain number)
554 * @param b the right hand operand (strong type)
555 * @return the result, wrapped in the strong type
556 */
557template <std::integral T1, std::integral T2, typename... Tags>
558 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
559constexpr decltype(auto) operator*(T1 a, Strong<T2, Tags...> b) {
560 return Strong<T2, Tags...>(a * b.get());
561}
562
563/**
564 * Multiply the wrapped values
565 * @param a the left hand operand (strong type)
566 * @param b the right hand operand (plain number)
567 * @return the result, wrapped in the strong type
568 */
569template <std::integral T1, std::integral T2, typename... Tags>
570 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
571constexpr decltype(auto) operator*(Strong<T1, Tags...> a, T2 b) {
572 return Strong<T1, Tags...>(a.get() * b);
573}
574
575/**
576 * Multiply the wrapped values
577 * @param a the left hand operand (strong type)
578 * @param b the right hand operand (strong type)
579 * @return the result, wrapped in the strong type
580 */
581template <std::integral T, typename... Tags>
582constexpr decltype(auto) operator*(Strong<T, Tags...> a, Strong<T, Tags...> b) {
583 return Strong<T, Tags...>(a.get() * b.get());
584}
585
586/**
587 * Divide the wrapped values
588 * @param a the left hand operand (plain number)
589 * @param b the right hand operand (strong type)
590 * @return the result, wrapped in the strong type
591 */
592template <std::integral T1, std::integral T2, typename... Tags>
593 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
594constexpr decltype(auto) operator/(T1 a, Strong<T2, Tags...> b) {
595 return Strong<T2, Tags...>(a / b.get());
596}
597
598/**
599 * Divide the wrapped values
600 * @param a the left hand operand (strong type)
601 * @param b the right hand operand (plain number)
602 * @return the result, wrapped in the strong type
603 */
604template <std::integral T1, std::integral T2, typename... Tags>
605 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
606constexpr decltype(auto) operator/(Strong<T1, Tags...> a, T2 b) {
607 return Strong<T1, Tags...>(a.get() / b);
608}
609
610/**
611 * Divide the wrapped values
612 * @param a the left hand operand (strong type)
613 * @param b the right hand operand (strong type)
614 * @return the result, wrapped in the strong type
615 */
616template <std::integral T, typename... Tags>
617constexpr decltype(auto) operator/(Strong<T, Tags...> a, Strong<T, Tags...> b) {
618 return Strong<T, Tags...>(a.get() / b.get());
619}
620
621/**
622 * Bitwise XOR of the wrapped values
623 * @param a the left hand operand (plain number)
624 * @param b the right hand operand (strong type)
625 * @return the result, wrapped in the strong type
626 */
627template <std::integral T1, std::integral T2, typename... Tags>
628 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
629constexpr decltype(auto) operator^(T1 a, Strong<T2, Tags...> b) {
630 return Strong<T2, Tags...>(a ^ b.get());
631}
632
633/**
634 * Bitwise XOR of the wrapped values
635 * @param a the left hand operand (strong type)
636 * @param b the right hand operand (plain number)
637 * @return the result, wrapped in the strong type
638 */
639template <std::integral T1, std::integral T2, typename... Tags>
640 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
641constexpr decltype(auto) operator^(Strong<T1, Tags...> a, T2 b) {
642 return Strong<T1, Tags...>(a.get() ^ b);
643}
644
645/**
646 * Bitwise XOR of the wrapped values
647 * @param a the left hand operand (strong type)
648 * @param b the right hand operand (strong type)
649 * @return the result, wrapped in the strong type
650 */
651template <std::integral T, typename... Tags>
652constexpr decltype(auto) operator^(Strong<T, Tags...> a, Strong<T, Tags...> b) {
653 return Strong<T, Tags...>(a.get() ^ b.get());
654}
655
656/**
657 * Bitwise AND of the wrapped values
658 * @param a the left hand operand (plain number)
659 * @param b the right hand operand (strong type)
660 * @return the result, wrapped in the strong type
661 */
662template <std::integral T1, std::integral T2, typename... Tags>
663 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
664constexpr decltype(auto) operator&(T1 a, Strong<T2, Tags...> b) {
665 return Strong<T2, Tags...>(a & b.get());
666}
667
668/**
669 * Bitwise AND of the wrapped values
670 * @param a the left hand operand (strong type)
671 * @param b the right hand operand (plain number)
672 * @return the result, wrapped in the strong type
673 */
674template <std::integral T1, std::integral T2, typename... Tags>
675 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
676constexpr decltype(auto) operator&(Strong<T1, Tags...> a, T2 b) {
677 return Strong<T1, Tags...>(a.get() & b);
678}
679
680/**
681 * Bitwise AND of the wrapped values
682 * @param a the left hand operand (strong type)
683 * @param b the right hand operand (strong type)
684 * @return the result, wrapped in the strong type
685 */
686template <std::integral T, typename... Tags>
687constexpr decltype(auto) operator&(Strong<T, Tags...> a, Strong<T, Tags...> b) {
688 return Strong<T, Tags...>(a.get() & b.get());
689}
690
691/**
692 * Bitwise OR of the wrapped values
693 * @param a the left hand operand (plain number)
694 * @param b the right hand operand (strong type)
695 * @return the result, wrapped in the strong type
696 */
697template <std::integral T1, std::integral T2, typename... Tags>
698 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
699constexpr decltype(auto) operator|(T1 a, Strong<T2, Tags...> b) {
700 return Strong<T2, Tags...>(a | b.get());
701}
702
703/**
704 * Bitwise OR of the wrapped values
705 * @param a the left hand operand (strong type)
706 * @param b the right hand operand (plain number)
707 * @return the result, wrapped in the strong type
708 */
709template <std::integral T1, std::integral T2, typename... Tags>
710 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
711constexpr decltype(auto) operator|(Strong<T1, Tags...> a, T2 b) {
712 return Strong<T1, Tags...>(a.get() | b);
713}
714
715/**
716 * Bitwise OR of the wrapped values
717 * @param a the left hand operand (strong type)
718 * @param b the right hand operand (strong type)
719 * @return the result, wrapped in the strong type
720 */
721template <std::integral T, typename... Tags>
722constexpr decltype(auto) operator|(Strong<T, Tags...> a, Strong<T, Tags...> b) {
723 return Strong<T, Tags...>(a.get() | b.get());
724}
725
726/**
727 * Right shift the wrapped values
728 * @param a the left hand operand (plain number)
729 * @param b the right hand operand (strong type)
730 * @return the result, wrapped in the strong type
731 */
732template <std::integral T1, std::integral T2, typename... Tags>
733 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
734constexpr decltype(auto) operator>>(T1 a, Strong<T2, Tags...> b) {
735 return Strong<T2, Tags...>(a >> b.get());
736}
737
738/**
739 * Right shift the wrapped values
740 * @param a the left hand operand (strong type)
741 * @param b the right hand operand (plain number)
742 * @return the result, wrapped in the strong type
743 */
744template <std::integral T1, std::integral T2, typename... Tags>
745 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
746constexpr decltype(auto) operator>>(Strong<T1, Tags...> a, T2 b) {
747 return Strong<T1, Tags...>(a.get() >> b);
748}
749
750/**
751 * Right shift the wrapped values
752 * @param a the left hand operand (strong type)
753 * @param b the right hand operand (strong type)
754 * @return the result, wrapped in the strong type
755 */
756template <std::integral T, typename... Tags>
757constexpr decltype(auto) operator>>(Strong<T, Tags...> a, Strong<T, Tags...> b) {
758 return Strong<T, Tags...>(a.get() >> b.get());
759}
760
761/**
762 * Left shift the wrapped values
763 * @param a the left hand operand (plain number)
764 * @param b the right hand operand (strong type)
765 * @return the result, wrapped in the strong type
766 */
767template <std::integral T1, std::integral T2, typename... Tags>
768 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
769constexpr decltype(auto) operator<<(T1 a, Strong<T2, Tags...> b) {
770 return Strong<T2, Tags...>(a << b.get());
771}
772
773/**
774 * Left shift the wrapped values
775 * @param a the left hand operand (strong type)
776 * @param b the right hand operand (plain number)
777 * @return the result, wrapped in the strong type
778 */
779template <std::integral T1, std::integral T2, typename... Tags>
780 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
781constexpr decltype(auto) operator<<(Strong<T1, Tags...> a, T2 b) {
782 return Strong<T1, Tags...>(a.get() << b);
783}
784
785/**
786 * Left shift the wrapped values
787 * @param a the left hand operand (strong type)
788 * @param b the right hand operand (strong type)
789 * @return the result, wrapped in the strong type
790 */
791template <std::integral T, typename... Tags>
792constexpr decltype(auto) operator<<(Strong<T, Tags...> a, Strong<T, Tags...> b) {
793 return Strong<T, Tags...>(a.get() << b.get());
794}
795
796/**
797 * Add to the wrapped value
798 * @param a the strong type to modify
799 * @param b the right hand operand (plain number)
800 * @return reference to a
801 */
802template <std::integral T1, std::integral T2, typename... Tags>
803 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
804constexpr auto operator+=(Strong<T1, Tags...>& a, T2 b) {
805 a.get() += b;
806 return a;
807}
808
809/**
810 * Add to the wrapped value
811 * @param a the strong type to modify
812 * @param b the right hand operand (strong type)
813 * @return reference to a
814 */
815template <std::integral T, typename... Tags>
817 a.get() += b.get();
818 return a;
819}
820
821/**
822 * Subtract from the wrapped value
823 * @param a the strong type to modify
824 * @param b the right hand operand (plain number)
825 * @return reference to a
826 */
827template <std::integral T1, std::integral T2, typename... Tags>
828 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
829constexpr auto operator-=(Strong<T1, Tags...>& a, T2 b) {
830 a.get() -= b;
831 return a;
832}
833
834/**
835 * Subtract from the wrapped value
836 * @param a the strong type to modify
837 * @param b the right hand operand (strong type)
838 * @return reference to a
839 */
840template <std::integral T, typename... Tags>
842 a.get() -= b.get();
843 return a;
844}
845
846/**
847 * Multiply in place the wrapped value
848 * @param a the strong type to modify
849 * @param b the right hand operand (plain number)
850 * @return reference to a
851 */
852template <std::integral T1, std::integral T2, typename... Tags>
853 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
854constexpr auto operator*=(Strong<T1, Tags...>& a, T2 b) {
855 a.get() *= b;
856 return a;
857}
858
859/**
860 * Multiply in place the wrapped value
861 * @param a the strong type to modify
862 * @param b the right hand operand (strong type)
863 * @return reference to a
864 */
865template <std::integral T, typename... Tags>
867 a.get() *= b.get();
868 return a;
869}
870
871/**
872 * Divide in place the wrapped value
873 * @param a the strong type to modify
874 * @param b the right hand operand (plain number)
875 * @return reference to a
876 */
877template <std::integral T1, std::integral T2, typename... Tags>
878 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
879constexpr auto operator/=(Strong<T1, Tags...>& a, T2 b) {
880 a.get() /= b;
881 return a;
882}
883
884/**
885 * Divide in place the wrapped value
886 * @param a the strong type to modify
887 * @param b the right hand operand (strong type)
888 * @return reference to a
889 */
890template <std::integral T, typename... Tags>
892 a.get() /= b.get();
893 return a;
894}
895
896/**
897 * Bitwise XOR in place the wrapped value
898 * @param a the strong type to modify
899 * @param b the right hand operand (plain number)
900 * @return reference to a
901 */
902template <std::integral T1, std::integral T2, typename... Tags>
903 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
904constexpr auto operator^=(Strong<T1, Tags...>& a, T2 b) {
905 a.get() ^= b;
906 return a;
907}
908
909/**
910 * Bitwise XOR in place the wrapped value
911 * @param a the strong type to modify
912 * @param b the right hand operand (strong type)
913 * @return reference to a
914 */
915template <std::integral T, typename... Tags>
917 a.get() ^= b.get();
918 return a;
919}
920
921/**
922 * Bitwise AND in place the wrapped value
923 * @param a the strong type to modify
924 * @param b the right hand operand (plain number)
925 * @return reference to a
926 */
927template <std::integral T1, std::integral T2, typename... Tags>
928 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
929constexpr auto operator&=(Strong<T1, Tags...>& a, T2 b) {
930 a.get() &= b;
931 return a;
932}
933
934/**
935 * Bitwise AND in place the wrapped value
936 * @param a the strong type to modify
937 * @param b the right hand operand (strong type)
938 * @return reference to a
939 */
940template <std::integral T, typename... Tags>
942 a.get() &= b.get();
943 return a;
944}
945
946/**
947 * Bitwise OR in place the wrapped value
948 * @param a the strong type to modify
949 * @param b the right hand operand (plain number)
950 * @return reference to a
951 */
952template <std::integral T1, std::integral T2, typename... Tags>
953 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
954constexpr auto operator|=(Strong<T1, Tags...>& a, T2 b) {
955 a.get() |= b;
956 return a;
957}
958
959/**
960 * Bitwise OR in place the wrapped value
961 * @param a the strong type to modify
962 * @param b the right hand operand (strong type)
963 * @return reference to a
964 */
965template <std::integral T, typename... Tags>
967 a.get() |= b.get();
968 return a;
969}
970
971/**
972 * Right shift in place the wrapped value
973 * @param a the strong type to modify
974 * @param b the right hand operand (plain number)
975 * @return reference to a
976 */
977template <std::integral T1, std::integral T2, typename... Tags>
978 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
979constexpr auto operator>>=(Strong<T1, Tags...>& a, T2 b) {
980 a.get() >>= b;
981 return a;
982}
983
984/**
985 * Right shift in place the wrapped value
986 * @param a the strong type to modify
987 * @param b the right hand operand (strong type)
988 * @return reference to a
989 */
990template <std::integral T, typename... Tags>
992 a.get() >>= b.get();
993 return a;
994}
995
996/**
997 * Left shift in place the wrapped value
998 * @param a the strong type to modify
999 * @param b the right hand operand (plain number)
1000 * @return reference to a
1001 */
1002template <std::integral T1, std::integral T2, typename... Tags>
1003 requires(detail::has_capability<EnableArithmeticWithPlainNumber, Tags...>)
1004constexpr auto operator<<=(Strong<T1, Tags...>& a, T2 b) {
1005 a.get() <<= b;
1006 return a;
1007}
1008
1009/**
1010 * Left shift in place the wrapped value
1011 * @param a the strong type to modify
1012 * @param b the right hand operand (strong type)
1013 * @return reference to a
1014 */
1015template <std::integral T, typename... Tags>
1017 a.get() <<= b.get();
1018 return a;
1019}
1020
1021/**
1022 * Increment the wrapped value (postfix)
1023 * @param a the strong type to modify
1024 * @return the value before the operation
1025 */
1026template <std::integral T, typename... Tags>
1027constexpr auto operator++(Strong<T, Tags...>& a, int) {
1028 auto tmp = a;
1029 ++a.get();
1030 return tmp;
1031}
1032
1033/**
1034 * Increment the wrapped value (prefix)
1035 * @param a the strong type to modify
1036 * @return the value after the operation
1037 */
1038template <std::integral T, typename... Tags>
1039constexpr auto operator++(Strong<T, Tags...>& a) {
1040 ++a.get();
1041 return a;
1042}
1043
1044/**
1045 * Decrement the wrapped value (postfix)
1046 * @param a the strong type to modify
1047 * @return the value before the operation
1048 */
1049template <std::integral T, typename... Tags>
1050constexpr auto operator--(Strong<T, Tags...>& a, int) {
1051 auto tmp = a;
1052 --a.get();
1053 return tmp;
1054}
1055
1056/**
1057 * Decrement the wrapped value (prefix)
1058 * @param a the strong type to modify
1059 * @return the value after the operation
1060 */
1061template <std::integral T, typename... Tags>
1062constexpr auto operator--(Strong<T, Tags...>& a) {
1063 --a.get();
1064 return a;
1065}
1066
1067/**
1068 * This mimics a std::span but keeps track of the strong-type information. Use
1069 * this when you would want to use `const Strong<...>&` as a parameter
1070 * declaration. In particular this allows assigning strong-type information to
1071 * slices of a bigger buffer without copying the bytes. E.g:
1072 *
1073 * using Foo = Strong<std::vector<uint8_t>, Foo_>;
1074 *
1075 * void bar(StrongSpan<Foo> foo) { ... }
1076 *
1077 * std::vector<uint8_t> buffer;
1078 * BufferSlicer slicer(buffer);
1079 * bar(slicer.take<Foo>()); // This does not copy the data from buffer but
1080 * // just annotates the 'Foo' strong-type info.
1081 */
1082template <concepts::contiguous_strong_type T>
1083class StrongSpan final {
1084 using underlying_span = std::
1085 conditional_t<std::is_const_v<T>, std::span<const typename T::value_type>, std::span<typename T::value_type>>;
1086
1087 public:
1088 /// The element type of the underlying span
1089 using value_type = typename underlying_span::value_type;
1090
1091 /// The size type of the underlying span
1092 using size_type = typename underlying_span::size_type;
1093
1094 /// The iterator type of the underlying span
1095 using iterator = typename underlying_span::iterator;
1096
1097 /// The pointer type of the underlying span
1098 using pointer = typename underlying_span::pointer;
1099
1100 /// The const pointer type of the underlying span
1101 using const_pointer = typename underlying_span::const_pointer;
1102
1103 /// Default constructor, creates an empty span
1104 StrongSpan() = default;
1105
1106 /// Annotate a plain span with this strong type's information
1107 /// @param span the span to annotate
1108 explicit StrongSpan(underlying_span span) : m_span(span) {}
1109
1110 /// Create a span covering the contents of a strong type
1111 /// @param strong the strong type to view
1112 // NOLINTNEXTLINE(*-explicit-conversions)
1113 StrongSpan(T& strong) : m_span(strong) {}
1114
1115 // Allows implicit conversion from `StrongSpan<T>` to `StrongSpan<const T>`.
1116 // Note that this is not bi-directional. Conversion from `StrongSpan<const T>`
1117 // to `StrongSpan<T>` is not allowed.
1118 //
1119 // TODO: Technically, we should be able to phrase this with a `requires std::is_const_v<T>`
1120 // instead of the `std::enable_if` constructions. clang-tidy (14 or 15) doesn't seem
1121 // to pick up on that (yet?). As a result, for a non-const T it assumes this to be
1122 // a declaration of an ordinary copy constructor. The existence of a copy constructor
1123 // is interpreted as "not cheap to copy", setting off the `performance-unnecessary-value-param` check.
1124 // See also: https://github.com/randombit/botan/issues/3591
1125 /// Convert a StrongSpan<T> to a StrongSpan<const T>
1126 /// @param other the span to convert
1127 template <concepts::contiguous_strong_type T2>
1128 // NOLINTNEXTLINE(*-explicit-conversions)
1130 requires(std::is_same_v<T2, std::remove_const_t<T>>)
1131 : m_span(other.get()) {}
1132
1133 /// Copy constructor
1134 /// @param other the span to copy
1135 StrongSpan(const StrongSpan& other) = default;
1136
1137 /// Move constructor
1138 /// @param other the span to move from
1139 StrongSpan(StrongSpan&& other) = default;
1140
1141 /// Copy assignment
1142 /// @param other the span to copy
1143 /// @return reference to this
1144 StrongSpan& operator=(const StrongSpan& other) = default;
1145
1146 /// Move assignment
1147 /// @param other the span to move from
1148 /// @return reference to this
1149 StrongSpan& operator=(StrongSpan&& other) = default;
1150
1151 ~StrongSpan() = default;
1152
1153 /**
1154 * Access the underlying span
1155 * @returns the underlying std::span without any type constraints
1156 */
1157 underlying_span get() const { return m_span; }
1158
1159 /**
1160 * Access the underlying span
1161 * @returns the underlying std::span without any type constraints
1162 */
1163 underlying_span get() { return m_span; }
1164
1165 /// Access the underlying storage
1166 /// @return a pointer to the first element
1167 decltype(auto) data() noexcept(noexcept(this->m_span.data())) { return this->m_span.data(); }
1168
1169 /// Access the underlying storage
1170 /// @return a const pointer to the first element
1171 decltype(auto) data() const noexcept(noexcept(this->m_span.data())) { return this->m_span.data(); }
1172
1173 /// Query the size of the span
1174 /// @return the number of elements
1175 decltype(auto) size() const noexcept(noexcept(this->m_span.size())) { return this->m_span.size(); }
1176
1177 /// Query whether the span is empty
1178 /// @return true if the span covers no elements
1179 bool empty() const noexcept(noexcept(this->m_span.empty())) { return this->m_span.empty(); }
1180
1181 /// Iterate the span
1182 /// @return an iterator to the first element
1183 decltype(auto) begin() noexcept(noexcept(this->m_span.begin())) { return this->m_span.begin(); }
1184
1185 /// Iterate the span
1186 /// @return a const iterator to the first element
1187 decltype(auto) begin() const noexcept(noexcept(this->m_span.begin())) { return this->m_span.begin(); }
1188
1189 /// Iterate the span
1190 /// @return an iterator one past the last element
1191 decltype(auto) end() noexcept(noexcept(this->m_span.end())) { return this->m_span.end(); }
1192
1193 /// Iterate the span
1194 /// @return a const iterator one past the last element
1195 decltype(auto) end() const noexcept(noexcept(this->m_span.end())) { return this->m_span.end(); }
1196
1197 /// Element access
1198 /// @param i the index of the element
1199 /// @return reference to the element at index i
1200 decltype(auto) operator[](typename underlying_span::size_type i) const noexcept { return this->m_span[i]; }
1201
1202 private:
1203 underlying_span m_span;
1204};
1205
1206/**
1207 * Trait that detects whether the given type is a StrongSpan<> instantiation
1208 */
1209template <typename>
1210struct is_strong_span : std::false_type {};
1211
1212/// @copydoc is_strong_span
1213template <typename T>
1214struct is_strong_span<StrongSpan<T>> : std::true_type {};
1215
1216template <typename T>
1218
1219} // namespace Botan
1220
1221#endif
typename underlying_span::size_type size_type
The size type of the underlying span.
decltype(auto) begin() noexcept(noexcept(this->m_span.begin()))
StrongSpan()=default
Default constructor, creates an empty span.
decltype(auto) data() noexcept(noexcept(this->m_span.data()))
underlying_span get() const
StrongSpan(T &strong)
typename underlying_span::value_type value_type
The element type of the underlying span.
~StrongSpan()=default
decltype(auto) begin() const noexcept(noexcept(this->m_span.begin()))
StrongSpan(const StrongSpan &other)=default
StrongSpan(const StrongSpan< T2 > &other)
decltype(auto) end() noexcept(noexcept(this->m_span.end()))
StrongSpan & operator=(StrongSpan &&other)=default
StrongSpan(underlying_span span)
typename underlying_span::const_pointer const_pointer
The const pointer type of the underlying span.
bool empty() const noexcept(noexcept(this->m_span.empty()))
underlying_span get()
decltype(auto) operator[](typename underlying_span::size_type i) const noexcept
typename underlying_span::pointer pointer
The pointer type of the underlying span.
decltype(auto) data() const noexcept(noexcept(this->m_span.data()))
decltype(auto) end() const noexcept(noexcept(this->m_span.end()))
StrongSpan & operator=(const StrongSpan &other)=default
typename underlying_span::iterator iterator
The iterator type of the underlying span.
StrongSpan(StrongSpan &&other)=default
decltype(auto) size() const noexcept(noexcept(this->m_span.size()))
static constexpr bool has_capability()
typename T::iterator iterator
The iterator type of the wrapped container.
typename T::value_type value_type
The element type of the wrapped container.
decltype(auto) cbegin() const noexcept(noexcept(this->get().cbegin()))
decltype(auto) begin() noexcept(noexcept(this->get().begin()))
Container_Strong_Adapter_Base(InputIt begin, InputIt end)
decltype(auto) cend() noexcept(noexcept(this->get().cend()))
typename T::size_type size_type
The size type of the wrapped container.
decltype(auto) cend() const noexcept(noexcept(this->get().cend()))
decltype(auto) end() const noexcept(noexcept(this->get().end()))
void reserve(size_type size) noexcept(noexcept(this->get().reserve(size)))
size_type size() const noexcept(noexcept(this->get().size()))
decltype(auto) end() noexcept(noexcept(this->get().end()))
bool empty() const noexcept(noexcept(this->get().empty()))
decltype(auto) begin() const noexcept(noexcept(this->get().begin()))
void resize(size_type size) noexcept(noexcept(this->get().resize(size)))
typename T::const_iterator const_iterator
The const iterator type of the wrapped container.
decltype(auto) cbegin() noexcept(noexcept(this->get().cbegin()))
decltype(auto) data() const noexcept(noexcept(this->get().data()))
decltype(auto) data() noexcept(noexcept(this->get().data()))
typename T::const_pointer const_pointer
Strong_Adapter(std::span< const typename Container_Strong_Adapter_Base< T >::value_type > span)
T wrapped_type
The type wrapped by this strong type.
Definition strong_type.h:81
constexpr const T & get() const &
Strong_Base(Strong_Base &&) noexcept=default
Move constructor.
constexpr T & get() &
Strong_Base()=default
Default constructor, value initializes the wrapped value.
constexpr const T && get() const &&
constexpr T && get() &&
Strong_Base(const Strong_Base &)=default
Copy constructor.
constexpr bool has_capability
Definition strong_type.h:69
ASN1_Type operator|(ASN1_Type x, ASN1_Type y)
Definition asn1_obj.h:84
BigInt operator*(const BigInt &x, const BigInt &y)
Definition big_ops3.cpp:57
constexpr auto operator&=(Strong< T1, Tags... > &a, T2 b)
OctetString operator^(const OctetString &k1, const OctetString &k2)
Definition symkey.cpp:109
OctetString operator+(const OctetString &k1, const OctetString &k2)
Definition symkey.cpp:99
constexpr auto operator++(Strong< T, Tags... > &a, int)
std::ostream & operator<<(std::ostream &out, const OID &oid)
Definition asn1_oid.cpp:302
constexpr auto operator>>=(Strong< T1, Tags... > &a, T2 b)
BigInt operator/(const BigInt &x, const BigInt &y)
Definition big_ops3.cpp:106
BigInt operator-(const BigInt &x, const BigInt &y)
Definition bigint.h:1219
constexpr auto operator<<=(Strong< T1, Tags... > &a, T2 b)
constexpr decltype(auto) unwrap_strong_type(T &&t)
Generically unwraps a strong type to its underlying type.
constexpr auto operator|=(Strong< T1, Tags... > &a, T2 b)
typename detail::wrapped_type_helper< std::remove_cvref_t< T > >::type strong_type_wrapped_type
Extracts the wrapped type from a strong type.
constexpr decltype(auto) wrap_strong_type(ParamT &&t)
Wraps a value into a caller-defined (strong) type.
std::vector< uint8_t, Alloc > & operator^=(std::vector< uint8_t, Alloc > &out, const std::vector< uint8_t, Alloc2 > &in)
Definition mem_ops.h:532
auto operator<=>(const Strong< T, Tags... > &lhs, const Strong< T, Tags... > &rhs)
constexpr bool is_strong_span_v
std::vector< T, Alloc > & operator+=(std::vector< T, Alloc > &out, const std::vector< T, Alloc2 > &in)
Definition secmem.h:168
int operator>>(int fd, Pipe &pipe)
Definition fd_unix.cpp:43
constexpr bool is_strong_type_v
Definition strong_type.h:33
bool operator==(const AlgorithmIdentifier &x, const AlgorithmIdentifier &y)
Definition alg_id.cpp:54
constexpr auto operator*=(Strong< T1, Tags... > &a, T2 b)
constexpr auto operator--(Strong< T, Tags... > &a, int)
constexpr auto operator/=(Strong< T1, Tags... > &a, T2 b)
constexpr auto operator-=(Strong< T1, Tags... > &a, T2 b)
ECIES_Flags operator&(ECIES_Flags a, ECIES_Flags b)
Definition ecies.h:70
typename T::wrapped_type type
The resolved type.