Spicy
lambda.h
1 // Copyright (c) 2020 by Ard Baxter. See LICENSE.3rdparty for details.
2 
3 #pragma once
4 
5 #include <cassert>
6 
7 // Code in this namespace is taken from Ard Baxter's article "C++11 Lambda Storage Without libc++",
8 // https://www.codeproject.com/Articles/313312/Cplusplus11-Lambda-Storage-Without-libcplusplus and
9 // licensed under the Code Project Open License 1.02.
10 
11 // LambdaExecutor is an
12 // internal class that adds the ability to execute to Lambdas. This functionality is separated because it is the
13 // only thing that needed to be specialized (by return type).
14 
15 // generateExecutor or receiveExecutor must be called after constructing,
16 // before use
17 template<typename T>
18 class LambdaExecutor {};
19 
20 template<typename Out, typename... In>
21 class LambdaExecutor<Out(In...)> {
22 public:
23  Out operator()(In... in) {
24  assert(lambda != nullptr);
25  return executeLambda(lambda, in...);
26  }
27 
28 protected:
29  LambdaExecutor(void*& lambda) : lambda(lambda) {}
30 
31  ~LambdaExecutor() {}
32 
33  template<typename T>
34  void generateExecutor(T const& lambda) {
35  executeLambda = [](void* lambda, In... arguments) -> Out {
36  return (reinterpret_cast<T*>(lambda))->operator()(arguments...);
37  };
38  }
39 
40  void receiveExecutor(LambdaExecutor<Out(In...)> const& other) { executeLambda = other.executeLambda; }
41 
42 private:
43  void*& lambda;
44  Out (*executeLambda)(void*, In...);
45 };
46 
47 template<typename... In>
48 class LambdaExecutor<void(In...)> {
49 public:
50  void operator()(In... in) {
51  assert(lambda != nullptr);
52  executeLambda(lambda, in...);
53  }
54 
55 protected:
56  LambdaExecutor(void*& lambda) : lambda(lambda) {}
57 
58  ~LambdaExecutor() {}
59 
60  template<typename T>
61  void generateExecutor(T const& lambda) {
62  executeLambda = [](void* lambda, In... arguments) {
63  return (reinterpret_cast<T*>(lambda))->operator()(arguments...);
64  };
65  }
66 
67  void receiveExecutor(LambdaExecutor<void(In...)> const& other) { executeLambda = other.executeLambda; }
68 
69 private:
70  void*& lambda;
71  void (*executeLambda)(void*, In...);
72 };
73 
74 // Lambda contains most of the lambda management code and can be used
75 // directly in external code.
76 template<typename T>
77 class Lambda {};
78 
79 template<typename Out, typename... In>
80 class Lambda<Out(In...)> : public LambdaExecutor<Out(In...)> {
81 public:
82  Lambda() : LambdaExecutor<Out(In...)>(lambda), lambda(nullptr), deleteLambda(nullptr), copyLambda(nullptr) {}
83 
84  Lambda(Lambda<Out(In...)> const& other)
85  : LambdaExecutor<Out(In...)>(lambda),
86  lambda(other.copyLambda ? other.copyLambda(other.lambda) : nullptr),
87  deleteLambda(other.deleteLambda),
88  copyLambda(other.copyLambda) {
89  this->receiveExecutor(other);
90  }
91 
92  template<typename T>
93  Lambda(T const& lambda) : LambdaExecutor<Out(In...)>(this->lambda), lambda(nullptr) {
94  // Copy should set all variables
95  copy_(lambda);
96  }
97 
98  ~Lambda() {
99  if ( deleteLambda != nullptr )
100  deleteLambda(lambda);
101 
102  // Seems clang-tidy doesn't track through the reinterpret_cast inside the deleter.
103  } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
104 
105  Lambda<Out(In...)>& operator=(Lambda<Out(In...)> const& other) {
106  this->lambda = other.copyLambda ? other.copyLambda(other.lambda) : nullptr;
107  this->receiveExecutor(other);
108  this->deleteLambda = other.deleteLambda;
109  this->copyLambda = other.copyLambda;
110  return *this;
111  }
112 
113  template<typename T>
114  Lambda<Out(In...)>& operator=(T const& lambda) {
115  copy_(lambda);
116  return *this;
117  }
118 
119  operator bool() { return lambda != nullptr; }
120 
121 private:
122  template<typename T>
123  void copy_(T const& lambda) {
124  if ( this->lambda != nullptr )
125  deleteLambda(this->lambda);
126  this->lambda = new T(lambda);
127 
128  this->generateExecutor(lambda);
129 
130  deleteLambda = [](void* lambda) { delete reinterpret_cast<T*>(lambda); };
131 
132  copyLambda = [](void* lambda) -> void* { return lambda ? new T(*(T*)lambda) : nullptr; };
133  }
134 
135  void* lambda;
136  void (*deleteLambda)(void*);
137  void* (*copyLambda)(void*);
138 };
139 
140 // namespace lambda
Definition: lambda.h:18
Definition: lambda.h:77