arg_router  1.4.0
C++ command line argument parsing and routing
unsafe_any.hpp
1 // Copyright (C) 2022 by Camden Mannett.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
4 
5 #pragma once
6 
7 #include "arg_router/config.hpp"
8 
9 #include <cstring>
10 #include <functional>
11 
12 namespace arg_router::utility
13 {
19 template <std::size_t SmallObjectOptimisationSize = sizeof(std::size_t)>
20 // NOLINTNEXTLINE(*-special-member-functions)
22 {
23  using ptr_type = void*;
24  using aligned_storage_type = std::aligned_storage_t<SmallObjectOptimisationSize>;
25 
26  template <typename T>
27  constexpr static bool use_internal_storage = (sizeof(T) <= sizeof(aligned_storage_type));
28 
29 public:
34  constexpr unsafe_any_t() = default;
35 
43  template <typename T,
44  typename = std::enable_if_t<use_internal_storage<T> &&
45  !std::is_same_v<std::decay_t<T>, unsafe_any_t>>>
46  // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
47  unsafe_any_t(T&& value) noexcept
48  {
49  using value_type = std::decay_t<T>;
50 
51  // Build in buffer
52  new (&storage_.buffer) value_type(std::forward<T>(value));
53 
54  copier_ = [](const storage_type& storage) -> unsafe_any_t {
55  return *reinterpret_cast<const value_type*>(&storage.buffer);
56  };
57  destroyer_ = [](storage_type& storage) {
58  auto ptr = reinterpret_cast<value_type*>(&storage.buffer);
59  ptr->~value_type();
60  };
61  }
62 
72  template <typename T,
73  typename Allocator = config::allocator<std::decay_t<T>>,
74  typename = std::enable_if_t<!use_internal_storage<T> &&
75  !std::is_same_v<std::decay_t<T>, unsafe_any_t>>>
76  // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
77  unsafe_any_t(T&& value, Allocator alloc = Allocator{})
78  {
79  using value_type = std::decay_t<T>;
80 
81  // Build using memory from allocator
82  storage_.ptr = alloc.allocate(1);
83  new (storage_.ptr) value_type(std::forward<T>(value));
84 
85  copier_ = [](const storage_type& storage) -> unsafe_any_t {
86  return *reinterpret_cast<const value_type*>(storage.ptr);
87  };
88  destroyer_ = [alloc = std::move(alloc)](storage_type& storage) mutable noexcept {
89  auto s_ptr = reinterpret_cast<value_type*>(storage.ptr);
90 
91  std::allocator_traits<Allocator>::destroy(alloc, s_ptr);
92  alloc.deallocate(s_ptr, 1);
93  storage.ptr = nullptr;
94  };
95  }
96 
102  unsafe_any_t(unsafe_any_t&& other) noexcept { swap(*this, other); }
103 
109  {
110  auto new_any = other.copier_(other.storage_);
111  swap(*this, new_any);
112  }
113 
120  {
121  swap(*this, other);
122  return *this;
123  }
124 
127  ~unsafe_any_t() noexcept
128  {
129  if (destroyer_) {
130  destroyer_(storage_);
131  }
132  }
133 
139  [[nodiscard]] bool has_value() const noexcept { return !!destroyer_; }
140 
147  template <typename T, typename DecayType = std::decay_t<T>>
148  [[nodiscard]] std::decay_t<T>& get() noexcept
149  {
150  using value_type = std::decay_t<T>;
151 
152  if constexpr (use_internal_storage<value_type>) {
153  return *reinterpret_cast<value_type*>(&storage_.buffer);
154  } else {
155  return *reinterpret_cast<value_type*>(storage_.ptr);
156  }
157  }
158 
166  template <typename T>
167  [[nodiscard]] auto get() const noexcept
168  -> std::conditional_t<(sizeof(T) <= sizeof(std::size_t)) && std::is_copy_constructible_v<T>,
169  std::decay_t<T>,
170  const std::decay_t<T>&>
171  {
172  using value_type = std::decay_t<T>;
173 
174  if constexpr (use_internal_storage<value_type>) {
175  return *reinterpret_cast<const value_type*>(&storage_.buffer);
176  } else {
177  return *reinterpret_cast<const value_type*>(storage_.ptr);
178  }
179  }
180 
186  friend void swap(unsafe_any_t& a, unsafe_any_t& b) noexcept
187  {
188  using std::swap;
189 
190  swap(a.storage_, b.storage_);
191  swap(a.copier_, b.copier_);
192  swap(a.destroyer_, b.destroyer_);
193  }
194 
195 private:
196  union storage_type {
197  constexpr storage_type() noexcept : ptr{nullptr} {}
198 
199  ptr_type ptr;
200  aligned_storage_type buffer;
201  };
202 
203  storage_type storage_;
204  std::function<unsafe_any_t(const storage_type&)> copier_;
205  std::function<void(storage_type&)> destroyer_;
206 };
207 
210 using unsafe_any = unsafe_any_t<sizeof(std::string_view)>;
211 } // namespace arg_router::utility
unsafe_any_t & operator=(unsafe_any_t other) noexcept
Definition: unsafe_any.hpp:119
bool has_value() const noexcept
Definition: unsafe_any.hpp:139
unsafe_any_t(unsafe_any_t &&other) noexcept
Definition: unsafe_any.hpp:102
unsafe_any_t(T &&value, Allocator alloc=Allocator{})
Definition: unsafe_any.hpp:77
auto get() const noexcept -> std::conditional_t<(sizeof(T)<=sizeof(std::size_t)) &&std::is_copy_constructible_v< T >, std::decay_t< T >, const std::decay_t< T > & >
Definition: unsafe_any.hpp:167
unsafe_any_t(const unsafe_any_t &other)
Definition: unsafe_any.hpp:108
std::decay_t< T > & get() noexcept
Definition: unsafe_any.hpp:148
unsafe_any_t(T &&value) noexcept
Definition: unsafe_any.hpp:47
constexpr unsafe_any_t()=default
friend void swap(unsafe_any_t &a, unsafe_any_t &b) noexcept
Definition: unsafe_any.hpp:186
std::allocator< T > allocator
Definition: config.hpp:70