/* CommonResult(Test) - verify reconciled function return types Copyright (C) 2026, Hermann Vosseler   **Lumiera** is free software; you can redistribute it and/or modify it   under the terms of the GNU General Public License as published by the   Free Software Foundation; either version 2 of the License, or (at your   option) any later version. See the file COPYING for further details. * *****************************************************************/ /** @file common-result-test.cpp ** unit test \ref CommonResult_test */ #include "test/run.hpp" #include "test/test-helper.hpp" #include "lib/meta/tuple-helper.hpp" #include "lib/random.hpp" #include "lib/meta/common-result.hpp" #include using test::showType; using std::string; using std::tuple; namespace lib { namespace meta { namespace test { namespace { // Dummy hierarchy struct II { }; struct S1 : II { }; struct S2 : II { }; template X yield(); } /*******************************************************************//** * @test demonstrate the metafunction to determine a common return type. * @see TupleIdxAdaptor_test * @see IterExplorer_test::verify_expandOperation * @see common-result.hpp * @see iter-explorer.hpp */ class CommonResult_test : public Test { virtual void run (Arg) { simpleUsage(); underlyingMechanism(); demonstrate_TypeReconciliation(); demonstrate_Metaprocessing(); } /** @test how to use the metafunction and the concept */ void simpleUsage() { using CT = CommonResult_t; CHECK (showType() == "long"_expect ); CHECK ((not has_CommonResult_v)); CHECK ((not reconcilable)); // Concept CHECK ((reconcilable_ref)); // Concept } /** @test show the language mechanism used as foundation */ void underlyingMechanism() { auto expr = defaultGen.i(2)? defaultGen.u64() : defaultGen.uni(); CHECK (expr >= 0); CHECK (showType() == "double"_expect ); CHECK (showType() == "long"_expect ); CHECK (showType() == "long"_expect ); CHECK (showType() : yield())>() == "const string"_expect ); CHECK (showType () : yield())>() == "II"_expect ); CHECK (showType() : yield())>() == "II"_expect ); } /** @test show how various combinations of types are handled * - some types can not be reconciled * - there is no common type for an empty sequence of types * - result falls back to return by-value (or RRef), * unless all involved types are given by LRef * - types that must be converted are reconciled into a value * - C++ allows to promote integral numbers to floating-point * - superclass / subclass relationship is observed * - yet no automatic upcast to superclass is performed */ void demonstrate_TypeReconciliation() { CHECK ((not CommonResult{})); // can not be reconciled CHECK ((not CommonResult::value)); // using X = CommonResult::Type; // does not (and should not) compile // using X = CommonResult_t; // does not compile CHECK ((not has_CommonResult_v)); CHECK ((not reconcilable)); // a concept CHECK ((not CommonResult<>::value)); // ¬ ∃ T∈{} |… // using X = CommonResult_t<>; // does not compile CHECK ((showType>()) == "string &&"_expect ); CHECK ((showType>()) == "const string &&"_expect ); CHECK ((showType>()) == "string &&"_expect ); CHECK ((showType>()) == "const string &&"_expect ); CHECK ((showType>()) == "string&"_expect ); CHECK ((showType>()) == "string &&"_expect ); CHECK ((showType>()) == "string const&"_expect ); CHECK ((showType>()) == "string* &&"_expect ); CHECK ((showType>()) == "string*&"_expect ); CHECK ((showType>()) == "string const*&"_expect ); CHECK ((showType>()) == "string* const&"_expect ); CHECK ((showType>()) == "const string* &&"_expect ); CHECK ((showType>()) == "string &&"_expect ); CHECK ((showType>()) == "string"_expect ); CHECK ((showType>()) == "string &&"_expect ); CHECK ((showType>()) == "string"_expect ); CHECK ((showType>()) == "const string"_expect ); CHECK ((showType>()) == "string"_expect ); CHECK ((showType>()) == "string &&"_expect ); CHECK ((showType>()) == "const string &&"_expect ); CHECK ((showType>()) == "const float* &&"_expect ); CHECK ((showType>()) == "const float *"_expect ); CHECK ((showType>()) == "void *"_expect ); CHECK ((showType>()) == "void *"_expect ); CHECK ((showType>()) == "void *"_expect ); CHECK ((showType>()) == "long"_expect ); CHECK ((showType>()) == "long"_expect ); CHECK ((showType>()) == "double"_expect ); CHECK ((showType>()) == "double"_expect ); CHECK ((showType>()) == "double"_expect ); CHECK ((showType>()) == "double"_expect ); CHECK ((not reconcilable )); CHECK ((not reconcilable )); CHECK (( reconcilable )); CHECK (( reconcilable )); CHECK ((not reconcilable_ref )); CHECK ((not reconcilable_ref )); CHECK (( reconcilable_ref )); CHECK ((not reconcilable_ref )); // II ◁— S1 ∧ II ◁— S2 CHECK (( reconcilable )); CHECK (( reconcilable )); CHECK (( reconcilable )); CHECK ((not reconcilable )); CHECK ((not reconcilable )); // NOTE: this is surprising CHECK (( reconcilable )); // the reason is processing from left-to-right CHECK (( reconcilable )); CHECK ((showType>()) == "II &&"_expect ); CHECK ((showType>()) == "II &&"_expect ); CHECK ((showType>()) == "II &&"_expect ); CHECK ((showType>()) == "II &&"_expect ); CHECK ((showType>()) == "II&"_expect ); } /** @test show use in Metaprogramming with variadics */ void demonstrate_Metaprocessing() { auto tup = std::make_tuple (1, 2.3, '4'); CHECK (tup == "«tuple»──(1,2.3,4)"_expect ); using Tup = decltype(tup); CHECK (showType() == "tuple"_expect ); using ValSeq = ElmTypes::Seq; using ConstSeq = ElmTypes::template Apply; CHECK (showType() == "Types"_expect ); using CommRes = CommonResult_t; CHECK (showType() == "double"_expect ); // Pick function overload based on reconcilability auto trp = std::tuple_cat (tup, tuple("😈")); CHECK (doIt (tup) == "Yes we can"_expect ); CHECK (doIt (trp) == "SO SAD!↯↯!"_expect ); } template requires reconcilable static string doIt (tuple) { return "Yes we can"; } template static string doIt (tuple) { return "SO SAD!↯↯!"; } }; LAUNCHER (CommonResult_test, "unit common"); }}} // namespace lib::meta::test