26#if JUCE_ENABLE_ALLOCATION_HOOKS
27#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE const UnitTestAllocationChecker checker (*this)
29#define JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
39 auto tie() const noexcept {
return std::tie (constructions, copies, moves, calls, destructions); }
42 int constructions = 0;
48 ConstructCounts withConstructions (
int i)
const noexcept {
auto c = *
this; c.constructions = i;
return c; }
49 ConstructCounts withCopies (
int i)
const noexcept {
auto c = *
this; c.copies = i;
return c; }
50 ConstructCounts withMoves (
int i)
const noexcept {
auto c = *
this; c.moves = i;
return c; }
51 ConstructCounts withCalls (
int i)
const noexcept {
auto c = *
this; c.calls = i;
return c; }
52 ConstructCounts withDestructions (
int i)
const noexcept {
auto c = *
this; c.destructions = i;
return c; }
54 bool operator== (
const ConstructCounts& other)
const noexcept {
return tie() == other.tie(); }
55 bool operator!= (
const ConstructCounts& other)
const noexcept {
return tie() != other.tie(); }
58String& operator<< (
String& str,
const ConstructCounts& c)
60 return str <<
"{ constructions: " << c.constructions
61 <<
", copies: " << c.copies
62 <<
", moves: " << c.moves
63 <<
", calls: " << c.calls
64 <<
", destructions: " << c.destructions
68class FixedSizeFunctionTest final :
public UnitTest
70 static void toggleBool (
bool& b) { b = ! b; }
72 struct ConstructCounter
74 explicit ConstructCounter (ConstructCounts& countsIn)
75 : counts (countsIn) {}
77 ConstructCounter (
const ConstructCounter& c)
83 ConstructCounter (ConstructCounter&& c) noexcept
89 ~ConstructCounter() noexcept { counts.destructions += 1; }
91 void operator()() const noexcept { counts.calls += 1; }
93 ConstructCounts& counts;
97 FixedSizeFunctionTest()
98 : UnitTest (
"Fixed Size Function", UnitTestCategories::containers)
101 void runTest()
override
103 beginTest (
"Can be constructed and called from a lambda");
105 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
107 const auto result = 5;
108 bool wasCalled =
false;
109 const auto lambda = [&] { wasCalled =
true;
return result; };
111 const FixedSizeFunction<
sizeof (lambda),
int()> fn (lambda);
112 const auto out = fn();
115 expectEquals (result, out);
118 beginTest (
"void fn can be constructed from function with return value");
120 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
122 bool wasCalled =
false;
123 const auto lambda = [&] { wasCalled =
true;
return 5; };
124 const FixedSizeFunction<
sizeof (lambda),
void()> fn (lambda);
130 beginTest (
"Can be constructed and called from a function pointer");
132 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
136 const FixedSizeFunction<
sizeof (
void*),
void (
bool&)> fn (toggleBool);
148 beginTest (
"Default constructed functions throw if called");
150 const auto a = FixedSizeFunction<8, void()>();
151 expectThrowsType (a(), std::bad_function_call)
153 const auto b = FixedSizeFunction<8, void()> (
nullptr);
154 expectThrowsType (b(), std::bad_function_call)
157 beginTest (
"Functions can be moved");
159 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
161 ConstructCounts counts;
163 auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (ConstructCounter { counts });
164 expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1));
167 expectEquals (counts, ConstructCounts().withMoves (1).withDestructions (1).withCalls (1));
169 const auto b = std::move (a);
170 expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (1));
173 expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (2));
176 expectEquals (counts, ConstructCounts().withMoves (2).withDestructions (1).withCalls (3));
179 beginTest (
"Functions are destructed properly");
181 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
183 ConstructCounts counts;
184 const ConstructCounter toCopy { counts };
187 auto a = FixedSizeFunction<sizeof (ConstructCounter), void()> (toCopy);
188 expectEquals (counts, ConstructCounts().withCopies (1));
191 expectEquals (counts, ConstructCounts().withCopies (1).withDestructions (1));
194 beginTest (
"Avoid destructing functions that fail to construct");
196 struct BadConstructor
198 explicit BadConstructor (ConstructCounts& c)
201 counts.constructions += 1;
202 throw std::runtime_error {
"this was meant to happen" };
205 BadConstructor (
const BadConstructor&) =
default;
206 BadConstructor& operator= (
const BadConstructor&) =
delete;
208 ~BadConstructor() noexcept { counts.destructions += 1; }
210 void operator()() const noexcept { counts.calls += 1; }
212 ConstructCounts& counts;
215 ConstructCounts counts;
217 expectThrowsType ((FixedSizeFunction<
sizeof (BadConstructor),
void()> (BadConstructor { counts })),
220 expectEquals (counts, ConstructCounts().withConstructions (1));
223 beginTest (
"Equality checks work");
225 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
227 FixedSizeFunction<8, void()> a;
229 expect (a ==
nullptr);
230 expect (
nullptr == a);
231 expect (! (a !=
nullptr));
232 expect (! (
nullptr != a));
234 FixedSizeFunction<8, void()> b ([] {});
236 expect (b !=
nullptr);
237 expect (
nullptr != b);
238 expect (! (b ==
nullptr));
239 expect (! (
nullptr == b));
242 beginTest (
"Functions can be cleared");
244 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
246 FixedSizeFunction<8, void()> fn ([] {});
250 expect (!
bool (fn));
253 beginTest (
"Functions can be assigned");
255 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
257 using Fn = FixedSizeFunction<8, void()>;
267 x = [&] { numCallsA += 1; };
268 y = [&] { numCallsB += 1; };
273 expectEquals (numCallsA, 1);
274 expectEquals (numCallsB, 0);
277 expectEquals (numCallsA, 1);
278 expectEquals (numCallsB, 1);
281 expectEquals (numCallsA, 1);
282 expectEquals (numCallsB, 1);
285 expectEquals (numCallsA, 1);
286 expectEquals (numCallsB, 2);
289 beginTest (
"Functions may mutate internal state");
291 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
293 using Fn = FixedSizeFunction<64, void()>;
299 x = [&numCalls, counter = 0]()
mutable { counter += 1; numCalls = counter; };
302 expectEquals (numCalls, 0);
305 expectEquals (numCalls, 1);
308 expectEquals (numCalls, 2);
311 beginTest (
"Functions can sink move-only parameters");
313 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
315 using FnA = FixedSizeFunction<64, int (std::unique_ptr<int>)>;
318 auto ptr = std::make_unique<int> (value);
320 FnA fnA = [] (std::unique_ptr<int> p) {
return *p; };
322 expect (value == fnA (std::move (ptr)));
324 using FnB = FixedSizeFunction<64, void (std::unique_ptr<int>&&)>;
326 FnB fnB = [&value] (std::unique_ptr<int>&& p)
328 auto x = std::move (p);
332 const auto newValue = 10;
333 fnB (std::make_unique<int> (newValue));
334 expect (value == newValue);
337 beginTest (
"Functions be converted from smaller functions");
339 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE;
341 using SmallFn = FixedSizeFunction<20, void()>;
342 using LargeFn = FixedSizeFunction<21, void()>;
344 bool smallCalled =
false;
345 bool largeCalled =
false;
347 SmallFn small = [&smallCalled, a = std::array<char, 8>{}] { smallCalled =
true; ignoreUnused (a); };
348 LargeFn large = [&largeCalled, a = std::array<char, 8>{}] { largeCalled =
true; ignoreUnused (a); };
350 large = std::move (small);
354 expect (smallCalled);
355 expect (! largeCalled);
360FixedSizeFunctionTest fixedSizedFunctionTest;
364#undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE