Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DelegateTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file DelegateTests.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #include <boost/test/data/test_case.hpp>
10 #include <boost/test/unit_test.hpp>
11 
15 
16 #include <memory>
17 #include <string>
18 #include <type_traits>
19 #include <utility>
20 #include <vector>
21 
22 using namespace Acts;
23 
24 namespace bd = boost::unit_test::data;
25 
26 BOOST_AUTO_TEST_SUITE(DelegateTests)
27 
28 int sumImpl(int a, int b) {
29  return a + b;
30 }
31 
32 BOOST_AUTO_TEST_CASE(ConnectConstexprLambda) {
34  BOOST_CHECK(!sum);
35  BOOST_CHECK(!sum.connected());
36 
37  sum.connect<&sumImpl>();
38 
39  BOOST_CHECK_EQUAL(sum(2, 5), 7);
40  BOOST_CHECK_NE(sum(2, 3), 7);
41 
42  sum.connect([](const void*, int a, int b) -> int { return a + b; });
43 
44  BOOST_CHECK(sum);
45  BOOST_CHECK(sum.connected());
46 
47  BOOST_CHECK_EQUAL(sum(2, 5), 7);
48  BOOST_CHECK_NE(sum(2, 3), 7);
49 }
50 
51 float multiply(float a, float b) {
52  return a * b;
53 }
54 
55 BOOST_AUTO_TEST_CASE(ConnectFunctionPointer) {
57 
58  BOOST_CHECK(!mult);
59  BOOST_CHECK(!mult.connected());
60 
61  mult.connect<multiply>();
62 
63  BOOST_CHECK(mult);
64  BOOST_CHECK(mult.connected());
65 
66  CHECK_CLOSE_REL(mult(2, 5.9), 2 * 5.9, 1e-6);
67  BOOST_CHECK_NE(mult(2, 3.2), 58.9);
68 }
69 
70 struct Subtractor {
71  int v;
72  int execute(int a) const { return a - v; }
73 };
74 
75 BOOST_AUTO_TEST_CASE(ConnectStruct) {
77 
78  BOOST_CHECK(!sub);
79  BOOST_CHECK(!sub.connected());
80 
81  Subtractor s{18};
82  sub.connect<&Subtractor::execute>(&s);
83 
84  BOOST_CHECK(sub);
85  BOOST_CHECK(sub.connected());
86 
87  BOOST_CHECK_EQUAL(sub(7), 7 - 18);
88 }
89 
90 int addition(const void* /*payload*/, int a, int b) {
91  return a + b;
92 }
93 
94 BOOST_AUTO_TEST_CASE(ConnectRuntime) {
95  {
97  BOOST_CHECK(!add);
98  BOOST_CHECK(!add.connected());
99 
100  add.connect(&addition);
101  BOOST_CHECK(add);
102  BOOST_CHECK(add.connected());
103 
104  BOOST_CHECK_EQUAL(add(4, 4), 8);
105  }
106 
107  {
109 
110  BOOST_CHECK(add);
111  BOOST_CHECK(add.connected());
112 
113  BOOST_CHECK_EQUAL(add(4, 4), 8);
114  }
115 
116  {
118  BOOST_CHECK(!add);
119  BOOST_CHECK(!add.connected());
120 
121  add = &addition;
122  BOOST_CHECK(add);
123  BOOST_CHECK(add.connected());
124 
125  BOOST_CHECK_EQUAL(add(4, 4), 8);
126  }
127 }
128 
129 void modify(int& v, int a) {
130  v = a;
131 }
132 
133 void noModify(int v, int a) {
134  (void)v;
135  v = a;
136 }
137 
138 BOOST_AUTO_TEST_CASE(DelegateReference) {
140  d.connect<&modify>();
141 
142  int v = 0;
143  d(v, 42);
144  BOOST_CHECK_EQUAL(v, 42);
145 
146  // This should not compile since the signature is not exactly matched
147  // d.connect<&noModify>();
148 }
149 
151  void modify(int& v, int a) const { v = a; }
152 
153  void noModify(int v, int a) const {
154  (void)v;
155  v = a;
156  }
157 };
158 
159 BOOST_AUTO_TEST_CASE(DelegateReferenceMember) {
162  d.connect<&SignatureTest::modify>(&s);
163 
164  int v = 0;
165  d(v, 42);
166  BOOST_CHECK_EQUAL(v, 42);
167 
168  // This should not compile since the signature is not exactly matched
169  // d.connect<&SignatureTest::noModify>(&s);
170 }
171 
172 BOOST_AUTO_TEST_CASE(StatefullLambdas) {
173  std::vector<int> v;
174 
175  auto lambda = [&](int n) -> int {
176  v.push_back(n);
177  return v.size();
178  };
179 
180  Delegate<int(int)> d(lambda);
181 
182  BOOST_CHECK(d);
183  BOOST_CHECK(d.connected());
184  BOOST_CHECK(d(2) == 1);
185 
186  d.disconnect();
187  d = lambda;
188 
189  BOOST_CHECK(d);
190  BOOST_CHECK(d.connected());
191  BOOST_CHECK(d(2) == 2);
192 
193  d.disconnect();
194  d.connect(lambda);
195 
196  BOOST_CHECK(d);
197  BOOST_CHECK(d.connected());
198  BOOST_CHECK(d(2) == 3);
199 
200  // This should not compile because of deleted && overloads
201  // d.connect([&](int a){ v.push_back(a); return v.size(); });
202 }
203 
205  CheckDestructor(bool* _out) : destructorCalled{_out} {}
206 
207  bool* destructorCalled;
208 
209  int func() const { return 4; }
210 
211  ~CheckDestructor() { (*destructorCalled) = true; }
212 };
213 
214 int owningTest() {
215  return 8;
216 }
217 
218 int owningTest2(const void* /*payload*/) {
219  return 8;
220 }
221 
222 BOOST_AUTO_TEST_CASE(OwningDelegateTest) {
223  {
224  auto s = std::make_unique<const SignatureTest>();
226  d.connect<&SignatureTest::modify>(std::move(s));
227 
228  int v = 0;
229  d(v, 42);
230  BOOST_CHECK_EQUAL(v, 42);
231  }
232 
233  {
234  bool destructorCalled = false;
235  auto s = std::make_unique<const CheckDestructor>(&destructorCalled);
236  {
237  BOOST_CHECK_EQUAL(destructorCalled, false);
239  BOOST_CHECK_EQUAL(destructorCalled, false);
240  d.connect<&CheckDestructor::func>(s.get());
241  BOOST_CHECK_EQUAL(destructorCalled, false);
243  BOOST_CHECK_EQUAL(d(), 4);
244  BOOST_CHECK_EQUAL(dCopy(), 4);
245  BOOST_CHECK_EQUAL(destructorCalled, false);
246  }
247  // destructor not called after non-owning delegate goes out of scope
248  BOOST_CHECK_EQUAL(destructorCalled, false);
249 
250  {
251  BOOST_CHECK_EQUAL(destructorCalled, false);
253  // This doesn't compile: owning delegate is not copyable
254  // Delegate<int(), DelegateType::Owning> dCopy = d;
255  BOOST_CHECK_EQUAL(destructorCalled, false);
256  // This doesn't compile: owning delegate cannot accept raw pointer
257  // instance
258  // d.connect<&CheckDestructor::func>(s.get());
259  d.connect<&CheckDestructor::func>(std::move(s));
260  BOOST_CHECK_EQUAL(destructorCalled, false);
261  BOOST_CHECK_EQUAL(d(), 4);
262  BOOST_CHECK_EQUAL(destructorCalled, false);
263  }
264  // destructor called after owning delegate goes out of scope
265  BOOST_CHECK_EQUAL(destructorCalled, true);
266 
267  destructorCalled = false;
268  s = std::make_unique<const CheckDestructor>(&destructorCalled);
269  {
270  BOOST_CHECK_EQUAL(destructorCalled, false);
272  // This doesn't compile: owning delegate is not copyable
273  // OwningDelegate<int()> dCopy = d;
274  BOOST_CHECK_EQUAL(destructorCalled, false);
275  d.connect<&CheckDestructor::func>(std::move(s));
276  BOOST_CHECK_EQUAL(destructorCalled, false);
277  BOOST_CHECK_EQUAL(d(), 4);
278  BOOST_CHECK_EQUAL(destructorCalled, false);
279  }
280  // destructor called after owning delegate goes out of scope
281  BOOST_CHECK_EQUAL(destructorCalled, true);
282  }
283 
284  {
285  bool destructorCalled = false;
286  auto s = std::make_unique<const CheckDestructor>(&destructorCalled);
287  {
288  BOOST_CHECK_EQUAL(destructorCalled, false);
290  BOOST_CHECK_EQUAL(destructorCalled, false);
291  d.connect<&CheckDestructor::func>(s.get());
293  BOOST_CHECK_EQUAL(destructorCalled, false);
294  BOOST_CHECK_EQUAL(d(), 4);
295  BOOST_CHECK_EQUAL(dCopy(), 4);
296  BOOST_CHECK_EQUAL(destructorCalled, false);
297  d.disconnect();
298  BOOST_CHECK_EQUAL(destructorCalled, false);
299  }
300 
301  {
302  BOOST_CHECK_EQUAL(destructorCalled, false);
304  // This doesn't compile: owning delegate is not copyable
305  // Delegate<int(), DelegateType::Owning> dCopy = d;
306  BOOST_CHECK_EQUAL(destructorCalled, false);
307  // This doesn't compile: owning delegate cannot accept raw pointer
308  // instance
309  // d.connect<&CheckDestructor::func>(s.get());
310  d.connect<&CheckDestructor::func>(std::move(s));
311  BOOST_CHECK_EQUAL(destructorCalled, false);
312  BOOST_CHECK_EQUAL(d(), 4);
313  BOOST_CHECK_EQUAL(destructorCalled, false);
314  d.disconnect();
315  BOOST_CHECK_EQUAL(destructorCalled, true);
316  }
317  // destructor called after owning delegate goes out of scope
318  BOOST_CHECK_EQUAL(destructorCalled, true);
319  }
320 
321  {
323  d.connect<&owningTest>();
324  BOOST_CHECK_EQUAL(d(), 8);
325 
326  d.disconnect();
327  d.connect<&owningTest>();
328  BOOST_CHECK_EQUAL(d(), 8);
329 
330  d.disconnect();
331  d.connect(owningTest2);
332  BOOST_CHECK_EQUAL(d(), 8);
333  }
334 }
335 
337  DelegateInterface() = default;
338  virtual ~DelegateInterface() = 0;
339 
340  virtual std::string func() const { return "base"; }
341 };
342 inline DelegateInterface::~DelegateInterface() = default;
343 
345  std::string func() const final { return "derived"; }
346 };
347 
349  std::string func() const { return "separate"; }
350 };
351 
352 BOOST_AUTO_TEST_CASE(NonVoidDelegateTest) {
353  // check void behavior with virtuals
354  {
357  d.connect<&ConcreteDelegate::func>(&c);
358  BOOST_CHECK_EQUAL(d(), "derived");
359 
360  // does not compile: delegate won't hand out void pointer
361  // d.instance();
362  }
363  {
366  d.connect<&DelegateInterface::func>(&c);
367  BOOST_CHECK_EQUAL(
368  d(), "derived"); // <- even if you plug in the base class member
369  // pointer you get the derived class call
370  }
371 
372  {
375  d.connect<&ConcreteDelegate::func>(&c);
376  BOOST_CHECK_EQUAL(d(), "derived");
377 
378  const auto* instance = d.instance();
379  static_assert(
380  std::is_same_v<
381  std::remove_const_t<std::remove_pointer_t<decltype(instance)>>,
383  "Did not get correct instance pointer");
384  BOOST_CHECK_NE(dynamic_cast<const DelegateInterface*>(instance), nullptr);
385  BOOST_CHECK_NE(dynamic_cast<const ConcreteDelegate*>(instance), nullptr);
386  }
387 
388  {
391  d.connect<&ConcreteDelegate::func>(&c);
392  BOOST_CHECK_EQUAL(d(), "derived");
393 
394  const auto* instance = d.instance();
395  static_assert(
396  std::is_same_v<
397  std::remove_const_t<std::remove_pointer_t<decltype(instance)>>,
399  "Did not get correct instance pointer");
400  BOOST_CHECK_NE(dynamic_cast<const DelegateInterface*>(instance), nullptr);
401  BOOST_CHECK_NE(dynamic_cast<const ConcreteDelegate*>(instance), nullptr);
402  }
403 
404  {
406  d.connect<&ConcreteDelegate::func>(
407  std::make_unique<const ConcreteDelegate>());
408  BOOST_CHECK_EQUAL(d(), "derived");
409 
410  const auto* instance = d.instance();
411  static_assert(
412  std::is_same_v<
413  std::remove_const_t<std::remove_pointer_t<decltype(instance)>>,
415  "Did not get correct instance pointer");
416  BOOST_CHECK_NE(dynamic_cast<const DelegateInterface*>(instance), nullptr);
417  BOOST_CHECK_NE(dynamic_cast<const ConcreteDelegate*>(instance), nullptr);
418  }
419 
420  {
423  d.connect<&ConcreteDelegate::func>(
424  std::make_unique<const ConcreteDelegate>());
425  BOOST_CHECK_EQUAL(d(), "derived");
426 
427  const auto* instance = d.instance();
428  static_assert(
429  std::is_same_v<
430  std::remove_const_t<std::remove_pointer_t<decltype(instance)>>,
432  "Did not get correct instance pointer");
433  BOOST_CHECK_NE(dynamic_cast<const DelegateInterface*>(instance), nullptr);
434  BOOST_CHECK_NE(dynamic_cast<const ConcreteDelegate*>(instance), nullptr);
435  }
436 
437  {
440  // Does not compile: cannot assign unrelated type
441  // d.connect<&SeparateDelegate::func>(&c);
442  (void)d;
443  (void)c;
444  }
445 
447 }
448 
449 BOOST_AUTO_TEST_SUITE_END()