emlabcpp
modern opinionated embedded C++ library
static_function.h
Go to the documentation of this file.
1 
24 #pragma once
25 
26 #include "./pmr/util.h"
27 
28 #include <functional>
29 #include <memory>
30 
31 namespace emlabcpp
32 {
33 
34 namespace detail
35 {
36  enum class static_function_operations : uint8_t
37  {
38  COPY,
39  MOVE,
40  DESTROY
41  };
42 
43  template < typename ReturnType, typename... ArgTypes >
45  {
46  ReturnType ( *invoke )( void*, ArgTypes... );
47  void* ( *handle )( void*, void*, static_function_operations );
48  };
49 
50  template < typename T, typename ReturnType, typename... ArgTypes >
52  {
53  [[no_unique_address]] T item_;
54 
55  public:
56  explicit static_function_storage( T&& item )
57  : item_( std::move( item ) )
58  {
59  }
60 
61  explicit static_function_storage( T const& item )
62  : item_( item )
63  {
64  }
65 
66  static void* construct_at( void* ptr, T item )
67  {
68  ptr = pmr::align( ptr, alignof( static_function_storage ) );
69  return std::construct_at(
70  reinterpret_cast< static_function_storage* >( ptr ),
71  std::move( item ) );
72  }
73 
74  static ReturnType invoke( void* const source, ArgTypes... args )
75  {
76  auto ptr = reinterpret_cast< static_function_storage* >( source );
77  return std::invoke( ptr->item_, std::forward< ArgTypes >( args )... );
78  }
79 
80  static void*
81  handle( void* const source, void* target, static_function_operations const op )
82  {
83  auto ptr = reinterpret_cast< static_function_storage* >( source );
84 
86  std::destroy_at( ptr );
87  return nullptr;
88  }
89 
90  target = pmr::align( target, alignof( static_function_storage ) );
91 
93  return std::construct_at(
94  reinterpret_cast< static_function_storage* >( target ), *ptr );
95  } else { // static_function_operations::MOVE
96  return std::construct_at(
97  reinterpret_cast< static_function_storage* >( target ),
98  std::move( *ptr ) );
99  }
100  }
101 
102  static constexpr static_function_vtable< ReturnType, ArgTypes... > vtable = {
103  invoke,
104  handle };
105  };
106 } // namespace detail
107 
108 template < typename CallableType, std::size_t Capacity, std::size_t Align >
110 
111 template < typename ReturnType, typename... ArgTypes, std::size_t Capacity, std::size_t Align >
112 class static_function_base< ReturnType( ArgTypes... ), Capacity, Align >
113 {
114  using vtable = detail::static_function_vtable< ReturnType, ArgTypes... >;
115 
116 public:
117  // TODO: maybe storage should also count the size of 'static_function_storage' ?
118  // that is: only sizeof(T) bytes from storage should be used for T, not
119  // sizeof(static_function_storage<T>) bytes
120  using storage_type = std::byte[Capacity];
121  using result_type = ReturnType;
122 
123  static_function_base() = default;
124 
125  explicit static_function_base( std::nullptr_t const )
127  {
128  }
129 
131  {
132  *this = other;
133  }
134 
136  {
137  *this = std::move( other );
138  }
139 
140  template < typename Callable >
141  explicit( false ) static_function_base( Callable c )
142  {
143  static_assert(
144  std::invocable< Callable, ArgTypes... >,
145  "Provided callable has to be invocable with arguments" );
146  static_assert(
147  std::
148  convertible_to< std::invoke_result_t< Callable, ArgTypes... >, ReturnType >,
149  "Provided callable has to return the same type as static_function" );
150  *this = std::move( c );
151  }
152 
154  {
155  if ( this == &other )
156  return *this;
157 
158  clear();
159 
160  if ( other ) {
161  obj_ = other.vtable_->handle(
162  other.obj_, &storage_, detail::static_function_operations::COPY );
163  vtable_ = other.vtable_;
164  }
165 
166  return *this;
167  }
168 
170  {
171  if ( this == &other )
172  return *this;
173 
174  clear();
175 
176  if ( other ) {
177  obj_ = other.vtable_->handle(
178  other.obj_, &storage_, detail::static_function_operations::MOVE );
179  vtable_ = other.vtable_;
180  }
181 
182  return *this;
183  }
184 
185  static_function_base& operator=( std::nullptr_t const )
186  {
187  clear();
188  return *this;
189  }
190 
191  template < typename Callable >
193  {
194  static_assert(
195  std::invocable< Callable, ArgTypes... >,
196  "Provided callable has to be invocable with arguments" );
197 
198  clear();
199 
200  using storage =
201  detail::static_function_storage< Callable, ReturnType, ArgTypes... >;
202 
203  static constexpr std::size_t requires_space =
204  required_space( sizeof( storage ), alignof( storage ) );
205 
206  static_assert(
207  requires_space <= Capacity, "Callable would not fit into the static_function" );
208 
209  // TODO: check that class fits with alignment into storage
210  obj_ = storage::construct_at( &storage_, std::move( c ) );
211  vtable_ = &storage::vtable;
212 
213  return *this;
214  }
215 
216  explicit( false ) operator bool() const noexcept
217  {
218  return obj_ != nullptr;
219  }
220 
221  ReturnType operator()( ArgTypes... args )
222  {
223  return vtable_->invoke( obj_, std::forward< ArgTypes >( args )... );
224  }
225 
226  ReturnType operator()( ArgTypes... args ) const
227  {
228  return vtable_->invoke( obj_, std::forward< ArgTypes >( args )... );
229  }
230 
232  {
233  clear();
234  }
235 
236 private:
237  static constexpr std::size_t required_space( std::size_t size, std::size_t const align )
238  {
239  if ( align > Align )
240  size += align - Align;
241  return size;
242  }
243 
244  void clear()
245  {
246  if ( obj_ != nullptr ) {
247  // temporary obj required for throwing destructors
248  void* obj = obj_;
249  vtable const* vtbl = vtable_;
250  obj_ = nullptr;
251  vtable_ = nullptr;
252  vtbl->handle( obj, nullptr, detail::static_function_operations::DESTROY );
253  }
254  }
255 
256  vtable const* vtable_ = nullptr;
257  void* obj_ = nullptr;
258  alignas( Align ) storage_type storage_;
259 };
260 
261 template < typename Signature, std::size_t Capacity >
262 using static_function = static_function_base< Signature, Capacity, alignof( void* ) >;
263 
264 } // namespace emlabcpp
Definition: static_function.h:52
static void * construct_at(void *ptr, T item)
Definition: static_function.h:66
static_function_storage(T const &item)
Definition: static_function.h:61
static constexpr static_function_vtable< ReturnType, ArgTypes... > vtable
Definition: static_function.h:102
static void * handle(void *const source, void *target, static_function_operations const op)
Definition: static_function.h:81
static_function_storage(T &&item)
Definition: static_function.h:56
static ReturnType invoke(void *const source, ArgTypes... args)
Definition: static_function.h:74
static_function_base(std::nullptr_t const)
Definition: static_function.h:125
static_function_base & operator=(Callable c)
Definition: static_function.h:192
static_function_base(static_function_base &&other) noexcept
Definition: static_function.h:135
ReturnType operator()(ArgTypes... args) const
Definition: static_function.h:226
static_function_base & operator=(static_function_base const &other)
Definition: static_function.h:153
static_function_base(static_function_base const &other)
Definition: static_function.h:130
ReturnType operator()(ArgTypes... args)
Definition: static_function.h:221
static_function_base & operator=(std::nullptr_t const)
Definition: static_function.h:185
std::byte[Capacity] storage_type
Definition: static_function.h:120
static_function_base & operator=(static_function_base &&other) noexcept
Definition: static_function.h:169
static_function_operations
Definition: static_function.h:37
void * align(void *const ptr, std::size_t const alignment)
TODO: this needs tests.
Definition: util.h:41
MIT License.
Definition: impl.h:31
Args const & args
Definition: min_max.h:83
physical_quantity< 0, 0, 0, 0, 0, 0, 0, 0, 1 > byte
Definition: physical_quantity.h:118
Definition: static_function.h:109
Definition: static_function.h:45
ReturnType(* invoke)(void *, ArgTypes...)
Definition: static_function.h:46