diff --git a/research/try.cpp b/research/try.cpp index 7b73f4c1f..763f90caa 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -21,30 +21,101 @@ // 1/11 - how to fetch the path of the own executable -- at least under Linux? // 10/11 - simple demo using a pointer and a struct // 11/11 - using the boost random number generator(s) +// 12/11 - how to detect if string conversion is possible? +#include "lib/util.hpp" + #include -#include -#include - -using boost::lexical_cast; +#include +using std::string; using std::cout; using std::endl; +typedef char Yes_t; +struct No_t { char more_than_one[4]; }; + + +template +struct _can_convertToString + { + static TY & probe(); + + static Yes_t check(string); + static No_t check(...); + + public: + static const bool value = (sizeof(Yes_t)==sizeof(check(probe()))); + }; + + +class SubString + : public string + { + public: + SubString() : string("sublunar") { } + }; + +class Something { }; + +struct SomehowStringy + { + operator string() { return "No such thing"; } + }; + +struct SomehowSub + { + operator SubString() { return SubString(); } + }; + +class SomehowSubSub + : public SomehowSub + { + }; + + + + +template +bool +investigate (TY const&) + { + return _can_convertToString::value; + } + +#define SHOW_CHECK(_EXPR_) cout << STRINGIFY(_EXPR_) << "\t : " << (investigate(_EXPR_)? "Yes":"No") << endl; int -main (int cnt, char* argv[]) +main (int, char**) { - int32_t seed = (2 == cnt)? lexical_cast (argv[1]) : 42; + SHOW_CHECK (string("nebbich")); + SHOW_CHECK ("gurks"); + SHOW_CHECK (23.34); + SHOW_CHECK (23); - boost::rand48 ranGen(seed); + string urgs("urgs"); + string & urgs_ref (urgs); + string const& urgs_const_ref (urgs); + string * urgs_ptr = &urgs; - cout << "seed = "<< seed << endl; - for (uint i=0; i< 100; ++i) - cout << ranGen() % CHAR_MAX <<"__"; + SHOW_CHECK (urgs_ref); + SHOW_CHECK (urgs_const_ref); + SHOW_CHECK (*urgs_ptr); + + SubString sub; + Something thing; + const SomehowStringy stringy = SomehowStringy(); + SomehowSubSub subsub; + SubString const& subRef(subsub); + + SHOW_CHECK (sub); + SHOW_CHECK (thing); + SHOW_CHECK (stringy); + SHOW_CHECK (subsub); + SHOW_CHECK (subRef); cout << "\n.gulp.\n"; diff --git a/src/lib/format-string.hpp b/src/lib/format-string.hpp index 78c51aa9b..b1a8ce44b 100644 --- a/src/lib/format-string.hpp +++ b/src/lib/format-string.hpp @@ -45,9 +45,8 @@ #ifndef UTIL_FORMAT_STRING_H #define UTIL_FORMAT_STRING_H -//#include "lib/symbol.hpp" -//#include "lib/util.hpp" #include "lib/error.hpp" +#include "lib/meta/util.hpp" #include "lib/meta/size-trait.hpp" #include @@ -73,7 +72,6 @@ namespace std { // forward declaration to avoid including namespace util { using boost::enable_if; -//using util::isnil; using std::string; @@ -95,11 +93,13 @@ namespace util { mutable char formatter_[FORMATTER_SIZE]; + /** helper to prepare parameters for inclusion */ template struct Converter; template void pushParameter (VAL const&); + template void pushParameter (const VAL * const); @@ -108,7 +108,7 @@ namespace util { ~_Fmt (); _Fmt (string formatString); - operator string() const; + operator string() const; ///< get the formatted result template _Fmt& @@ -136,14 +136,13 @@ namespace util { /* ===== forwarding into the implementation ====== */ - /** The percent operator (\c '%' ) is used do feed - * additional parameter values to be included into - * the formatted result, at the positions marked - * within the format string. + /** The percent operator (\c '%' ) is used do feed parameter values + * to be included into the formatted result, at the positions marked + * by printf-style placeholders within the format string. * * \par type specific treatment * Basic types (numbers, chars, strings) are passed to the implementation - * (= boost::format) literally. For custom types, we try to use an custom + * (= boost::format) literally. For custom types, we try to use a custom * string conversion, if applicable. Any other type gets just translated * into a type-ID (using the mangled RTTI info). In case of errors during * the conversion, a string representation of the error is returned @@ -163,30 +162,31 @@ namespace util { namespace { // helpers to pick a suitable specialisation.... - template - struct _can_convertToString - { - enum{ value = false }; - }; - - /** * by default we don't allow to * treat any types directly by boost::format. * As fallback we rather just produce a type-ID */ template - struct _can_forward { enum{ value = false };}; + struct _shall_forward { enum{ value = false };}; /* the following definitions enable some basic types * to be forwarded to boost::format literally */ - template<> struct _can_forward { enum{ value = true }; }; - template<> struct _can_forward { enum{ value = true }; }; - template<> struct _can_forward { enum{ value = true }; }; - template<> struct _can_forward { enum{ value = true }; }; - template<> struct _can_forward { enum{ value = true }; }; + template<> struct _shall_forward { enum{ value = true }; }; + template<> struct _shall_forward { enum{ value = true }; }; + template<> struct _shall_forward { enum{ value = true }; }; + template<> struct _shall_forward { enum{ value = true }; }; + template<> struct _shall_forward { enum{ value = true }; }; + template + struct _shall_convert_toString + { + enum{ value = ! _shall_forward::value + && lib::meta::_can_convertToString::value + }; + }; + inline string _log_and_stringify (std::exception const& ex) @@ -215,8 +215,10 @@ namespace util { + /* === explicit specialisations to control the kind of conversion === */ + /** default/fallback: just indicate the (static) type */ template struct _Fmt::Converter { @@ -227,8 +229,9 @@ namespace util { } }; + /** some custom types explicitly provide string representation */ template - struct _Fmt::Converter >::type> + struct _Fmt::Converter >::type> { static string prepare (VAL const& val) @@ -245,8 +248,10 @@ namespace util { } }; + /** some basic types are directly forwarded down to the implementation; + * @note this requires explicit specialisations in format-string.cpp */ template - struct _Fmt::Converter >::type> + struct _Fmt::Converter >::type> { static VAL const& prepare (VAL const& val) @@ -256,7 +261,7 @@ namespace util { }; template - struct _Fmt::Converter >::type> + struct _Fmt::Converter >::type> { static const VAL * prepare (const VAL * const pVal) @@ -302,5 +307,4 @@ namespace util { } // namespace util - -#endif /*UTIL_FORMAT_STRING_H*/ +#endif diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index f32cbb1ed..f305c46bb 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -21,10 +21,22 @@ */ +/** @file util.hpp + ** Simple and lightweight helpers for metaprogramming and type detection. + ** This header is a collection of very basic type detection and metaprogramming utilities. + ** @warning indirectly, this header gets included into the majority of compilation units. + ** Avoid anything here which increases compilation times or adds much debugging info. + ** + ** @see MetaUtils_test + ** @see trait.hpp + ** @see typelist.hpp + ** + */ + + #ifndef LIB_META_UTIL_H #define LIB_META_UTIL_H - namespace lib { namespace meta { @@ -33,7 +45,7 @@ namespace meta { /* types for figuring out the overload resolution chosen by the compiler */ typedef char Yes_t; - struct No_t { char padding[8]; }; + struct No_t { char more_than_one[4]; }; @@ -56,6 +68,25 @@ namespace meta { }; + /** detect possibility of a conversion to string. + * Naive implementation just trying a the direct conversion. + * The embedded constant #value will be true in case this succeeds. + * Might fail in more tricky situations (references, const, volatile) + * @see string-util.hpp more elaborate solution including lexical_cast + */ + template + struct _can_convertToString + { + static T & probe(); + + static Yes_t check(std::string); + static No_t check(...); + + public: + static const bool value = (sizeof(Yes_t)==sizeof(check(probe()))); + }; + + /** semi-automatic detection if an instantiation is possible. * Requires help by the template to be tested, which needs to define * a typedef member \c is_defined. The embedded metafunction Test can be used diff --git a/tests/40components.tests b/tests/40components.tests index 3ad2bc07d..62827efac 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -394,6 +394,11 @@ return: 0 END +TEST "metaprogramming helpers" Metautils_test < + + 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. + +* *****************************************************/ + + +#include "lib/test/run.hpp" +#include "lib/meta/util.hpp" +#include "lib/meta/typelist.hpp" + +#include + + +namespace lib { +namespace meta { +namespace test { + + using std::string; + + + + + + + /************************************************************************* + * @test verify basic type trait and metaprogramming helpers. + * - marker types to tell which overload the compiler picks + * - simple trait to detect the possibility of a string conversion + * - trait to detect a typelist type + */ + class MetaUtils_test : public Test + { + void + run (Arg) + { + verify_basicAssumptions(); + + detect_stringConversion(); + detect_typeList(); + } + + + /** @test demonstrate / verify the + * basic type trait detection technique: + * By investigating the return type, we can + * figure out which overload the compiler picks.. + */ + void + verify_basicAssumptions() + { + CHECK (sizeof(Yes_t) != sizeof (No_t)); + + CHECK (sizeof(Yes_t) == sizeof (probe (1))); + CHECK (sizeof(Yes_t) == sizeof (probe (1L))); // conversion long -> int + CHECK (sizeof(Yes_t) == sizeof (probe ('a'))); // conversion char -> int + CHECK (sizeof(No_t) == sizeof (probe ("a"))); // char * can't be converted + } + + static Yes_t probe (int); + static No_t probe (...); + + + + //-------------------------------------------------TEST-types-- + class SubString : public string + { + public: + SubString() : string("sublunar") { } + }; + + class Something { }; + + struct SomehowStringy + { + operator string() { return "No such thing"; } + }; + + struct SomehowSubtle + { + operator SubString() { return SubString(); } + }; + + class SomehowSubSub : public SomehowSubtle { }; + //-------------------------------------------------TEST-types-- + + template + static bool + can_convert (TY const&) + { + return _can_convertToString::value; + } + + void + detect_stringConversion() + { + CHECK ( can_convert (string("inline string"))); + CHECK ( can_convert ("char literal")); + CHECK (!can_convert (23.34)); + CHECK (!can_convert (23)); + CHECK (!can_convert (1L)); + + string str("mhm"); + string & str_ref (str); + string const& str_const_ref (str); + string * str_ptr = &str; + + CHECK ( can_convert (str)); + CHECK ( can_convert (str_ref)); + CHECK ( can_convert (str_const_ref)); + CHECK ( can_convert (*str_ptr)); + CHECK (!can_convert (str_ptr)); + + SubString sub; + Something thing; + const SomehowStringy stringy = SomehowStringy(); + SomehowSubSub subsub; + SubString const& subRef(subsub); + + CHECK ( can_convert (sub)); + CHECK (!can_convert (thing)); + CHECK ( can_convert (stringy)); + CHECK ( can_convert (subsub)); + CHECK ( can_convert (subRef)); + } + + + + //-------------------------------------------------TEST-types-- + typedef Types< int + , uint + , int64_t + , uint64_t + >::List TheList; + + typedef Types< >::List EmptyList; + //-------------------------------------------------TEST-types-- + + + void + detect_typeList() + { + CHECK ( is_Typelist::value); + CHECK ( is_Typelist::value); + CHECK (!is_Typelist::value); + } + }; + + + /** Register this test class... */ + LAUNCHER (MetaUtils_test, "unit meta"); + + + +}}} // namespace lib::meta::test