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:
parent
034d5f99dc
commit
99c478768c
7 changed files with 109 additions and 33 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -51,20 +50,6 @@ 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>() << "»";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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*/
|
||||||
|
|
|
||||||
|
|
@ -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>>
|
||||||
|
>
|
||||||
|
{ };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,12 +266,9 @@ 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;
|
||||||
std::string showFloat (float) noexcept;
|
std::string showFloat (float) 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) + "»";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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())+">";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue