diff --git a/src/lib/format.hpp b/src/lib/format.hpp index a7856e183..9b472dd82 100644 --- a/src/lib/format.hpp +++ b/src/lib/format.hpp @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -46,6 +47,7 @@ namespace util { using lumiera::typelist::can_ToString; + using lumiera::typelist::can_lexical2string; using lumiera::Symbol; using boost::enable_if; using boost::disable_if; @@ -70,11 +72,36 @@ namespace util { { return ""; } + + + + template + inline string + invoke_indirect2string ( typename enable_if< can_lexical2string, + X >::type const& val) + { + try { return boost::lexical_cast (val); } + catch(...) { return ""; } + } + + template + inline string + invoke_indirect2string ( typename disable_if< can_lexical2string, + X >::type const&) + { + return ""; + } } - /** try to get an object converted to string */ + /** try to get an object converted to string. + * An custom/standard conversion to string is used, + * if applicable; otherwise, some standard types can be + * converted by a lexical_cast (based on operator<< ). + * Otherwise, either the fallback string is used, or just + * a string denoting the (mangled) type. + */ template inline string str ( TY const& val @@ -86,8 +113,17 @@ namespace util { return string(prefix) + invoke_2string(val); else - return fallback? fallback - : tyStr(val); + { + if (can_lexical2string::value) + { + string res (invoke_indirect2string (val)); + if ("" != res) + return string(prefix) + res; + } + + return fallback? fallback + : tyStr(val); + } } diff --git a/src/lib/meta/trait.hpp b/src/lib/meta/trait.hpp index 8e0348e9b..608888561 100644 --- a/src/lib/meta/trait.hpp +++ b/src/lib/meta/trait.hpp @@ -28,11 +28,17 @@ #include "lib/meta/util.hpp" #include +#include +#include #include namespace lumiera { namespace typelist { + + using boost::enable_if; + using boost::is_arithmetic; + /** Trait template for detecting if a type can be converted to string. * For example, this allows to write specialisations with the help of @@ -46,6 +52,24 @@ namespace typelist { }; + /** Trait template for guarding \c lexical_cast<..> expressions. + * Such an expression won't even compile for some types, because of + * missing or ambiguous output operator(s). + * Ideally, there would be some automatic detection (relying on the + * existence of an operator<< for the given type. But I couldn't make + * this work, so I fell back on just declaring types which are known + * to work with lexical_cast to string + * @note this compile-time trait can't predict if such an conversion + * to string will be successful at runtime; indeed it may throw, + * so you should additionally guard the invocation with try-catch! + */ + template + struct can_lexical2string + { + enum { value = is_arithmetic::value + }; + }; + }} // namespace lumiera::typelist #endif diff --git a/tests/40components.tests b/tests/40components.tests index 31533b2b4..0439391c2 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -240,6 +240,9 @@ out: «..util.test.Reticent.» out: out: ^hey Joe! out: ^he says: hey Joe! +out: ^the truth: 0 +out: ^just a number: 1.234e\+56 +out: ^12345X return: 0 END diff --git a/tests/45controller.tests b/tests/45controller.tests index 8e2a25dcf..060fd131c 100644 --- a/tests/45controller.tests +++ b/tests/45controller.tests @@ -7,7 +7,7 @@ PLANNED "CommandBasic_test" CommandBasic_test <") << endl; + + cout << str (false, "the truth: ") << endl; + cout << str (12.34e55, "just a number: ") << endl; + cout << str (short(12)) << str (345L) << str ('X') << endl; }