From 99c478768cca7e272d37ed6f0dab759da3effb70 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 8 Jan 2016 01:00:05 +0100 Subject: [PATCH] generic-toString(#985): define streamlined converter ...based on all the clean-up and reorganisation done thus far, we're now able to rebuild the util::str in a more direct and sane way, and thus to disentangle the header inclusion problem. --- research/try.cpp | 1 - src/lib/format-cout.hpp | 19 ++--------- src/lib/format-obj.cpp | 4 +-- src/lib/format-obj.hpp | 71 +++++++++++++++++++++++++++++++++++++++-- src/lib/meta/trait.hpp | 16 ++++++++++ src/lib/meta/util.hpp | 29 +++++++++++------ src/lib/p.hpp | 2 +- 7 files changed, 109 insertions(+), 33 deletions(-) diff --git a/research/try.cpp b/research/try.cpp index 095cdfa63..c8da96329 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -86,7 +86,6 @@ using lib::meta::is_StringLike; using lib::meta::can_lexical2string; using lib::meta::can_convertToString; using lib::meta::use_StringConversion4Stream; -using lib::meta::CustomStringConv; using lib::meta::Strip; using std::string; diff --git a/src/lib/format-cout.hpp b/src/lib/format-cout.hpp index 1349327b5..e044c9ed6 100644 --- a/src/lib/format-cout.hpp +++ b/src/lib/format-cout.hpp @@ -39,7 +39,6 @@ #define LIB_FORMAT_COUT_H #include "lib/format-obj.hpp" -//#include "lib/util.hpp" #include #include @@ -50,20 +49,6 @@ using std::cerr; using std::endl; - -namespace lib { -namespace meta { - - /** when to use custom string conversions for output streams */ - template - struct use_StringConversion4Stream - : __and_::TypePlain> - ,__not_> - ,__not_> - > - { }; -}} - namespace std { @@ -80,7 +65,7 @@ namespace std { ostream& operator<< (ostream& os, X const& obj) { - return os << lib::meta::CustomStringConv::invoke (obj); + return os << util::StringConv::invoke (obj); } @@ -93,7 +78,7 @@ namespace std { operator<< (ostream& os, X const* ptr) { if (ptr) - return util::showAddr(os, ptr) << " ↗" << lib::meta::CustomStringConv::invoke (*ptr); + return util::showAddr(os, ptr) << " ↗" << util::StringConv::invoke (*ptr); else return os << "⟂ «" << lib::meta::typeStr() << "»"; } diff --git a/src/lib/format-obj.cpp b/src/lib/format-obj.cpp index 5481b0d45..9c34ecfae 100644 --- a/src/lib/format-obj.cpp +++ b/src/lib/format-obj.cpp @@ -30,8 +30,8 @@ ** tasks commonly used from debugging and diagnostics code, both to ** investigate object contents and show types and addresses. They ** are referred from our [lightweight string converter](\ref - ** lib::meta::CustomStringConv), but also from the util::toString() - ** function and more common [formatting utils](format-util.hpp). + ** util::StringConv), but also from the util::toString() function + ** and more common [formatting utils](format-util.hpp). ** ** @see FormatHelper_test ** @see FormatString_test diff --git a/src/lib/format-obj.hpp b/src/lib/format-obj.hpp index 58b7e9391..f33826a16 100644 --- a/src/lib/format-obj.hpp +++ b/src/lib/format-obj.hpp @@ -43,10 +43,9 @@ #define LIB_FORMAT_OBJ_H #include "lib/meta/trait.hpp" -//#include "lib/util.hpp" -#include #include +#include namespace std { // forward declaration to avoid including @@ -83,6 +82,10 @@ namespace meta { }}// namespace lib::meta + + + + namespace util { std::string showDouble (double) noexcept; @@ -93,6 +96,70 @@ namespace util { std::ostream& showAddr (std::ostream&, void const* addr); + namespace { + /** toggle to prefer specialisation with direct lexical conversion */ + template + using enable_LexicalConversion = lib::meta::enable_if< lib::meta::use_LexicalConversion>; + } + + + + /* === generalise the failsafe string conversion === */ + + /** @note base case is defined in meta/util.hpp */ + template + struct StringConv> + { + static std::string + invoke (X const& val) noexcept + try { return boost::lexical_cast (val); } + catch(...) { return "↯"; } + }; + + /** explicit specialisation to control precision of double values. + * @note we set an explicit precision, since this is a diagnostic facility + * and we typically do not want to see all digits, but, for test code, + * we do want a predictable string representation of simple fractional + * values like `0.1` (which can not be represented as binary floats) + */ + template<> + struct StringConv + { + static std::string + invoke (double val) noexcept + { + return util::showDouble (val); + } + }; + template<> + struct StringConv + { + static std::string + invoke (float val) noexcept + { + return util::showFloat (val); + } + }; + + + + + /** + * get some string representation of any object, reliably. + * A custom string conversion operator is invoked, if applicable, + * while all lexically convertible types (numbers etc) are treated + * by boost::lexical_cast. For double or float values, hard wired + * rounding to a fixed number of digits will be performed, to yield + * a predictable display of printed unit-test results. + */ + template + inline std::string + toString (TY const& val) noexcept + { + return StringConv::invoke (val); + } + + } // namespace util #endif /*LIB_FORMAT_OBJ_H*/ diff --git a/src/lib/meta/trait.hpp b/src/lib/meta/trait.hpp index 86675f1e4..399276768 100644 --- a/src/lib/meta/trait.hpp +++ b/src/lib/meta/trait.hpp @@ -302,6 +302,22 @@ namespace meta { > { }; + template + struct use_LexicalConversion + : __and_ + ,__not_> + > + { }; + + /** when to use custom string conversions for output streams */ + template + struct use_StringConversion4Stream + : __and_::TypePlain> + ,__not_> + ,__not_> + > + { }; + diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 5a2d4a5ca..0a823dc8e 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -114,10 +114,10 @@ namespace meta { * Might fail in more tricky situations (references, const, volatile) * @see \ref format-obj.hpp more elaborate solution including lexical_cast */ - template + template struct can_convertToString { - static T & probe(); + static X & probe(); static Yes_t check(std::string); static No_t check(...); @@ -126,6 +126,11 @@ namespace meta { static const bool value = (sizeof(Yes_t)==sizeof(check(probe()))); }; + /** toggle for explicit specialisations */ + template + using enable_CustomStringConversion = enable_if>; + + /** strip const from type: naive implementation */ template @@ -220,6 +225,13 @@ namespace meta { return typeStr (&ref); } +}}// namespace lib::meta + + + +namespace util { + + using lib::meta::typeStr; /** failsafe invocation of custom string conversion. @@ -234,7 +246,7 @@ namespace meta { * the returned string indicates "↯" in this case. */ template - struct CustomStringConv + struct StringConv { static std::string invoke (X const& x) noexcept @@ -243,7 +255,7 @@ namespace meta { }; template - struct CustomStringConv> > + struct StringConv> { static std::string invoke (X const& val) noexcept @@ -254,11 +266,8 @@ namespace meta { // NOTE: this is meant to be extensible; // more specialisations are e.g. in format-obj.hpp -}}// namespace lib::meta - - - -namespace util { + + /** pretty-print a double in fixed-point format */ std::string showDouble (double) noexcept; @@ -280,7 +289,7 @@ namespace util { inline std::string showPtr (X* ptr =nullptr) { - return ptr? showAddr(ptr) + " ↗" + lib::meta::CustomStringConv::invoke(*ptr) + return ptr? showAddr(ptr) + " ↗" + StringConv::invoke(*ptr) : "⟂ «" + typeStr(ptr) + "»"; } diff --git a/src/lib/p.hpp b/src/lib/p.hpp index ef757f62e..1858f8d4c 100644 --- a/src/lib/p.hpp +++ b/src/lib/p.hpp @@ -158,7 +158,7 @@ namespace lib { P::operator std::string() const noexcept try { if (BASE::get()) - return meta::CustomStringConv::invoke (this->operator*()); + return util::StringConv::invoke (this->operator*()); else return "⟂ P<"+meta::typeStr(this->get())+">"; }