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.
This commit is contained in:
Fischlurch 2016-01-08 01:00:05 +01:00
parent 034d5f99dc
commit 99c478768c
7 changed files with 109 additions and 33 deletions

View file

@ -86,7 +86,6 @@ using lib::meta::is_StringLike;
using lib::meta::can_lexical2string; using lib::meta::can_lexical2string;
using lib::meta::can_convertToString; using lib::meta::can_convertToString;
using lib::meta::use_StringConversion4Stream; using lib::meta::use_StringConversion4Stream;
using lib::meta::CustomStringConv;
using lib::meta::Strip; using lib::meta::Strip;
using std::string; using std::string;

View file

@ -39,7 +39,6 @@
#define LIB_FORMAT_COUT_H #define LIB_FORMAT_COUT_H
#include "lib/format-obj.hpp" #include "lib/format-obj.hpp"
//#include "lib/util.hpp"
#include <string> #include <string>
#include <iostream> #include <iostream>
@ -50,20 +49,6 @@ using std::cerr;
using std::endl; using std::endl;
namespace lib {
namespace meta {
/** when to use custom string conversions for output streams */
template<typename X>
struct use_StringConversion4Stream
: __and_<std::is_class<typename Strip<X>::TypePlain>
,__not_<std::is_pointer<X>>
,__not_<can_lexical2string<X>>
>
{ };
}}
namespace std { namespace std {
@ -80,7 +65,7 @@ namespace std {
ostream& ostream&
operator<< (ostream& os, X const& obj) operator<< (ostream& os, X const& obj)
{ {
return os << lib::meta::CustomStringConv<X>::invoke (obj); return os << util::StringConv<X>::invoke (obj);
} }
@ -93,7 +78,7 @@ namespace std {
operator<< (ostream& os, X const* ptr) operator<< (ostream& os, X const* ptr)
{ {
if (ptr) if (ptr)
return util::showAddr(os, ptr) << "" << lib::meta::CustomStringConv<X>::invoke (*ptr); return util::showAddr(os, ptr) << "" << util::StringConv<X>::invoke (*ptr);
else else
return os << "⟂ «" << lib::meta::typeStr<X>() << "»"; return os << "⟂ «" << lib::meta::typeStr<X>() << "»";
} }

View file

@ -30,8 +30,8 @@
** tasks commonly used from debugging and diagnostics code, both to ** tasks commonly used from debugging and diagnostics code, both to
** investigate object contents and show types and addresses. They ** investigate object contents and show types and addresses. They
** are referred from our [lightweight string converter](\ref ** are referred from our [lightweight string converter](\ref
** lib::meta::CustomStringConv), but also from the util::toString() ** util::StringConv), but also from the util::toString() function
** function and more common [formatting utils](format-util.hpp). ** and more common [formatting utils](format-util.hpp).
** **
** @see FormatHelper_test ** @see FormatHelper_test
** @see FormatString_test ** @see FormatString_test

View file

@ -43,10 +43,9 @@
#define LIB_FORMAT_OBJ_H #define LIB_FORMAT_OBJ_H
#include "lib/meta/trait.hpp" #include "lib/meta/trait.hpp"
//#include "lib/util.hpp"
#include <string>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <string>
namespace std { // forward declaration to avoid including <iostream> namespace std { // forward declaration to avoid including <iostream>
@ -83,6 +82,10 @@ namespace meta {
}}// namespace lib::meta }}// namespace lib::meta
namespace util { namespace util {
std::string showDouble (double) noexcept; std::string showDouble (double) noexcept;
@ -93,6 +96,70 @@ namespace util {
std::ostream& showAddr (std::ostream&, void const* addr); std::ostream& showAddr (std::ostream&, void const* addr);
namespace {
/** toggle to prefer specialisation with direct lexical conversion */
template<typename X>
using enable_LexicalConversion = lib::meta::enable_if< lib::meta::use_LexicalConversion<X>>;
}
/* === generalise the failsafe string conversion === */
/** @note base case is defined in meta/util.hpp */
template<typename X>
struct StringConv<X, enable_LexicalConversion<X>>
{
static std::string
invoke (X const& val) noexcept
try { return boost::lexical_cast<std::string> (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<double>
{
static std::string
invoke (double val) noexcept
{
return util::showDouble (val);
}
};
template<>
struct StringConv<float>
{
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<typename TY>
inline std::string
toString (TY const& val) noexcept
{
return StringConv<TY>::invoke (val);
}
} // namespace util } // namespace util
#endif /*LIB_FORMAT_OBJ_H*/ #endif /*LIB_FORMAT_OBJ_H*/

View file

@ -302,6 +302,22 @@ namespace meta {
> >
{ }; { };
template<typename X>
struct use_LexicalConversion
: __and_<can_lexical2string<X>
,__not_<can_convertToString<X>>
>
{ };
/** when to use custom string conversions for output streams */
template<typename X>
struct use_StringConversion4Stream
: __and_<std::is_class<typename Strip<X>::TypePlain>
,__not_<std::is_pointer<X>>
,__not_<can_lexical2string<X>>
>
{ };

View file

@ -114,10 +114,10 @@ namespace meta {
* Might fail in more tricky situations (references, const, volatile) * Might fail in more tricky situations (references, const, volatile)
* @see \ref format-obj.hpp more elaborate solution including lexical_cast * @see \ref format-obj.hpp more elaborate solution including lexical_cast
*/ */
template<typename T> template<typename X>
struct can_convertToString struct can_convertToString
{ {
static T & probe(); static X & probe();
static Yes_t check(std::string); static Yes_t check(std::string);
static No_t check(...); static No_t check(...);
@ -126,6 +126,11 @@ namespace meta {
static const bool value = (sizeof(Yes_t)==sizeof(check(probe()))); static const bool value = (sizeof(Yes_t)==sizeof(check(probe())));
}; };
/** toggle for explicit specialisations */
template<typename X>
using enable_CustomStringConversion = enable_if<can_convertToString<X>>;
/** strip const from type: naive implementation */ /** strip const from type: naive implementation */
template<typename T> template<typename T>
@ -220,6 +225,13 @@ namespace meta {
return typeStr (&ref); return typeStr (&ref);
} }
}}// namespace lib::meta
namespace util {
using lib::meta::typeStr;
/** failsafe invocation of custom string conversion. /** failsafe invocation of custom string conversion.
@ -234,7 +246,7 @@ namespace meta {
* the returned string indicates "" in this case. * the returned string indicates "" in this case.
*/ */
template<typename X, typename COND =void> template<typename X, typename COND =void>
struct CustomStringConv struct StringConv
{ {
static std::string static std::string
invoke (X const& x) noexcept invoke (X const& x) noexcept
@ -243,7 +255,7 @@ namespace meta {
}; };
template<typename X> template<typename X>
struct CustomStringConv<X, enable_if<can_convertToString<X>> > struct StringConv<X, lib::meta::enable_CustomStringConversion<X>>
{ {
static std::string static std::string
invoke (X const& val) noexcept invoke (X const& val) noexcept
@ -254,11 +266,8 @@ namespace meta {
// NOTE: this is meant to be extensible; // NOTE: this is meant to be extensible;
// more specialisations are e.g. in format-obj.hpp // more specialisations are e.g. in format-obj.hpp
}}// namespace lib::meta
namespace util {
/** pretty-print a double in fixed-point format */ /** pretty-print a double in fixed-point format */
std::string showDouble (double) noexcept; std::string showDouble (double) noexcept;
@ -280,7 +289,7 @@ namespace util {
inline std::string inline std::string
showPtr (X* ptr =nullptr) showPtr (X* ptr =nullptr)
{ {
return ptr? showAddr(ptr) + "" + lib::meta::CustomStringConv<X>::invoke(*ptr) return ptr? showAddr(ptr) + "" + StringConv<X>::invoke(*ptr)
: "⟂ «" + typeStr(ptr) + "»"; : "⟂ «" + typeStr(ptr) + "»";
} }

View file

@ -158,7 +158,7 @@ namespace lib {
P<TAR,BASE>::operator std::string() const noexcept P<TAR,BASE>::operator std::string() const noexcept
try { try {
if (BASE::get()) if (BASE::get())
return meta::CustomStringConv<TAR>::invoke (this->operator*()); return util::StringConv<TAR>::invoke (this->operator*());
else else
return "⟂ P<"+meta::typeStr(this->get())+">"; return "⟂ P<"+meta::typeStr(this->get())+">";
} }