research: detecting the possibility of a string conversion

find out about the corner cases of this
simplistic implementation
This commit is contained in:
Fischlurch 2011-12-30 03:45:10 +01:00
parent 7efff377d0
commit e054c272b6
5 changed files with 321 additions and 39 deletions

View file

@ -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 <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/random/linear_congruential.hpp>
using boost::lexical_cast;
#include <string>
using std::string;
using std::cout;
using std::endl;
typedef char Yes_t;
struct No_t { char more_than_one[4]; };
template<typename TY>
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<typename TY>
bool
investigate (TY const&)
{
return _can_convertToString<TY>::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<int32_t> (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";

View file

@ -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 <string>
@ -73,7 +72,6 @@ namespace std { // forward declaration to avoid including <iostream>
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<typename VAL, class SEL =void>
struct Converter;
template<typename VAL>
void pushParameter (VAL const&);
template<typename VAL>
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<typename VAL>
_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<typename X>
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<typename X>
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<string> { enum{ value = true }; };
template<> struct _can_forward<int> { enum{ value = true }; };
template<> struct _can_forward<uint> { enum{ value = true }; };
template<> struct _can_forward<float> { enum{ value = true }; };
template<> struct _can_forward<double> { enum{ value = true }; };
template<> struct _shall_forward<string> { enum{ value = true }; };
template<> struct _shall_forward<int> { enum{ value = true }; };
template<> struct _shall_forward<uint> { enum{ value = true }; };
template<> struct _shall_forward<float> { enum{ value = true }; };
template<> struct _shall_forward<double> { enum{ value = true }; };
template<typename X>
struct _shall_convert_toString
{
enum{ value = ! _shall_forward<X>::value
&& lib::meta::_can_convertToString<X>::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<typename VAL, class SEL>
struct _Fmt::Converter
{
@ -227,8 +229,9 @@ namespace util {
}
};
/** some custom types explicitly provide string representation */
template<typename VAL>
struct _Fmt::Converter<VAL, typename enable_if< _can_convertToString<VAL> >::type>
struct _Fmt::Converter<VAL, typename enable_if< _shall_convert_toString<VAL> >::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<typename VAL>
struct _Fmt::Converter<VAL, typename enable_if< _can_forward<VAL> >::type>
struct _Fmt::Converter<VAL, typename enable_if< _shall_forward<VAL> >::type>
{
static VAL const&
prepare (VAL const& val)
@ -256,7 +261,7 @@ namespace util {
};
template<typename VAL>
struct _Fmt::Converter<VAL*, typename enable_if< _can_forward<VAL> >::type>
struct _Fmt::Converter<VAL*, typename enable_if< _shall_forward<VAL> >::type>
{
static const VAL *
prepare (const VAL * const pVal)
@ -302,5 +307,4 @@ namespace util {
} // namespace util
#endif /*UTIL_FORMAT_STRING_H*/
#endif

View file

@ -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<typename T>
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

View file

@ -394,6 +394,11 @@ return: 0
END
TEST "metaprogramming helpers" Metautils_test <<END
return: 0
END
TEST "NIL object singleton" NullValue_test <<END
return: 0
END

View file

@ -0,0 +1,171 @@
/*
MetaUtils(Test) - check some simple type trait helpers
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
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 <string>
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<typename TY>
static bool
can_convert (TY const&)
{
return _can_convertToString<TY>::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<TheList>::value);
CHECK ( is_Typelist<EmptyList>::value);
CHECK (!is_Typelist<Something>::value);
}
};
/** Register this test class... */
LAUNCHER (MetaUtils_test, "unit meta");
}}} // namespace lib::meta::test