├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── any
├── any.h
└── tag_invoke.h
├── can
├── can_codec.h
├── can_kernel.h
└── frame_packet.h
├── dbc
├── README.md
├── dbc_parser.cpp
├── dbc_parser.h
└── parser_template.h
├── example
├── example.cpp
└── example.dbc
└── v2c
├── README.md
├── v2c_transcoder.cpp
└── v2c_transcoder.h
/.editorconfig:
--------------------------------------------------------------------------------
1 | # https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = tab
6 | indent_size = 4
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Compiled Object files
5 | *.slo
6 | *.lo
7 | *.o
8 | *.obj
9 |
10 | # Precompiled Headers
11 | *.gch
12 | *.pch
13 |
14 | # Compiled Dynamic libraries
15 | *.so
16 | *.dylib
17 | *.dll
18 |
19 | # Fortran module files
20 | *.mod
21 | *.smod
22 |
23 | # Compiled Static libraries
24 | *.lai
25 | *.la
26 | *.a
27 | *.lib
28 |
29 | # Executables
30 | *.exe
31 | *.out
32 | *.app
33 |
34 | # IDEs
35 | .vs
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2001-2023 Mireo, EU
2 |
3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 |
5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6 |
7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8 |
9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10 |
11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | C++ CAN utilities, including fully compliant CAN DBC C++ parser
===============================================================
[](LICENSE)
[](https://github.com/mireo/can-utils/graphs/contributors)
[](README.md)
[](README.md)
[](https://github.com/mireo/can-utils/issues)
Introduction
------------
This repository contains several CAN (Controller Area Network) C++ utilities which could simplify collecting, decoding, transcoding and transferring CAN messages to cloud.
Most of the code in the repository is designed to run on an edge device (for example, an embedded telemetry device). However, utilities like CAN DBC parser or CAN frame packet buffer can also be used on server side, thus providing some of the essential tools in [IOT telemetry](https://iotatlas.net/en/patterns/telemetry/) ecosystems.
Features
--------
* [DBC parser](dbc/README.md)
* A complete, customizable and efficient DBC parser written in C++ with full DBC syntax support for all keywords.
* [Vehicle-To-Cloud Transcoder](v2c/README.md)
* Edge-computing telemetric component that groups, filters, and aggregates CAN signals. Can drastically reduce the amount of data sent from the device over the network.
Uses the DBC parser to read and define the CAN network.
How to Build
------------
#### 1. Fetch Boost
* Download [Boost](https://www.boost.org/users/download/) and move it to your include path
The project requires only headers from Boost, so no libraries need to be built.
#### 2. Build
You can compile the example as follows:
```sh
$ g++ -std=c++20 example/example.cpp dbc/dbc_parser.cpp v2c/v2c_transcoder.cpp -I . -o can_example
```
`can-utils` has been tested with Clang, GCC and MSVC on Windows and Linux. It requires C++20.
Usage
-----
### Example
- [Full source here](example/example.cpp)
Build, then run without any command line arguments:
```sh
$ ./can_example
```
The example program parses [example.dbc](example/example.dbc), generates millions of random frames, aggregates them with `v2c_transcoder`, and prints the decoded raw signals to the console.
Example output:
```py
New frame_packet (from 2121812 frames):
can_frame at t: 1683709842.116000s, can_id: 4
SOCavg: 574
can_frame at t: 1683709842.116000s, can_id: 6
RawBattCurrent: 10914
SmoothBattCurrent: 10921
BattVoltage: 32760
can_frame at t: 1683709842.516000s, can_id: 2
GPSAccuracy: 118
GPSLongitude: -106019721
GPSLatitude: 26758102
can_frame at t: 1683709842.516000s, can_id: 3
GPSAltitude: -8084
can_frame at t: 1683709842.516000s, can_id: 5
GPSSpeed: 2160
can_frame at t: 1683709842.516000s, can_id: 7
PowerState: 2
can_frame at t: 1683709842.616000s, can_id: 4
SOCavg: 163
can_frame at t: 1683709842.616000s, can_id: 6
RawBattCurrent: -27877
SmoothBattCurrent: -27827
BattVoltage: 32731
...
```
The signal values are raw decoded bytes, not scaled by the signal's factor or offset.
___
### DBC Parser
- [Full documentation here.](dbc/README.md)
The parser can be used as follows:
```cpp
custom_dbc dbc_impl; // custom class that implements your logic and data structures
bool success = can::parse_dbc(dbc_content, std::ref(dbc_impl)); // parses the DBC
// dbc_impl is now populated by the parser and can be used
```
The behavior of the parser is customized by user-defined callbacks invoked when parsing a DBC keyword.
Defining the following callback would print all `BO_` objects (messages) in the DBC, and call `add_message()` on `dbc_impl`:
``` cpp
inline void tag_invoke(
def_bo_cpo, dbc_impl& this_,
uint32_t msg_id, std::string msg_name, size_t msg_size, size_t transmitter_ord
) {
std::cout << "New message '" << msg_name << "' with ID = " << msg_id << std::endl;
this_.add_message(msg_id, msg_name, msg_size);
}
```
The full list of callback function signatures, with examples, can be found [here](dbc/README.md).
___
### V2C Transcoder
- [Full documentation here](v2c/README.md)
V2C is modeled as a node in the CAN network. It reads CAN frames as input, aggregates their values, and encodes them back into CAN `frame_packets`.
To use it, initialize `v2c_transcoder` and then call its `transcode(t, frame)` method with frames read from the CAN socket.
`transcode()` periodically returns a `frame_packet` containing the aggregated `can_frames`, ready to be sent over the network.
```cpp
can::v2c_transcoder transcoder;
can::parse_dbc(read_file("example/example.dbc"), std::ref(transcoder));
while (true) {
// read a frame from the CAN socket
can_frame frame = read_frame();
auto t = std::chrono::system_clock::now();
auto fp = transcoder.transcode(t, frame);
if (fp) {
// send the frame_packet over the network
send_frame_packet(fp);
}
}
```
The transcoder's message groups, aggregation types and sampling/sending windows are customized through the DBC directly:
```py
EV_ V2CTxTime: 0 [0|60000] "ms" 2000 1 DUMMY_NODE_VECTOR1 V2C;
EV_ GPSGroupTxFreq: 0 [0|60000] "ms" 600 11 DUMMY_NODE_VECTOR1 V2C;
EV_ EnergyGroupTxFreq: 0 [0|60000] "ms" 500 13 DUMMY_NODE_VECTOR1 V2C;
BA_ "AggType" SG_ 7 PowerState "LAST";
BA_ "AggType" SG_ 4 SOCavg "LAST";
BA_ "AggType" SG_ 6 RawBattCurrent "AVG";
BA_ "AggType" SG_ 6 SmoothBattCurrent "AVG";
```
A more in-depth explanation can be found [here](v2c/README.md).
Contributing
------------
When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.
You may merge a Pull Request once you have the sign-off from other developers, or you may request the reviewer to merge it for you.
License
-------
Copyright (c) 2001-2023 Mireo, EU
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Credits
----------
Maintained and authored by [Mireo](https://www.mireo.com/spacetime).
--------------------------------------------------------------------------------
/any/any.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | /**
4 |
5 | Read:
6 |
7 | https://github.com/facebookexperimental/libunifex/blob/main/doc/type_erasure.md
8 |
9 | Synopsis:
10 |
11 | template
12 | class any;
13 |
14 | Type-erasing wrapper is a move-only wrapper that implements a small-object optimisation
15 | that allows storing the wrapped object inline if it is smaller than the inline buffer,
16 | and heap-allocates storage for the object if it does not fit in the inline buffer.
17 |
18 | template
19 | class any_copyable;
20 |
21 | Copyable variant of `mireo::any`. Requires wrapped objects to be copy-constructible and
22 | copy-assignable.
23 |
24 | template
25 | class any_function;
26 |
27 | Similar to std::function that can be further customized with CPOs; like mireo::any.
28 |
29 | mireo::any_function the_main = main;
30 | the_main(argc, argv);
31 |
32 | template
33 | class any_copyable_function;
34 |
35 | Copyable variant of `mireo::any_function`.
36 |
37 | mireo::any_copyable_function main1 = &main;
38 | auto main2 = main1;
39 |
40 | */
41 |
42 | #include
43 | #include "tag_invoke.h"
44 |
45 | namespace mireo {
46 |
47 | //
48 | // this_
49 | //
50 |
51 | struct this_ { };
52 |
53 | template
54 | inline constexpr bool is_this_v = false;
55 | template <>
56 | inline constexpr bool is_this_v = true;
57 | template <>
58 | inline constexpr bool is_this_v = true;
59 | template <>
60 | inline constexpr bool is_this_v = true;
61 | template <>
62 | inline constexpr bool is_this_v = true;
63 | template <>
64 | inline constexpr bool is_this_v = true;
65 | template <>
66 | inline constexpr bool is_this_v = true;
67 |
68 | namespace detail {
69 |
70 | struct _ignore {
71 | template _ignore(T&&) noexcept { }
72 | };
73 |
74 | template
75 | struct _replace_this;
76 |
77 | template <>
78 | struct _replace_this {
79 | template
80 | using apply = Arg;
81 |
82 | template
83 | static Arg&& get(Arg&& arg, _ignore) noexcept {
84 | return (Arg &&) arg;
85 | }
86 | };
87 |
88 | template <>
89 | struct _replace_this {
90 | template
91 | using apply = T;
92 |
93 | template
94 | static T&& get(_ignore, T& obj) noexcept {
95 | return (T &&) obj;
96 | }
97 | };
98 |
99 | template <>
100 | struct _replace_this {
101 | template
102 | using apply = T&;
103 |
104 | template
105 | static T& get(_ignore, T& obj) noexcept {
106 | return obj;
107 | }
108 | };
109 |
110 | template <>
111 | struct _replace_this {
112 | template
113 | using apply = T&&;
114 |
115 | template
116 | static T&& get(_ignore, T& obj) noexcept {
117 | return (T &&) obj;
118 | }
119 | };
120 |
121 | template <>
122 | struct _replace_this {
123 | template
124 | using apply = const T&;
125 |
126 | template
127 | static const T& get(_ignore, T& obj) noexcept {
128 | return obj;
129 | }
130 | };
131 |
132 | template <>
133 | struct _replace_this {
134 | template
135 | using type = const T&&;
136 |
137 | template
138 | static const T&& get(_ignore, T& obj) noexcept {
139 | return (const T&&) obj;
140 | }
141 | };
142 |
143 | template
144 | using _normalize_t = std::conditional_t, Arg, void>;
145 |
146 | template
147 | using replace_this = _replace_this<_normalize_t>;
148 |
149 | template
150 | using replace_this_t = typename replace_this::template apply;
151 |
152 | template
153 | using replace_this_with_void_ptr_t = std::conditional_t, void*, Arg>;
154 |
155 | template
156 | struct _extract_this {
157 | template
158 | TFirst&& operator()(TFirst&& first, TRest&&...) const noexcept {
159 | return (TFirst&&) first;
160 | }
161 | };
162 |
163 | template
164 | struct _extract_this {
165 | template
166 | decltype(auto) operator()(_ignore, TRest&&... rest) const noexcept {
167 | static_assert(sizeof...(IsThis) > 0, "Arguments to extract_this");
168 | return _extract_this{ }((TRest &&) rest...);
169 | }
170 | };
171 |
172 | template
173 | using extract_this = _extract_this...>;
174 |
175 | //
176 | // overload
177 | //
178 |
179 | template
180 | struct _cpo_t {
181 | struct type;
182 | };
183 |
184 | // This type will have the associated namespaces
185 | // of CPO (by inheritance) but not those of Sig.
186 | template
187 | struct _cpo_t::type : CPO {
188 | constexpr type() = default;
189 | constexpr type(CPO) noexcept {}
190 |
191 | using base_cpo_t = CPO;
192 | using type_erased_signature_t = Sig;
193 | };
194 |
195 | template
196 | struct base_cpo {
197 | using type = CPO;
198 | };
199 |
200 | template
201 | struct base_cpo> {
202 | using type = typename CPO::base_cpo_t;
203 | };
204 |
205 | template
206 | inline constexpr typename _cpo_t::type _cpo { };
207 |
208 | template
209 | struct _sig { };
210 |
211 | template
212 | inline constexpr _sig const sig { };
213 |
214 | template
215 | using base_cpo_t = typename base_cpo::type;
216 |
217 | //
218 | // vtable
219 | //
220 |
221 | template
222 | Ret _vtable_invoke(CPO cpo, replace_this_with_void_ptr_t... args) noexcept {
223 | void* this_ptr = extract_this { }(args...);
224 | return ((CPO &&)cpo)(replace_this::get((decltype(args)&&)args, *static_cast(this_ptr))...);
225 | }
226 |
227 | template
228 | class vtable_entry;
229 |
230 | template
231 | class vtable_entry {
232 | public:
233 | using fn_t = Ret(base_cpo_t, replace_this_with_void_ptr_t...) noexcept;
234 |
235 | constexpr fn_t* get() const noexcept {
236 | return _fn;
237 | }
238 |
239 | template
240 | static constexpr vtable_entry create() noexcept {
241 | return vtable_entry { &_vtable_invoke, T, Ret, Args...> };
242 | }
243 |
244 | private:
245 | explicit constexpr vtable_entry(fn_t* fn) noexcept : _fn(fn) {
246 | }
247 |
248 | fn_t* _fn;
249 | };
250 |
251 | template
252 | class vtable_entry {
253 | public:
254 | using fn_t = Ret(base_cpo_t, replace_this_with_void_ptr_t...);
255 |
256 | constexpr fn_t* get() const noexcept {
257 | return _fn;
258 | }
259 |
260 | template
261 | static constexpr vtable_entry create() noexcept {
262 | return vtable_entry { &_vtable_invoke, T, Ret, Args...> };
263 | }
264 |
265 | private:
266 | explicit constexpr vtable_entry(fn_t* fn) noexcept : _fn(fn) {
267 | }
268 |
269 | fn_t* _fn;
270 | };
271 |
272 | template
273 | class vtable : private vtable_entry... {
274 |
275 | explicit constexpr vtable(vtable_entry... entries) noexcept : vtable_entry { entries }... {
276 | }
277 |
278 | public:
279 | template
280 | static const vtable* inst() {
281 | static constexpr vtable v { vtable_entry::template create()... };
282 | return &v;
283 | };
284 |
285 | template
286 | constexpr auto get() const noexcept -> typename vtable_entry::fn_t* {
287 | const vtable_entry& entry = *this;
288 | return entry.get();
289 | }
290 | };
291 |
292 | //
293 | // get_wrapped_object / with_forwarding_tag_invoke
294 | //
295 |
296 | struct _get_wrapped_object_cpo {
297 | template requires tag_invocable<_get_wrapped_object_cpo, T>
298 | auto operator()(T&& wrapper) const noexcept(is_nothrow_tag_invocable_v<_get_wrapped_object_cpo, T>) ->
299 | tag_invoke_result_t<_get_wrapped_object_cpo, T>
300 | {
301 | return mireo::tag_invoke(*this, static_cast(wrapper));
302 | }
303 | };
304 |
305 | inline constexpr _get_wrapped_object_cpo get_wrapped_object { };
306 |
307 | template
308 | struct _with_forwarding_tag_invoke;
309 |
310 | template
311 | using with_forwarding_tag_invoke = typename _with_forwarding_tag_invoke<
312 | Derived,
313 | base_cpo_t,
314 | typename CPO::type_erased_signature_t
315 | >::type;
316 |
317 | // noexcept(false) specialisation
318 | template
319 | struct _with_forwarding_tag_invoke {
320 | struct type {
321 | friend Ret tag_invoke(CPO cpo, replace_this_t... args) {
322 | auto& wrapper = extract_this{}(args...);
323 | auto& wrapped = get_wrapped_object(wrapper);
324 | return static_cast(cpo)(replace_this::get(static_cast(args), wrapped)...);
325 | }
326 | };
327 | };
328 |
329 | // noexcept(true) specialisation
330 | template
331 | struct _with_forwarding_tag_invoke {
332 | struct type {
333 | friend Ret tag_invoke(CPO cpo, replace_this_t... args) noexcept {
334 | auto& wrapper = extract_this{}(args...);
335 | auto& wrapped = get_wrapped_object(wrapper);
336 | return static_cast(cpo)(replace_this::get(static_cast(args), wrapped)...);
337 | }
338 | };
339 | };
340 |
341 | //
342 | // with_type_erased_tag_invoke
343 | //
344 |
345 | template
346 | struct _with_type_erased_tag_invoke;
347 |
348 | template
349 | struct _with_type_erased_tag_invoke {
350 | struct type {
351 | friend Ret tag_invoke(base_cpo_t cpo, replace_this_t... args) {
352 | using cpo_t = base_cpo_t;
353 | auto&& t = extract_this{ }((decltype(args)&&)args...);
354 | void* ptr = get_object_address(t);
355 | auto* fn = get_vtable(t)->template get();
356 | return fn((cpo_t&&) cpo, replace_this::get((decltype(args)&&)args, ptr)...);
357 | }
358 | };
359 | };
360 |
361 | //
362 | // any_heap_allocated_storage
363 | //
364 |
365 | template
366 | class _any_heap_allocated_storage {
367 | struct state;
368 |
369 | using allocator_type = typename std::allocator_traits::template rebind_alloc;
370 | using allocator_traits = std::allocator_traits;
371 |
372 | // This is the state that is actually heap-allocated.
373 | struct state {
374 | template requires std::constructible_from
375 | explicit state(std::allocator_arg_t, allocator_type allocator, std::in_place_type_t, Args&&... args) :
376 | object(static_cast(args)...),
377 | allocator(std::move(allocator))
378 | {
379 | }
380 |
381 | [[no_unique_address]] T object;
382 | [[no_unique_address]] allocator_type allocator;
383 | };
384 |
385 | // This is the base-class object that holds the pointer to the
386 | // heap-allocated state.
387 | struct base {
388 | template requires std::constructible_from
389 | base(std::allocator_arg_t, allocator_type allocator, std::in_place_type_t, Args&&... args) {
390 | _state = allocator_traits::allocate(allocator, 1);
391 | allocator_traits::construct(
392 | allocator,
393 | _state,
394 | std::allocator_arg,
395 | allocator,
396 | std::in_place_type,
397 | static_cast(args)...
398 | );
399 | }
400 |
401 | template requires (
402 | !std::same_as &&
403 | std::constructible_from
404 | )
405 | explicit base(std::allocator_arg_t, Allocator allocator, std::in_place_type_t, Args&&... args) :
406 | base(
407 | std::allocator_arg,
408 | allocator_type(std::move(allocator)),
409 | std::in_place_type,
410 | static_cast(args)...
411 | )
412 | {
413 | }
414 |
415 | base(const base& other) requires std::copy_constructible :
416 | base(
417 | std::allocator_arg,
418 | const_cast(other._state->allocator),
419 | std::in_place_type,
420 | const_cast(other._state->object)
421 | )
422 | {
423 | }
424 |
425 | base(base&& other) noexcept : _state(std::exchange(other._state, nullptr)) {
426 | }
427 |
428 | ~base() {
429 | if (_state != nullptr) {
430 | allocator_type alloc = std::move(_state->allocator);
431 | _state->~state();
432 | std::allocator_traits::deallocate(alloc, _state, 1);
433 | }
434 | }
435 |
436 | private:
437 | friend T& tag_invoke(tag_t, base& self) noexcept {
438 | return self._state->object;
439 | }
440 |
441 | friend const T& tag_invoke(tag_t, const base& self) noexcept {
442 | return self._state->object;
443 | }
444 |
445 | state* _state;
446 | };
447 |
448 | template
449 | struct concrete final {
450 | class type : public base, private detail::with_forwarding_tag_invoke... {
451 | using base::base;
452 | };
453 | };
454 |
455 | public:
456 | template
457 | using type = typename concrete::type;
458 | };
459 |
460 | template
461 | using any_heap_allocated_storage = typename _any_heap_allocated_storage::template type;
462 |
463 | //
464 | // _destroy_cpo / _move_construct_cpo / _copy_construct_cpo / _invoke_cpo
465 | //
466 |
467 | struct _destroy_cpo {
468 | using type_erased_signature_t = void(this_&) noexcept;
469 |
470 | template
471 | void operator()(T& object) const noexcept {
472 | object.~T();
473 | }
474 | };
475 |
476 | struct _move_construct_cpo {
477 | using type_erased_signature_t = void(void* p, this_&& src) noexcept;
478 |
479 | template
480 | void operator()(void* p, T&& src) const noexcept {
481 | ::new (p) T(static_cast(src));
482 | }
483 | };
484 |
485 | struct _copy_construct_cpo {
486 | using type_erased_signature_t = void(void* p, const this_& src) noexcept;
487 |
488 | template
489 | void operator()(void* p, const T& src) const noexcept {
490 | ::new (p) T(src);
491 | }
492 | };
493 |
494 | template struct _invoke_cpo;
495 |
496 | template
497 | struct _invoke_cpo {
498 | using type_erased_signature_t = R(this_&, Args...);
499 |
500 | template
501 | static constexpr bool with_tag_invoke_v = mireo::is_tag_invocable_v<_invoke_cpo, F, Args...>;
502 |
503 | template
504 | static constexpr bool nothrow_invoke_v = with_tag_invoke_v ?
505 | mireo::is_nothrow_tag_invocable_v<_invoke_cpo, F, Args...> :
506 | std::is_nothrow_invocable_v;
507 |
508 | template
509 | R operator()(F&& fn, Args... arg) const noexcept(nothrow_invoke_v) {
510 | if constexpr (with_tag_invoke_v) {
511 | return mireo::tag_invoke(*this, (F&&)fn, (Args&&)arg...);
512 | }
513 | else {
514 | return ((F&&)fn)((Args&&)arg...);
515 | }
516 | }
517 | };
518 |
519 | } // namespace detail
520 |
521 | //
522 | // with_type_erased_tag_invoke
523 | //
524 | // When defining a type-erasing wrapper type, Derived, you can privately inherit
525 | // from this class to have the type opt-in to customising the specified CPO.
526 | //
527 |
528 | template
529 | using with_type_erased_tag_invoke = typename detail::_with_type_erased_tag_invoke<
530 | Derived,
531 | CPO,
532 | typename
533 | CPO::type_erased_signature_t
534 | >::type;
535 |
536 | //
537 | // _any_object
538 | //
539 |
540 | template
541 | struct _any_object {
542 | // Pad size/alignment out to allow storage of at least a pointer.
543 | static constexpr std::size_t inline_alignment = alignof(void*);
544 | static constexpr std::size_t inline_size = InlineSize < sizeof(void*) ? sizeof(void*) : InlineSize;
545 |
546 | // move-constructor is (must be) noexcept
547 | template
548 | static constexpr bool can_be_stored_inplace_v = (sizeof(T) <= inline_size && alignof(T) <= inline_alignment);
549 |
550 | static constexpr bool copyable_v = IsCopyable;
551 |
552 | class type;
553 | };
554 |
555 | template
556 | class _any_object::type :
557 | private with_type_erased_tag_invoke...
558 | {
559 | using vtable_t = std::conditional_t<
560 | _any_object::copyable_v,
561 | detail::vtable,
562 | detail::vtable
563 | >;
564 |
565 | const vtable_t* _vtable = nullptr;
566 | alignas(inline_alignment) std::byte _storage[inline_size];
567 |
568 | public:
569 | type() = default;
570 |
571 | template requires (!std::is_same_v>)
572 | type(T&& object) : type(std::in_place_type>, static_cast(object)) {
573 | }
574 |
575 | template
576 | explicit type(std::allocator_arg_t, Allocator allocator, T&& value) noexcept :
577 | type(
578 | std::allocator_arg,
579 | std::move(allocator),
580 | std::in_place_type>,
581 | static_cast(value)
582 | )
583 | {
584 | }
585 |
586 | template requires _any_object::can_be_stored_inplace_v
587 | explicit type(std::in_place_type_t, Args&&... args) : _vtable(vtable_t::template inst()) {
588 | ::new (static_cast(&_storage)) T(static_cast(args)...);
589 | }
590 |
591 | template requires (!_any_object::can_be_stored_inplace_v)
592 | explicit type(std::in_place_type_t, Args&&... args) :
593 | type(std::allocator_arg, DefaultAllocator(), std::in_place_type, static_cast(args)...)
594 | {
595 | }
596 |
597 | template requires _any_object::can_be_stored_inplace_v
598 | explicit type(std::allocator_arg_t, Allocator, std::in_place_type_t, Args&&... args) noexcept :
599 | type(std::in_place_type, static_cast(args)...)
600 | {
601 | }
602 |
603 | template requires (!_any_object::can_be_stored_inplace_v)
604 | explicit type(std::allocator_arg_t, Alloc alloc, std::in_place_type_t, Args&&... args) :
605 | _vtable(vtable_t::template inst>())
606 | {
607 | ::new (static_cast(&_storage)) detail::any_heap_allocated_storage(
608 | std::allocator_arg,
609 | std::move(alloc),
610 | std::in_place_type,
611 | static_cast(args)...
612 | );
613 | }
614 |
615 | type(const type& other) noexcept requires _any_object::copyable_v : _vtable(other._vtable) {
616 | if (_vtable) {
617 | auto* copy_cons = _vtable->template get();
618 | copy_cons(detail::_copy_construct_cpo { }, &_storage, get_object_address(other));
619 | }
620 | }
621 |
622 | type(type&& other) noexcept : _vtable(other._vtable) {
623 | if (_vtable) {
624 | auto* move_cons = _vtable->template get();
625 | move_cons(detail::_move_construct_cpo{ }, &_storage, &other._storage);
626 | }
627 | }
628 |
629 | ~type() noexcept {
630 | if (_vtable) {
631 | auto* destroy = _vtable->template get();
632 | destroy(detail::_destroy_cpo{ }, &_storage);
633 | }
634 | }
635 |
636 | type& operator=(const type& other) noexcept requires _any_object::copyable_v {
637 | if (std::addressof(other) == this)
638 | return *this;
639 |
640 | if (_vtable) {
641 | auto* destroy = _vtable->template get();
642 | destroy(detail::_destroy_cpo{}, &_storage);
643 | }
644 | _vtable = other._vtable;
645 | if (_vtable) {
646 | auto* copy_cons = _vtable->template get();
647 | copy_cons(detail::_copy_construct_cpo { }, &_storage, get_object_address(other));
648 | }
649 | return *this;
650 | }
651 |
652 | type& operator=(type&& other) noexcept {
653 | if (std::addressof(other) == this)
654 | return *this;
655 |
656 | if (_vtable) {
657 | auto* destroy = _vtable->template get();
658 | destroy(detail::_destroy_cpo{ }, &_storage);
659 | }
660 | _vtable = other._vtable;
661 | if (_vtable) {
662 | auto* move_cons = _vtable->template get();
663 | move_cons(detail::_move_construct_cpo{ }, &_storage, &other._storage);
664 | }
665 | return *this;
666 | }
667 |
668 | template requires
669 | _any_object::can_be_stored_inplace_v> &&
670 | (!std::is_same_v>)
671 | type& operator=(T&& value) noexcept {
672 | if (_vtable) {
673 | auto* destroy = _vtable->template get();
674 | destroy(detail::_destroy_cpo{ }, &_storage);
675 | }
676 | using value_type = std::remove_cvref_t;
677 | ::new (static_cast(&_storage)) value_type(static_cast(value));
678 | _vtable = vtable_t::template inst();
679 | return *this;
680 | }
681 |
682 | template requires
683 | (!_any_object::can_be_stored_inplace_v>) &&
684 | (!std::is_same_v>)
685 | type& operator=(T&& value) noexcept {
686 | if (_vtable) {
687 | auto* destroy = _vtable->template get();
688 | destroy(detail::_destroy_cpo{ }, &_storage);
689 | }
690 | using value_type = detail::any_heap_allocated_storage, DefaultAllocator, CPOs...>;
691 | ::new (static_cast(&_storage)) value_type(
692 | std::allocator_arg,
693 | DefaultAllocator { },
694 | std::in_place_type>,
695 | static_cast(value)
696 | );
697 | _vtable = vtable_t::template inst();
698 | return *this;
699 | }
700 |
701 | explicit operator bool() const noexcept {
702 | return _vtable != nullptr;
703 | }
704 |
705 | private:
706 | friend const vtable_t* get_vtable(const type& self) noexcept {
707 | return self._vtable;
708 | }
709 |
710 | friend void* get_object_address(const type& self) noexcept {
711 | return const_cast(static_cast(&self._storage));
712 | }
713 | };
714 |
715 | //
716 | // _any_function_t
717 | //
718 |
719 | template
720 | class _any_function_t;
721 |
722 | template
723 | class _any_function_t :
724 | public _any_object, CPOs...>::type
725 | {
726 | using invoke_cpo = detail::_invoke_cpo;
727 | using base_t = typename _any_object<
728 | InlineSize,
729 | DefaultAllocator,
730 | IsCopyable,
731 | detail::_invoke_cpo,
732 | CPOs...
733 | >::type;
734 |
735 | public:
736 | using result_type = R;
737 |
738 | using base_t::base_t; // use constructors from base
739 |
740 | R operator()(Args... arg) const noexcept {
741 | auto* invoke = get_vtable(*this)->template get();
742 | return invoke(invoke_cpo { }, get_object_address(*this), (Args&&)arg...);
743 | }
744 | };
745 |
746 | //
747 | // basic_any / basic_any_copyable
748 | //
749 |
750 | template
751 | using basic_any_copyable_t = typename _any_object::type;
752 |
753 | template
754 | using basic_any_t = typename _any_object::type;
755 |
756 | template
757 | using basic_any_copyable = basic_any_copyable_t...>;
758 |
759 | template
760 | using basic_any = basic_any_t...>;
761 |
762 | //
763 | // any / any_copyable
764 | //
765 |
766 | template
767 | using any_copyable_t = basic_any_copyable_t<4 * sizeof(void*), std::allocator, CPOs...>;
768 |
769 | template
770 | using any_copyable = any_copyable_t...>;
771 |
772 | template
773 | using any_t = basic_any_t<4 * sizeof(void*), std::allocator, CPOs...>;
774 |
775 | template