type-traits(#985): rework and clean-up our type-traits

include the improved facility to detect the ability
for lexical cast. Also remove the boost dependency
and switch entirely to standard <type_traits>
This commit is contained in:
Fischlurch 2016-01-06 02:41:58 +01:00
parent e443fd79fd
commit 1814b1fc69
4 changed files with 103 additions and 87 deletions

View file

@ -82,9 +82,14 @@ using lib::diff::GenNode;
using lib::P;
using lib::meta::enable_if;
using lib::meta::typeStr;
using lib::meta::is_basically;
using lib::meta::can_lexical2string;
using lib::meta::can_convertToString;
using lib::meta::CustomStringConv;
using lib::meta::Strip;
using std::__and_;
using std::__not_;
using std::string;
using std::cout;
using std::endl;
@ -108,50 +113,6 @@ stringz (P<X> ptr)
/////////////////////////////////////////reworked traits
namespace {
using lib::meta::Strip;
using std::is_convertible;
using std::is_arithmetic;
using std::is_same;
using std::__not_;
using std::__and_;
using std::__or_;
template<typename T, typename U>
struct is_basically
: is_same <typename Strip<T>::TypeReferred
,typename Strip<U>::TypeReferred>
{ };
template<typename X>
struct is_StringLike
: __or_< is_basically<X, string>
, is_convertible<X, const char*>
>
{ };
template<typename X>
struct can_lexical2string
: __or_< is_arithmetic<X>
, is_StringLike<X>
>
{ };
}
namespace lib {
namespace meta {
template<>
struct Unwrap<void>
{
typedef void Type;
};
}}
/////////////////////////////////////////reworked traits
/////////////////////////////////////////planned new ostream inclusion
namespace {

View file

@ -57,7 +57,7 @@
namespace util {
using boost::enable_if;
using lib::meta::can_ToString;
using lib::meta::can_convertToString;
using lib::meta::can_lexical2string;
using lib::meta::can_IterForEach;
using lib::Symbol;
@ -77,13 +77,13 @@ namespace util {
template<typename X>
struct use_StringConversion : can_ToString<X> { };
struct use_StringConversion : can_convertToString<X> { };
template<typename X>
struct use_LexicalConversion
{
enum { value = can_lexical2string<X>::value
&& !can_ToString<X>::value
&& !can_convertToString<X>::value
};
};

View file

@ -61,7 +61,7 @@
** can be used as a building block within this framework.
**
**
** \par Lumiera forward iterator concept
** # Lumiera forward iterator concept
**
** Similar to the STL, instead of using a common "Iterator" base class,
** we rather define a common set of functions and behaviour which can

View file

@ -21,6 +21,39 @@
*/
/** @file trait.hpp
** Helpers for type detection, type rewriting and metaprogramming.
** This header is a collection of frequently used templates for working with types.
** It incurs only modest header inclusion overhead (be sure not to jeopardise that!).
**
** \par unwrapping
** Strip away all kinds of type adornments, like const, reference, pointer, smart-ptr.
** The accompanying \ref unwrap() function can be used to accept "stuff packaged
** in various forms". The \ref Strip template packages this ability in various
** degrees for metaprogramming
** @warning these helpers can be quite dangerous, as they silently break
** any protective barriers (including lifecycle managing smart-ptrs)
**
** \par string conversion
** a set of trait templates to categorise arbitrary types with respect to
** the ability for string conversions
**
** \par ability to iterate
** these traits [can be used](util-foreach.hpp) to build the notion of a
** generic container -- basically anything that can be enumerated.
** Within Lumiera, we frequently use our own concept of "iterability",
** known as ["Lumiera Forward Iterator"](iter-adapter.hpp). These
** helpers here allow to unify this concept with the "Range"
** concept from the standard library (`begin()` and `end()`)
**
** @see MetaUtils_test
** @see \rem format-obj.hpp string representation for _anything_
** @see \ref lib/meta/utils.hpp very basic metaprogramming helpers
** @see typelist.hpp
**
*/
#ifndef LIB_META_TRAIT_H
#define LIB_META_TRAIT_H
@ -28,13 +61,8 @@
#include "lib/meta/util.hpp"
#include "lib/meta/duck-detector.hpp"
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/is_arithmetic.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <type_traits>
#include <string>
//Forward declarations for the Unwrap helper....
namespace boost{
@ -55,13 +83,23 @@ namespace mobject{
namespace lib {
namespace meta {
using std::remove_cv;
using std::remove_pointer;
using std::remove_reference;
using std::is_convertible;
using std::is_arithmetic;
using std::is_same;
using std::__not_;
using std::__and_;
using std::__or_;
/**
* Helper for type analysis:
* tries to extract a base type from various wrappers.
* Helper for type analysis and convenience accessors:
* attempts to extract a base type from various wrappers.
* Additionally allows to extract/deref the wrapped element.
* @warning strips away any const
* @warning also strips away smart-ptrs and lifecycle managers!
*/
template<typename X>
struct Unwrap
@ -75,10 +113,16 @@ namespace meta {
}
};
template<>
struct Unwrap<void> ///< @note we can't unwrap void!
{
typedef void Type;
};
template<typename X>
struct Unwrap<X*>
{
typedef typename boost::remove_cv<X>::type Type;
typedef typename std::remove_cv<X>::type Type;
static Type&
extract (const X* ptr)
@ -173,10 +217,10 @@ namespace meta {
template<typename X>
struct Strip
{
typedef typename boost::remove_cv<X> ::type TypeUnconst;
typedef typename boost::remove_reference<TypeUnconst>::type TypeReferred;
typedef typename boost::remove_pointer<TypeReferred> ::type TypePointee;
typedef typename boost::remove_cv<TypePointee> ::type TypePlain;
typedef typename std::remove_cv<X> ::type TypeUnconst;
typedef typename std::remove_reference<TypeUnconst>::type TypeReferred;
typedef typename std::remove_pointer<TypeReferred> ::type TypePointee;
typedef typename std::remove_cv<TypePointee> ::type TypePlain;
typedef typename Unwrap<TypePlain> ::Type Type;
};
@ -218,41 +262,52 @@ namespace meta {
/* ==== Traits ==== */
/** compare unadorned types, disregarding const and references */
template<typename T, typename U>
struct is_basically
: is_same <typename Strip<T>::TypeReferred
,typename Strip<U>::TypeReferred>
{ };
/** Trait template for detecting if a type can be converted to string.
* For example, this allows to write specialisations with the help of
* boost::enable_if
*/
template <typename TY>
struct can_ToString
{
enum { value = boost::is_convertible<TY, std::string>::value
};
};
/** detect various flavours of string / text data */
template<typename X>
struct is_StringLike
: __or_< is_basically<X, std::string>
, is_convertible<X, const char*>
>
{ };
/** Trait template for guarding \c lexical_cast<..> expressions.
* Such an expression won't even compile for some types, because of
* missing or ambiguous output operator(s).
* Ideally, there would be some automatic detection (relying on the
* existence of an operator<< for the given type. But I couldn't make
* this work, so I fell back on just declaring types which are known
* to work with lexical_cast to string
* @note this compile-time trait can't predict if such an conversion
* to string will be successful at runtime; indeed it may throw,
* so you should additionally guard the invocation with try-catch!
/** types able to be lexically converted to string representation
* @note this compile-time trait can't predict if such an conversion
* to string will be successful at runtime; indeed it may throw,
* so you should additionally guard the invocation with try-catch!
* @remarks this template is relevant for guarding `lexical_cast<..>` expressions.
* Such an expression won't even compile for some types, because of missing or
* ambiguous output operator(s). Ideally, there would be some automatic detection
* (relying on the existence of an `operator<<` for the given type. But at my
* first attempt in 2009 (commit 1533e5bd0) I couldn't make this work, so I
* fell back on just declaring all classes of types which are known to work
* with lexical_cast to string.
* @remarks Meanwhile (2016) I think this is an adequate and robust solution
* and here to stay. Based on this, I'll add a generic overload for the
* output stream inserter `operator<<` to use custom string conversions;
* this trait is essential to exclude types which can be printed as-is.
*/
template<typename X>
struct can_lexical2string
{
enum { value = boost::is_arithmetic<X>::value
};
};
: __or_< is_arithmetic<X>
, is_StringLike<X>
>
{ };
/* ====== generic iteration support ====== */
/** Trait template to detect a type usable immediately as
* "Lumiera Forward Iterator" in a specialised for-each loop
* This is just a heuristic, based on some common properties
@ -269,7 +324,7 @@ namespace meta {
META_DETECT_OPERATOR_INC();
public:
enum{ value = boost::is_convertible<Type, bool>::value
enum{ value = std::is_convertible<Type, bool>::value
&& HasNested_value_type<Type>::value
&& HasOperator_deref<Type>::value
&& HasOperator_inc<Type>::value