research: detecting the possibility of a string conversion
find out about the corner cases of this simplistic implementation
This commit is contained in:
parent
7efff377d0
commit
e054c272b6
5 changed files with 321 additions and 39 deletions
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
171
tests/lib/meta/meta-utils-test.cpp
Normal file
171
tests/lib/meta/meta-utils-test.cpp
Normal 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
|
||||
Loading…
Reference in a new issue