33// Distributed under the ISC License. See accompanying file LICENSE.md
44// or copy at https://opensource.org/licenses/ISC
55
6+ #include " catch2/catch_test_macros.hpp"
67#include < pfn/expected.hpp>
78
89#include < catch2/catch_all.hpp>
910
11+ #include < stdexcept>
1012#include < type_traits>
1113
1214enum Error { unknown, secret = 142 , mystery = 176 };
@@ -131,8 +133,86 @@ TEST_CASE("unexpect", "[expected][polyfill][unexpect]")
131133 }
132134}
133135
136+ namespace unxp {
137+ static int witness = 0 ;
138+ struct Foo {
139+ int v = {};
140+
141+ Foo () = delete ;
142+ Foo (Foo &&) = default ;
143+
144+ Foo &operator =(Foo &o) noexcept
145+ {
146+ v = o.v ;
147+ witness *= 53 ;
148+ return *this ;
149+ }
150+
151+ Foo &operator =(Foo const &o) noexcept
152+ {
153+ v = o.v ;
154+ witness *= 59 ;
155+ return *this ;
156+ }
157+
158+ Foo &operator =(Foo &&o) noexcept
159+ {
160+ v = o.v ;
161+ witness *= 61 ;
162+ return *this ;
163+ }
164+
165+ Foo &operator =(Foo const &&o) noexcept
166+ {
167+ v = o.v ;
168+ witness *= 67 ;
169+ return *this ;
170+ }
171+
172+ Foo (int a) noexcept : v(a) { witness += a; }
173+
174+ Foo (auto &&...a) noexcept
175+ requires (sizeof ...(a) > 1 && (std::is_same_v<std::remove_cvref_t <decltype (a)>, int > && ...))
176+ : v((1 * ... * a))
177+ {
178+ witness += v;
179+ }
180+
181+ Foo (std::initializer_list<double > l, auto ... a) noexcept (false )
182+ requires (std::is_same_v<std::remove_cvref_t <decltype (a)>, int > && ...)
183+ : v(init(l, a...))
184+ {
185+ witness += v;
186+ }
187+
188+ bool operator ==(Foo const &) const noexcept = default ;
189+
190+ static int init (std::initializer_list<double > l, auto &&...a) noexcept (false )
191+ {
192+ double ret = (1 * ... * a);
193+ for (auto d : l) {
194+ if (d == 0.0 )
195+ throw std::runtime_error (" invalid input" );
196+ ret *= d;
197+ }
198+ return static_cast <int >(ret);
199+ }
200+ };
201+
202+ void swap (Foo &l, Foo &r)
203+ {
204+ std::swap (l.v , r.v );
205+ witness *= 97 ;
206+ }
207+
208+ } // namespace unxp
209+
134210TEST_CASE (" unexpected" , " [expected][polyfill][unexpected]" )
135211{
212+ using pfn::unexpected;
213+ using unxp::Foo;
214+ using unxp::witness;
215+
136216 SECTION (" is_valid_unexpected" )
137217 {
138218 using pfn::detail::_is_valid_unexpected;
@@ -156,39 +236,209 @@ TEST_CASE("unexpected", "[expected][polyfill][unexpected]")
156236
157237 SECTION (" constructors" )
158238 {
159- using pfn::unexpected;
239+ SECTION (" constexpr, CTAD" )
240+ {
241+ constexpr unexpected c{Error::mystery};
242+ static_assert (c.error () == Error::mystery);
243+ static_assert (std::is_same_v<decltype (c), unexpected<Error> const >);
244+ static_assert (std::is_nothrow_constructible_v<decltype (c), Error>);
245+ SUCCEED ();
246+ }
160247
161- SECTION (" explicit single parameter " )
248+ SECTION (" constexpr, no CTAD " )
162249 {
163- SECTION (" constexpr, CTAD" )
164- {
165- constexpr unexpected c1{Error::mystery};
166- static_assert (std::is_same_v<decltype (c1), unexpected<Error> const >);
167- static_assert (c1.error () == Error::mystery);
168- }
250+ constexpr unexpected<int > c{42 };
251+ static_assert (c.error () == 42 );
252+ static_assert (std::is_nothrow_constructible_v<decltype (c), int >);
253+ SUCCEED ();
254+ }
255+
256+ SECTION (" no conversion, CTAD" )
257+ {
258+ auto const before = witness;
259+ unexpected c{Foo{2 }};
260+ CHECK (witness == before + 2 );
261+ CHECK (c.error () == Foo{2 });
262+ CHECK (c == unexpected<Foo>{2 });
263+ static_assert (std::is_same_v<decltype (c), unexpected<Foo>>);
264+ static_assert (std::is_nothrow_constructible_v<decltype (c), Foo>);
265+ }
266+
267+ SECTION (" conversion, no CTAD" )
268+ {
269+ auto const before = witness;
270+ unexpected<Foo> c (3 );
271+ CHECK (witness == before + 3 );
272+ CHECK (c.error ().v == 3 );
273+ CHECK (c == unexpected<Foo>{3 });
274+ static_assert (std::is_nothrow_constructible_v<decltype (c), int >);
275+ }
169276
170- SECTION (" constexpr, no CTAD" )
277+ SECTION (" in-place, no CTAD" )
278+ {
279+ auto const before = witness;
280+ unexpected<Foo> c (std::in_place, 3 , 5 );
281+ CHECK (witness == before + 3 * 5 );
282+ CHECK (c.error () == Foo{3 , 5 });
283+ CHECK (c == unexpected<Foo>{15 });
284+ static_assert (std::is_nothrow_constructible_v<decltype (c), std::in_place_t , int , int >);
285+ }
286+
287+ SECTION (" in_place, not CTAD, initializer_list, noexcept(false)" )
288+ {
289+ SECTION (" forwarded args" )
171290 {
172- constexpr unexpected<int > c1{42 };
173- static_assert (std::is_same_v<decltype (c1), unexpected<int > const >);
174- static_assert (c1.error () == 42 );
291+ auto const before = witness;
292+ unexpected<Foo> c (std::in_place, {3.0 , 5.0 }, 7 , 11 );
293+ auto const d = 3 * 5 * 7 * 11 ;
294+ CHECK (witness == before + d);
295+ CHECK (c.error () == Foo{d});
296+ CHECK (c == unexpected{Foo{d}});
297+ static_assert (
298+ not std::is_nothrow_constructible_v<decltype (c), std::in_place_t , std::initializer_list<double >, int , int >);
299+ static_assert (std::is_constructible_v<decltype (c), std::in_place_t , std::initializer_list<double >, int , int >);
175300 }
176301
177- SECTION (" no conversion, CTAD " )
302+ SECTION (" no forwarded args " )
178303 {
179- unexpected const c2{Error::secret};
180- static_assert (std::is_same_v<decltype (c2), unexpected<Error> const >);
181- CHECK (c2.error () == Error::secret);
304+ auto const before = witness;
305+ unexpected<Foo> c (std::in_place, {2.0 , 2.5 });
306+ CHECK (witness == before + 5 );
307+ CHECK (c.error () == Foo{5 });
308+ CHECK (c == unexpected<Foo>{5 });
309+ static_assert (not std::is_nothrow_constructible_v<decltype (c), std::in_place_t , std::initializer_list<double >>);
310+ static_assert (std::is_constructible_v<decltype (c), std::in_place_t , std::initializer_list<double >>);
182311 }
183312
184- SECTION (" conversion, no CTAD " )
313+ SECTION (" exception thrown " )
185314 {
186- unexpected<double > c3 (12 );
187- static_assert (std::is_same_v<decltype (c3), unexpected<double >>);
188- CHECK (c3.error () == 12.0 );
315+ unexpected<Foo> t{13 };
316+ auto const before = witness;
317+ try {
318+ t = unexpected<Foo>{std::in_place, {2.0 , 1.0 , 0.0 }, 5 };
319+ FAIL ();
320+ } catch (std::runtime_error const &) {
321+ SUCCEED ();
322+ }
323+ CHECK (t.error ().v == 13 );
324+ CHECK (witness == before);
189325 }
190326 }
327+ }
191328
192- SECTION (" in_place" ) {}
329+ SECTION (" accessor" )
330+ {
331+ Foo v{1 };
332+
333+ SECTION (" lval" )
334+ {
335+ unexpected<Foo> t{13 };
336+ auto before = witness;
337+ v = t.error ();
338+ CHECK (witness == before * 53 );
339+ CHECK (v == Foo{13 });
340+ }
341+
342+ SECTION (" lval const" )
343+ {
344+ unexpected<Foo> const t{17 };
345+ auto before = witness;
346+ v = t.error ();
347+ CHECK (witness == before * 59 );
348+ CHECK (v == Foo{17 });
349+ }
350+
351+ SECTION (" rval" )
352+ {
353+ unexpected<Foo> t{19 };
354+ auto before = witness;
355+ v = std::move (t.error ());
356+ CHECK (witness == before * 61 );
357+ CHECK (v == Foo{19 });
358+ }
359+
360+ SECTION (" rval const" )
361+ {
362+ unexpected<Foo> const t{23 };
363+ auto before = witness;
364+ v = std::move (t.error ());
365+ CHECK (witness == before * 67 );
366+ CHECK (v == Foo{23 });
367+ }
368+ }
369+
370+ SECTION (" assignment" )
371+ {
372+ unexpected<Foo> v{0 };
373+
374+ SECTION (" lval" )
375+ {
376+ unexpected<Foo> t{13 };
377+ auto before = witness;
378+ v = t; // t binds to unexpected<Foo> const &
379+ CHECK (witness == before * 59 );
380+ CHECK (v.error () == Foo{13 });
381+ }
382+
383+ SECTION (" lval const" )
384+ {
385+ unexpected<Foo> const t{17 };
386+ auto before = witness;
387+ v = t; // t binds to unexpected<Foo> const &
388+ CHECK (witness == before * 59 );
389+ CHECK (v.error () == Foo{17 });
390+ }
391+
392+ SECTION (" rval" )
393+ {
394+ unexpected<Foo> t{19 };
395+ auto before = witness;
396+ v = std::move (t); // t binds to unexpected<Foo> &&
397+ CHECK (witness == before * 61 );
398+ CHECK (v.error () == Foo{19 });
399+ }
400+
401+ SECTION (" rval const" )
402+ {
403+ unexpected<Foo> const t{23 };
404+ auto before = witness;
405+ v = std::move (t); // t binds to unexpected<Foo> const &
406+ CHECK (witness == before * 59 );
407+ CHECK (v.error () == Foo{23 });
408+ }
409+ }
410+
411+ SECTION (" swap" )
412+ {
413+ unexpected<Foo> v{2 };
414+ unexpected w{Foo{3 }};
415+ auto before = witness;
416+ v.swap (w);
417+ CHECK (witness == before * 97 );
418+ CHECK (v == unexpected{Foo{3 }});
419+ CHECK (w == unexpected{Foo{2 }});
420+ w.error () = Foo{11 };
421+ before = witness;
422+ swap (v, w);
423+ CHECK (v.error () == Foo{11 });
424+ CHECK (w.error () == Foo (3 ));
425+ }
426+
427+ SECTION (" constexpr all, CTAD" )
428+ {
429+ constexpr auto test = [](auto i) constexpr {
430+ unexpected a{i};
431+ unexpected b{i * 5 };
432+ swap (a, b);
433+ unexpected c{0 };
434+ c = b;
435+ b.swap (c);
436+ return unexpected{b.error () * a.error () * 7 };
437+ };
438+ constexpr auto c = test (21 );
439+ static_assert (std::is_same_v<decltype (c), unexpected<int > const >);
440+ static_assert (c.error () == 21 * 21 * 5 * 7 );
441+
442+ SUCCEED ();
193443 }
194444}
0 commit comments