Lodestar
An integrated real-time control package in C++
Signal.hpp
1 //
2 // Created by Hamza El-Kebir on 12/22/21.
3 //
4 
5 #ifndef LODESTAR_SIGNAL_HPP
6 #define LODESTAR_SIGNAL_HPP
7 
8 #include <type_traits>
9 #include <iostream>
10 #include <tuple>
11 #include <cassert>
12 
13 #include "SignalBase.hpp"
14 #include "BlockProto.hpp"
15 #include "Lodestar/GlobalConstants.hpp"
16 #include "Lodestar/aux/DynamicData.hpp"
17 #include "Lodestar/aux/TemplateTraits.hpp"
18 
19 namespace ls {
20  namespace blocks {
21  template<typename TObject = void *, typename TIndicated = TObject>
22  class Signal : public SignalBase {
23  public:
24  using Object = TObject;
25  using type = TIndicated;
26 
27 
28  // static Signal<TObject> withBlockIdx(int blockId)
29  // {
30  // auto s = Signal<TObject>{};
31  // s.blockId = blockId;
32  //
33  // return s;
34  // }
35 
36  Signal() : object{}, SignalBase(next()) {};
37 
38  // Signal(Object obj) : object(obj) {};
39 
40  Signal(const TObject &obj) : object(obj), SignalBase(next()) {};
41 
42  // Signal(Object *obj) : object(*obj) {};
43 
44  Signal(const TObject *obj) : object(*obj), SignalBase(next()) {};
45 
46  unsigned int id() const
47  {
48  return id_;
49  }
50 
51  operator TObject() const
52  {
53  return object;
54  }
55 
56  operator const TObject *() const
57  {
58  return &object;
59  }
60 
61  operator TObject *()
62  {
63  return &object;
64  }
65 
66  Signal<TObject>& operator=(const TObject &obj)
67  {
68  object = obj;
69  propagate();
70  return *this;
71  }
72 
73  Signal<TObject>& operator=(const Signal<TObject> &signal)
74  {
75  object = signal.object;
76  propagate();
77  return *this;
78  }
79 
80  template <typename TObject2, typename std::enable_if<std::is_convertible<TObject2, TObject>::value, TObject2>::type* = nullptr>
81  Signal<TObject>& operator=(const TObject2 &obj)
82  {
83  object = obj;
84  propagate();
85  return *this;
86  }
87 
88  template <typename TObject2, typename std::enable_if<!std::is_convertible<TObject2, TObject>::value, TObject2>::type* = nullptr>
89  Signal<Object>& operator=(const TObject2 &obj)
90  {
91  static_assert(std::is_convertible<TObject2, TObject>::value, "Signal object type is not convertible.");
92  return *this;
93  }
94 
95  template <typename TObject2, typename std::enable_if<std::is_convertible<TObject2, TObject>::value, TObject2>::type* = nullptr>
96  Signal<TObject>& operator=(const Signal<TObject2> &signal)
97  {
98  object = signal.object;
99  propagate();
100  return *this;
101  }
102 
103  template <typename TObject2, typename std::enable_if<!std::is_convertible<TObject2, TObject>::value, TObject2>::type* = nullptr>
104  Signal<Object>& operator=(const Signal<TObject2> &signal)
105  {
106  static_assert(std::is_convertible<TObject2, TObject>::value, "Signal object type is not convertible.");
107  return *this;
108  }
109 
110  template<typename TObject1, typename TObject2>
111  friend typename std::enable_if<std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
112  operator+(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2);
113 
114  template<typename TObject1, typename TObject2>
115  friend typename std::enable_if<!std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
116  operator+(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2);
117 
118  template<typename TObject1, typename TObject2>
119  friend typename std::enable_if<std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
120  operator-(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2);
121 
122  template<typename TObject1, typename TObject2>
123  friend typename std::enable_if<!std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
124  operator-(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2);
125 
126  template<typename TObject1, typename TObject2>
127  friend typename std::enable_if<std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
128  operator*(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2);
129 
130  template<typename TObject1, typename TObject2>
131  friend typename std::enable_if<!std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
132  operator*(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2);
133 
134  template<typename TObject1, typename TObject2>
135  friend typename std::enable_if<std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
136  operator/(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2);
137 
138  template<typename TObject1, typename TObject2>
139  friend typename std::enable_if<!std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
140  operator/(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2);
141 
142  // TODO: Implement operator==, !=, <, etc. Feed-through to corresponding TObject methods.
143 
144  bool isDynamicData() const
145  {
146  return std::is_same<TObject, ls::aux::DynamicData>::value;
147  }
148 
149  template <typename TInstance, typename TShadow = TObject>
150  typename std::enable_if<!std::is_same<TShadow, ls::aux::DynamicData>::value, bool>::type isType() const
151  {
152  return std::is_same<TInstance, TObject>::value;
153  }
154 
155  template <typename TInstance, typename TShadow = TObject>
156  typename std::enable_if<std::is_same<TShadow, ls::aux::DynamicData>::value, bool>::type isType() const
157  {
158  return object.data.template is<TInstance>();
159  }
160 
161  template <typename TInstance>
162  static bool isSignal()
163  {
165  }
166 
167  template <typename TInstance>
168  static bool isSignal(const TInstance &)
169  {
171  }
172 
173  bool connect(Signal<TObject> * other)
174  {
175  // If self is input, no connections can be made.
176  if (isInput)
177  return false;
178 
179  // If other is not input, no connections can be made.
180  if (!other->isInput)
181  return false;
182 
183  SignalBase::connectionPtrs.insert(other);
185 
186  other->connectionPtrs.insert(this);
187  other->connectionNumber++;
188 
189  static_assert(other->connectionNumber < 2, "There cannot be multiple connections to an input slot.");
190 
191  return true;
192  }
193 
194  bool connect(Signal<TObject> & other)
195  {
196  // If self is input, no connections can be made.
197  if (isInput)
198  return false;
199 
200  // If other is not input, no connections can be made.
201  if (!other.isInput)
202  return false;
203 
204  SignalBase::connectionPtrs.insert(&other);
206 
207  other.connectionPtrs.insert(this);
208  other.connectionNumber++;
209 
210  assert(other.connectionNumber < 2 && "There cannot be multiple connections to an input slot.");
211 
212  return true;
213  }
214 
215  bool disconnect(Signal<TObject> * other)
216  {
217  auto res1 = SignalBase::connectionPtrs.erase(other);
218  auto res2 = other->connectionPtrs.erase(this);
219 
220  if ((res1 == 1) && (res2 == 1)) {
222  other->connectionNumber--;
223 
224  return true;
225  }
226 
227  return false;
228  }
229 
230  bool disconnect(Signal<TObject> & other)
231  {
232  auto res1 = SignalBase::connectionPtrs.erase(&other);
233  auto res2 = other.connectionPtrs.erase(this);
234 
235  if ((res1 == 1) && (res2 == 1)) {
237  other.connectionNumber--;
238 
239  return true;
240  }
241 
242  return false;
243  }
244 
245  void propagate()
246  {
247  if (!isInput)
248  for (int i = 0; i < connectionNumber; i++)
249  static_cast<Signal<TObject>*>(getConnection(i))->operator=(object);
250  }
251 
252  template <typename TInstance>
253  static typename std::enable_if<ls::aux::TemplateTraits::isInstance<TInstance, Signal>::value, TInstance>::type toSignal(const TInstance &obj)
254  {
255  return obj;
256  }
257 
258  template <typename TInstance>
259  static typename std::enable_if<!ls::aux::TemplateTraits::isInstance<TInstance, Signal>::value, Signal<TInstance>>::type toSignal(const TInstance &obj)
260  {
261  return Signal<TInstance>{obj};
262  }
263 
264  Object object;
265  };
266 
267  template<typename TObject1, typename TObject2>
268  typename std::enable_if<std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
269  operator+(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2)
270  {
271  TObject1 obj = signal1.object + signal2.object;
272  return Signal<TObject1>{obj};
273  }
274 
275  template<typename TObject1, typename TObject2>
276  typename std::enable_if<!std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
277  operator+(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2)
278  {
279  static_assert(std::is_convertible<TObject1, TObject2>::value, "Signal object type is not convertible.");
280  return signal1;
281  }
282 
283  template<typename TObject1, typename TObject2>
284  typename std::enable_if<std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
285  operator-(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2)
286  {
287  TObject1 obj = signal1.object - signal2.object;
288  return Signal<TObject1>{obj};
289  }
290 
291  template<typename TObject1, typename TObject2>
292  typename std::enable_if<!std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
293  operator-(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2)
294  {
295  static_assert(std::is_convertible<TObject1, TObject2>::value, "Signal object type is not convertible.");
296  return signal1;
297  }
298 
299  template<typename TObject1, typename TObject2>
300  typename std::enable_if<std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
301  operator*(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2)
302  {
303  TObject1 obj = signal1.object * signal2.object;
304  return Signal<TObject1>{obj};
305  }
306 
307  template<typename TObject1, typename TObject2>
308  typename std::enable_if<!std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
309  operator*(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2)
310  {
311  static_assert(std::is_convertible<TObject1, TObject2>::value, "Signal object type is not convertible.");
312  return signal1;
313  }
314 
315  template<typename TObject1, typename TObject2>
316  typename std::enable_if<std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
317  operator/(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2)
318  {
319  TObject1 obj = signal1.object / signal2.object;
320  return Signal<TObject1>{obj};
321  }
322 
323  template<typename TObject1, typename TObject2>
324  typename std::enable_if<!std::is_convertible<TObject1, TObject2>::value, Signal<TObject1>>::type
325  operator/(const Signal<TObject1> &signal1, const Signal<TObject2> &signal2)
326  {
327  static_assert(std::is_convertible<TObject1, TObject2>::value, "Signal object type is not convertible.");
328  return signal1;
329  }
330 
331  template <typename TObject>
332  static std::ostream& operator<<(std::ostream& os, const Signal<TObject>& signal)
333  {
334  os << (TObject) signal;
335  return os;
336  }
337  }
338 }
339 
340 
341 #endif //LODESTAR_SIGNAL_HPP
ls::blocks::SignalBase::next
static unsigned int next()
Definition: SignalBase.hpp:28
ls
Main Lodestar code.
Definition: BilinearTransformation.hpp:12
ls::blocks::Signal
Definition: Signal.hpp:22
ls::blocks::SignalBase::connectionPtrs
::std::set< SignalBase * > connectionPtrs
Number of connections the SignalBase is currently engaged in.
Definition: SignalBase.hpp:53
ls::aux::TemplateTraits::isInstance
Definition: TemplateTraits.hpp:22
ls::blocks::SignalBase::connectionNumber
int connectionNumber
True if the signal is an input, false otherwise.
Definition: SignalBase.hpp:51
ls::blocks::SignalBase::isInput
bool isInput
Block index (global counter).
Definition: SignalBase.hpp:49
ls::blocks::SignalBase
Definition: SignalBase.hpp:16