/* ConfigFlags(Test) - generating a configuration type defined by flags Copyright (C) Lumiera.org 2008, Hermann Vosseler This program 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****************************************************/ /** @file configflagstest.cpp ** \par build a type representing a single configuration defined by a set of flags ** ** The semi-automatic assembly of processing node invocation code utilizes some ** metaprogramming to generate a factory, which in turn produces node wiring objects ** according to the configuration to be used for the corresponding ProcNode. This relies on ** generating a distinct type based on a given set of configuration flags, ** which is covered by this test. ** ** @see configflags.hpp ** @see typelistmanip.hpp ** @see nodewiringconfig.hpp real world usage example ** */ #include "common/test/run.hpp" #include "common/meta/generator.hpp" #include "common/meta/typelistutil.hpp" #include "proc/engine/nodewiringconfig.hpp" #include "common/util.hpp" #include #include using ::test::Test; using std::string; using std::cout; using boost::format; namespace lumiera { namespace typelist { namespace test { namespace { // internal definitions enum Cases { ONE = 1 , TWO , THR , FOU , NUM_Cases = FOU , NOT_SET = 0 }; format fmt ("-<%u>%s"); struct NullP { static string print () { return "-"; } }; /** debugging template, * printing the "number" used for intantiation on ctor call */ template struct Printer; template struct Printer : BASE { static string print () { return str( fmt % "ยท" % BASE::print()); } }; template struct Printer, BASE> : BASE { static string print () { return str( fmt % uint(Fl) % BASE::print()); } }; /** call the debug-print for a typelist * utilizing the Printer template */ template string printSublist () { typedef InstantiateChained SubList; return SubList::print(); } /** Spezialisation for debug-printing of a nested sublist */ template struct Printer, BASE> : BASE { static string print () { typedef Node List; return string("\n\t+--") + printSublist()+"+" + BASE::print(); } }; template struct Printer, BASE> : BASE { static string print () { typedef typename Config::Flags FlagList; return string("\n\t+-Conf-[") + printSublist()+"]" + BASE::print(); } }; #define DIAGNOSE(LIST) \ typedef InstantiateChained Contents_##LIST; #define DISPLAY(NAME) \ DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t" << Contents_##NAME::print() << "\n"; /* === Test data === */ typedef Config<> Conf0; typedef Config Conf1; typedef Config Conf2; typedef Config Conf3; typedef Config Conf4; typedef Flags::Tuple Flags1; typedef Flags::Tuple Flags2; typedef Types SomeFlagsets; typedef Flags::Tuple AllFlags; typedef CombineFlags AllFlagCombinations; /** a type which is only partially defined, for some configs. * In ConfigFlags_test::check_filter() we use the metaprogramming machinery * to figure out all possible configs for which \c struct Maybe is defined. * (for this to work, the "defined" specialisations need to provide a * typedef \c is_defined ) */ template struct Maybe; struct Indeed { typedef Yes_t is_defined; }; template<> struct Maybe : Indeed { enum{ CODE = 10 }; }; template<> struct Maybe : Indeed { enum{ CODE = 30 }; }; template struct Maybe > { typedef Yes_t is_defined; enum{ CODE = 20 + Fl }; }; } // (End) internal defs /************************************************************************** * @test check the handling of types representing a specific configuration. * Basically, this is a bitset like feature, but working on types * instead of runtime values. The Goal is to detect automatically * all possible defined specialisations of some template based on * such configuration-tuples. This allows us to assemble the glue code * for pulling data from processing nodes out of small building blocks * in all possible configuraions. */ class ConfigFlags_test : public Test { virtual void run(Arg arg) { check_testdata (); check_flags(); check_instantiation (); check_filter (); check_FlagInfo (); check_ConfigSelector (); } void check_testdata () { DISPLAY (Conf0); DISPLAY (Conf1); DISPLAY (Conf2); DISPLAY (Conf3); DISPLAY (Conf4); DISPLAY (AllFlags); } /** @test conversion between list-of-flags and a config-type in both directions */ void check_flags () { cout << "\n==== check_flags()\n"; typedef Config Flags1; typedef Flags Flags2; DISPLAY (Flags1); DISPLAY (Flags2); // should denote the same type Flags1::Flags flags1 = Flags2::Tuple(); Flags2::Tuple flags2 = flags1; ASSERT (1==sizeof(flags1)); // pure marker-type without content ASSERT (1==sizeof(flags2)); typedef DefineConfigByFlags,NullType> >::Config SimpleConfig_defined_by_Typelist; DISPLAY (SimpleConfig_defined_by_Typelist); typedef DefineConfigByFlags::Config AnotherConfig_defined_by_Typelist; DISPLAY (AnotherConfig_defined_by_Typelist); } /** @test creates a predicate template (metafunction) returning true * iff the template \c Maybe is defined for the configuration in question */ void check_instantiation () { #define CAN_INSTANTIATE(NAME) \ cout << "defined " \ << STRINGIFY(NAME) \ << "? ---> " \ << Instantiation::Test::value << "\n"; cout << "\n==== check_instantiation()\n"; CAN_INSTANTIATE (Conf0); CAN_INSTANTIATE (Conf1); CAN_INSTANTIATE (Conf2); CAN_INSTANTIATE (Conf3); CAN_INSTANTIATE (Conf4); typedef Config Trash; CAN_INSTANTIATE (Trash); } /** @test given a list of flag-tuples, we first create config-types out of them * and then filter out those configs for which \c template Maybe is defined */ void check_filter () { cout << "\n==== check_filter()\n"; DISPLAY (SomeFlagsets); typedef Apply Configs_defined_by_Flagsets; DISPLAY (Configs_defined_by_Flagsets); typedef Filter::Test> Filter_possible_Configs; DISPLAY (Filter_possible_Configs); DISPLAY (AllFlagCombinations); typedef Apply ListAllConfigs; DISPLAY (ListAllConfigs); typedef Filter::Test> Filter_all_possible_Configs; DISPLAY (Filter_all_possible_Configs); } struct TestVisitor { string result; TestVisitor() : result ("TestVisitor application:\n") {} /* === visitation interface === */ typedef string Ret; Ret done() {return result; } template void visit (ulong code) { result += str (format ("visit(code=%u) --> %s\n") % code % Printer::print() ); } }; /** @test FlagInfo metafunction, which takes as argument a list-of-flags * as well as a list-of-lists-of-flags and especially allows to * apply a visitor object to the latter */ void check_FlagInfo() { cout << "\n==== check_FlagInfo()\n"; DISPLAY (Flags1); cout << "max bit : " << FlagInfo::BITS <<"\n"; cout << "binary code: " << FlagInfo::CODE <<"\n"; typedef Apply SomeConfigs; DISPLAY (SomeConfigs); cout << "max bit in [SomeConfigs] : " << FlagInfo::BITS <<"\n"; TestVisitor visitor; cout << FlagInfo::accept (visitor); } template struct TestFactory { uint operator() () { return offset_ + Maybe::CODE; } TestFactory(long o) : offset_(o) {} private: long offset_; }; /** @test use the ConfigSelector template to build a set of factories, * based on a set of configurations. Then invoke the apropriate * factory by specifying the configuration bit code */ void check_ConfigSelector() { cout << "\n==== check_ConfigSelector()\n"; typedef Apply ListAllConfigs; typedef Filter::Test> Possible_Configs; DISPLAY (Possible_Configs); typedef engine::ConfigSelector TestFactorySelector; const long offset = 1000; // parameter fed to all TestFactory ctors TestFactorySelector testConfigSelector (Possible_Configs::List(), offset); #define INVOKE_CONFIG_SELECTOR(CODE) \ cout << " Flag-code = " << CODE \ << " ConfigSelector() ---> " \ << testConfigSelector (CODE) << "\n"; INVOKE_CONFIG_SELECTOR (2); INVOKE_CONFIG_SELECTOR (12); INVOKE_CONFIG_SELECTOR (20); INVOKE_CONFIG_SELECTOR (4); INVOKE_CONFIG_SELECTOR (8); try { INVOKE_CONFIG_SELECTOR (23); NOTREACHED ; } catch (lumiera::error::Invalid& err) { cout << err.what() << "\n"; lumiera_error (); // reset errorflag } } }; /** Register this test class... */ LAUNCHER (ConfigFlags_test, "unit common"); } // namespace test } // namespace typelist } // namespace lumiera