CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
function_wrapper.hh
1#include <cdefs.h>
2#include <functional>
3#include <tuple>
4
5/**
6 * Base template for `FunctionWrapper`, never used.
7 */
8template<typename FnType>
10
11/**
12 * A non-owning type-erased reference to a callable object. This is used
13 * to pass lambdas (and similar) down the stack without increasing code
14 * size by template specialisation. Instances of this class must not be
15 * stored, they should be used only for passing type-erased callable objects
16 * down the stack.
17 *
18 * Instances of this class are two words: a reference to the lambda, and a
19 * wrapper callback that invokes the lambda.
20 *
21 * This is similar to `std::function` but is non-owning and so is guaranteed
22 * not to allocate memory (the called function may capture memory).
23 */
24template<class R, class... Args>
25class FunctionWrapper<R(Args...)>
26{
27 /**
28 * Storage for the type-erased function. This holds the reference to
29 * lambda and to the invoke function.
30 */
31 alignas(void *) char storage[2 * sizeof(void *)];
32
33 /**
34 * Base type for the type-erased function. This defines the virtual
35 * function that is used to invoke the captured lambda.
36 */
37 struct ErasedFunctionWrapperBase
38 {
39 virtual R operator()(Args... args) = 0;
40 };
41
42 /**
43 * Returns a pointer to the storage, cast to the type-erased function
44 * type.
45 */
46 ErasedFunctionWrapperBase &stored_function()
47 {
48 return *reinterpret_cast<ErasedFunctionWrapperBase *>(storage);
49 }
50
51 /**
52 * Templated subclass that is specialised for each concrete callable
53 * type `T` that is passed. One instance of this will be created for
54 * each lambda type, with a single method in its vtable that invokes
55 * the lambda.
56 */
57 template<typename T>
58 class ErasedFunctionWrapper : public ErasedFunctionWrapperBase
59 {
60 /// Pointer to the captured lambda.
61 T &&fn;
62
63 public:
64 /**
65 * Invoke function. This is virtual and overrides the version in
66 * the parent class, allowing this to be called from code that does
67 * not know the cocrete type of the lambda.
68 */
69 R operator()(Args... args) override
70 {
71 return fn(std::forward<Args>(args)...);
72 }
73
74 /**
75 * Construct the type-erased function wrapper, capturing the
76 * lambda.
77 */
78 ErasedFunctionWrapper(T &&fn) : fn{std::forward<T>(fn)} {}
79 };
80
81 public:
82 /**
83 * This is a non-owning reference, its copy constructor is safe to use for
84 * passing down the stack.
85 */
88 FunctionWrapper &operator=(FunctionWrapper &&) = delete;
89
90 /**
91 * Construct the type-erased function wrapper, capturing the lambda.
92 */
93 template<typename T>
94 __always_inline FunctionWrapper(T &&fn)
95 requires(!std::is_same_v<T, FunctionWrapper>)
96 {
97 // Make sure that we got the size for the storage right!
98 static_assert(sizeof(storage) >= sizeof(ErasedFunctionWrapper<T>));
99 // Construct the type-erased function in place.
100 new (storage) ErasedFunctionWrapper<T>(std::forward<T>(fn));
101 }
102
103 /**
104 * Invoke the captured lambda.
105 */
106 __always_inline R operator()(Args... args)
107 {
108 return stored_function()(std::forward<Args>(args)...);
109 }
110};
FunctionWrapper(T &&fn)
Construct the type-erased function wrapper, capturing the lambda.
R operator()(Args... args)
Invoke the captured lambda.
FunctionWrapper(FunctionWrapper &)=default
This is a non-owning reference, its copy constructor is safe to use for passing down the stack.
Base template for FunctionWrapper, never used.