2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
23 ==============================================================================
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
41 auto tie() const noexcept
{ return std::tie (constructions
, copies
, moves
, calls
, destructions
); }
44 int constructions
= 0;
50 ConstructCounts
withConstructions (int i
) const noexcept
{ auto c
= *this; c
.constructions
= i
; return c
; }
51 ConstructCounts
withCopies (int i
) const noexcept
{ auto c
= *this; c
.copies
= i
; return c
; }
52 ConstructCounts
withMoves (int i
) const noexcept
{ auto c
= *this; c
.moves
= i
; return c
; }
53 ConstructCounts
withCalls (int i
) const noexcept
{ auto c
= *this; c
.calls
= i
; return c
; }
54 ConstructCounts
withDestructions (int i
) const noexcept
{ auto c
= *this; c
.destructions
= i
; return c
; }
56 bool operator== (const ConstructCounts
& other
) const noexcept
{ return tie() == other
.tie(); }
57 bool operator!= (const ConstructCounts
& other
) const noexcept
{ return tie() != other
.tie(); }
60 String
& operator<< (String
& str
, const ConstructCounts
& c
)
62 return str
<< "{ constructions: " << c
.constructions
63 << ", copies: " << c
.copies
64 << ", moves: " << c
.moves
65 << ", calls: " << c
.calls
66 << ", destructions: " << c
.destructions
70 class FixedSizeFunctionTest
: public UnitTest
72 static void toggleBool (bool& b
) { b
= ! b
; }
74 struct ConstructCounter
76 explicit ConstructCounter (ConstructCounts
& countsIn
)
77 : counts (countsIn
) {}
79 ConstructCounter (const ConstructCounter
& c
)
85 ConstructCounter (ConstructCounter
&& c
) noexcept
91 ~ConstructCounter() noexcept
{ counts
.destructions
+= 1; }
93 void operator()() const noexcept
{ counts
.calls
+= 1; }
95 ConstructCounts
& counts
;
99 FixedSizeFunctionTest()
100 : UnitTest ("Fixed Size Function", UnitTestCategories::dsp
)
103 void runTest() override
105 beginTest ("Can be constructed and called from a lambda");
107 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
109 const auto result
= 5;
110 bool wasCalled
= false;
111 const auto lambda
= [&] { wasCalled
= true; return result
; };
113 const FixedSizeFunction
<sizeof (lambda
), int()> fn (lambda
);
114 const auto out
= fn();
117 expectEquals (result
, out
);
120 beginTest ("void fn can be constructed from function with return value");
122 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
124 bool wasCalled
= false;
125 const auto lambda
= [&] { wasCalled
= true; return 5; };
126 const FixedSizeFunction
<sizeof (lambda
), void()> fn (lambda
);
132 beginTest ("Can be constructed and called from a function pointer");
134 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
138 const FixedSizeFunction
<sizeof (void*), void (bool&)> fn (toggleBool
);
150 beginTest ("Default constructed functions throw if called");
152 const auto a
= FixedSizeFunction
<8, void()>();
153 expectThrowsType (a(), std::bad_function_call
)
155 const auto b
= FixedSizeFunction
<8, void()> (nullptr);
156 expectThrowsType (b(), std::bad_function_call
)
159 beginTest ("Functions can be moved");
161 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
163 ConstructCounts counts
;
165 auto a
= FixedSizeFunction
<sizeof (ConstructCounter
), void()> (ConstructCounter
{ counts
});
166 expectEquals (counts
, ConstructCounts().withMoves (1).withDestructions (1)); // The temporary gets destroyed
169 expectEquals (counts
, ConstructCounts().withMoves (1).withDestructions (1).withCalls (1));
171 const auto b
= std::move (a
);
172 expectEquals (counts
, ConstructCounts().withMoves (2).withDestructions (1).withCalls (1));
175 expectEquals (counts
, ConstructCounts().withMoves (2).withDestructions (1).withCalls (2));
178 expectEquals (counts
, ConstructCounts().withMoves (2).withDestructions (1).withCalls (3));
181 beginTest ("Functions are destructed properly");
183 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
185 ConstructCounts counts
;
186 const ConstructCounter toCopy
{ counts
};
189 auto a
= FixedSizeFunction
<sizeof (ConstructCounter
), void()> (toCopy
);
190 expectEquals (counts
, ConstructCounts().withCopies (1));
193 expectEquals (counts
, ConstructCounts().withCopies (1).withDestructions (1));
196 beginTest ("Avoid destructing functions that fail to construct");
198 struct BadConstructor
200 explicit BadConstructor (ConstructCounts
& c
)
203 counts
.constructions
+= 1;
204 throw std::runtime_error
{ "this was meant to happen" };
207 BadConstructor (const BadConstructor
&) = default;
208 BadConstructor
& operator= (const BadConstructor
&) = delete;
210 ~BadConstructor() noexcept
{ counts
.destructions
+= 1; }
212 void operator()() const noexcept
{ counts
.calls
+= 1; }
214 ConstructCounts
& counts
;
217 ConstructCounts counts
;
219 expectThrowsType ((FixedSizeFunction
<sizeof (BadConstructor
), void()> (BadConstructor
{ counts
})),
222 expectEquals (counts
, ConstructCounts().withConstructions (1));
225 beginTest ("Equality checks work");
227 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
229 FixedSizeFunction
<8, void()> a
;
231 expect (a
== nullptr);
232 expect (nullptr == a
);
233 expect (! (a
!= nullptr));
234 expect (! (nullptr != a
));
236 FixedSizeFunction
<8, void()> b ([] {});
238 expect (b
!= nullptr);
239 expect (nullptr != b
);
240 expect (! (b
== nullptr));
241 expect (! (nullptr == b
));
244 beginTest ("Functions can be cleared");
246 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
248 FixedSizeFunction
<8, void()> fn ([] {});
252 expect (! bool (fn
));
255 beginTest ("Functions can be assigned");
257 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
259 using Fn
= FixedSizeFunction
<8, void()>;
269 x
= [&] { numCallsA
+= 1; };
270 y
= [&] { numCallsB
+= 1; };
275 expectEquals (numCallsA
, 1);
276 expectEquals (numCallsB
, 0);
279 expectEquals (numCallsA
, 1);
280 expectEquals (numCallsB
, 1);
283 expectEquals (numCallsA
, 1);
284 expectEquals (numCallsB
, 1);
287 expectEquals (numCallsA
, 1);
288 expectEquals (numCallsB
, 2);
291 beginTest ("Functions may mutate internal state");
293 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
295 using Fn
= FixedSizeFunction
<64, void()>;
301 x
= [&numCalls
, counter
= 0]() mutable { counter
+= 1; numCalls
= counter
; };
304 expectEquals (numCalls
, 0);
307 expectEquals (numCalls
, 1);
310 expectEquals (numCalls
, 2);
313 beginTest ("Functions can sink move-only parameters");
315 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
317 using Fn
= FixedSizeFunction
<64, int (std::unique_ptr
<int>)>;
320 auto ptr
= std::make_unique
<int> (value
);
322 Fn fn
= [] (std::unique_ptr
<int> p
) { return *p
; };
324 expect (value
== fn (std::move (ptr
)));
327 beginTest ("Functions be converted from smaller functions");
329 JUCE_FAIL_ON_ALLOCATION_IN_SCOPE
;
331 using SmallFn
= FixedSizeFunction
<20, void()>;
332 using LargeFn
= FixedSizeFunction
<21, void()>;
334 bool smallCalled
= false;
335 bool largeCalled
= false;
337 SmallFn small
= [&smallCalled
, a
= std::array
<char, 8>{}] { smallCalled
= true; juce::ignoreUnused (a
); };
338 LargeFn large
= [&largeCalled
, a
= std::array
<char, 8>{}] { largeCalled
= true; juce::ignoreUnused (a
); };
340 large
= std::move (small
);
344 expect (smallCalled
);
345 expect (! largeCalled
);
350 FixedSizeFunctionTest fixedSizedFunctionTest
;
355 #undef JUCE_FAIL_ON_ALLOCATION_IN_SCOPE