Newer
Older
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
#ifndef DUNE_TYPETRAITS_HH
#define DUNE_TYPETRAITS_HH
#include <type_traits>
Steffen Müthing
committed
#include <dune/common/std/utility.hh>
namespace Dune
{
/**
* @file
* @brief Traits for type conversions and type information.
* @author Markus Blatt, Christian Engwer
*/
/** @addtogroup Common
*
* @{
*/
/**
* @brief Just an empty class
*/
struct Empty {};
/**
* @brief General type traits class to check whether type is reference or
* pointer type
*
* \deprecated This class will be replaced by alternatives found in the C++11 stl.
* - Use is_pointer<T>::value instead of TypeTraits<T>::isPointer
* - Use is_lvalue_reference<T>::value instead of TypeTraits<T>::isReference
* - Use remove_pointer<T>::type instead of TypeTraits<T>::PointeeType
* - Use remove_reference<T>::type instead of TypeTraits<T>::ReferredType
*/
template <typename T>
class TypeTraits
{
private:
template <class U>
struct PointerTraits {
enum { result = false };
typedef Empty PointeeType;
};
template <class U>
struct PointerTraits<U*> {
enum { result = true };
typedef U PointeeType;
};
template <class U> struct ReferenceTraits
{
enum { result = false };
typedef U ReferredType;
};
template <class U> struct ReferenceTraits<U&>
{
enum { result = true };
typedef U ReferredType;
};
public:
enum { isPointer = PointerTraits<T>::result };
typedef typename PointerTraits<T>::PointeeType PointeeType DUNE_DEPRECATED_MSG("Use remove_pointer instead!");
enum { isReference = ReferenceTraits<T>::result };
typedef typename ReferenceTraits<T>::ReferredType ReferredType DUNE_DEPRECATED_MSG("Use remove_reference instead!");
/**
* @brief Determines wether a type is const or volatile and provides the
* unqualified types.
*/
template<typename T>
struct ConstantVolatileTraits
{
enum {
/** @brief True if T has a volatile specifier. */
isVolatile=false,
/** @brief True if T has a const qualifier. */
isConst=false
};
/** @brief The unqualified type. */
typedef T UnqualifiedType;
/** @brief The const type. */
typedef const T ConstType;
/** @brief The const volatile type. */
typedef const volatile T ConstVolatileType;
};
template<typename T>
struct ConstantVolatileTraits<const T>
{
enum {
isVolatile=false, isConst=true
};
typedef T UnqualifiedType;
typedef const UnqualifiedType ConstType;
typedef const volatile UnqualifiedType ConstVolatileType;
};
template<typename T>
struct ConstantVolatileTraits<volatile T>
{
enum {
isVolatile=true, isConst=false
};
typedef T UnqualifiedType;
typedef const UnqualifiedType ConstType;
typedef const volatile UnqualifiedType ConstVolatileType;
};
template<typename T>
struct ConstantVolatileTraits<const volatile T>
{
enum {
isVolatile=true, isConst=true
};
typedef T UnqualifiedType;
typedef const UnqualifiedType ConstType;
typedef const volatile UnqualifiedType ConstVolatileType;
};
/** @brief Tests wether a type is volatile. */
template<typename T>
struct IsVolatile
{
enum {
/** @brief True if The type is volatile. */
value=ConstantVolatileTraits<T>::isVolatile
};
};
/** @brief Tests wether a type is constant. */
template<typename T>
struct IsConst
{
enum {
/** @brief True if The type is constant. */
value=ConstantVolatileTraits<T>::isConst
};
};
template<typename T, bool isVolatile>
struct RemoveConstHelper
{
typedef typename ConstantVolatileTraits<T>::UnqualifiedType Type;
};
template<typename T>
struct RemoveConstHelper<T,true>
{
typedef volatile typename ConstantVolatileTraits<T>::UnqualifiedType Type;
};
using std::remove_const;
using std::remove_reference;
* @brief Checks wether a type is convertible to another.
*
* @tparam From type you want to convert
* @tparam To type you want to obtain
*
* Inspired by
* <A HREF="http://www.kotiposti.net/epulkkin/instructive/base-class-determination.html"> this website</A>
*/
template<class From, class To>
class Conversion
{
typedef char Small;
struct Big {char dummy[2];};
static Small test(To);
static Big test(...);
static typename remove_reference< From >::type &makeFrom ();
enum {
/** @brief True if the conversion exists. */
exists = sizeof(test(makeFrom())) == sizeof(Small),
Oliver Sander
committed
/** @brief Whether the conversion exists in both ways. */
/** @brief True if To and From are the same type. */
sameType = false
};
Conversion(){}
template <class From>
class Conversion<From, void>
{
public:
enum {
exists = false,
isTwoWay = false,
sameType = false
};
};
template <class To>
class Conversion<void, To>
{
public:
enum {
exists = false,
isTwoWay = false,
sameType = false
};
};
Martin Nolte
committed
template<>
class Conversion< int, double >
{
public:
enum {
exists = true,
isTwoWay = false,
sameType = false
};
};
template<class T>
class Conversion<T,T>{
public:
enum { exists=true, isTwoWay=true, sameType=true};
/**
* @brief Checks wether a type is derived from another.
*
* @tparam Base the potential base class you want to test for
* @tparam Derived type you want to test
*
* Similar idea to
* <A HREF="http://www.kotiposti.net/epulkkin/instructive/base-class-determination.html"> this website</A>
*/
template <class Base, class Derived>
class IsBaseOf
{
typedef typename ConstantVolatileTraits< typename remove_reference< Base >::type >::UnqualifiedType RawBase;
typedef typename ConstantVolatileTraits< typename remove_reference< Derived >::type >::UnqualifiedType RawDerived;
typedef char Small;
struct Big {char dummy[2];};
static Small test(RawBase*);
static RawDerived* &makePtr ();
public:
enum {
/** @brief True if Base is a base class of Derived. */
value = sizeof(test(makePtr())) == sizeof(Small)
};
IsBaseOf(){}
/**
* @brief Checks wether two types are interoperable.
*
* Two types are interoperable if conversions in either directions
* exists.
*/
template<class T1, class T2>
struct IsInteroperable
{
enum {
/**
* @brief True if either a conversion from T1 to T2 or vice versa
* exists.
*/
value = Conversion<T1,T2>::exists || Conversion<T2,T1>::exists
Oliver Sander
committed
using std::enable_if;
/**
* @brief Enable typedef if two types are interoperable.
*
* (also see IsInteroperable)
*/
template<class T1, class T2, class Type>
struct EnableIfInterOperable
: public enable_if<IsInteroperable<T1,T2>::value, Type>
// pull in default implementation
using std::is_same;
using std::conditional;
using std::integral_constant;
using std::true_type;
using std::false_type;
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
template<typename>
struct __is_pointer_helper
: public false_type { };
template<typename T>
struct __is_pointer_helper<T*>
: public true_type { };
/// is_pointer
template<typename T>
struct is_pointer
: public integral_constant<bool, (__is_pointer_helper<T>::value)>
{ };
// Helper class for is_lvalue_reference
template<typename>
struct __is_lvalue_reference_helper
: public false_type { };
template<typename T>
struct __is_lvalue_reference_helper<T&>
: public true_type { };
/** \brief Determine whether a type is a lvalue reference type */
template<typename T>
struct is_lvalue_reference
: public integral_constant<bool, (__is_lvalue_reference_helper<T>::value)>
{ };
template<typename _Tp>
struct __remove_pointer_helper
{ typedef _Tp type; };
template<typename _Tp>
struct __remove_pointer_helper<_Tp*>
{ typedef _Tp type; };
/** \brief Return the type a pointer type points to
*
* \note When the argument T is not a pointer, TypeTraits::PointeeType returns Dune::Empty,
* while Dune::remove_pointer (as std::remove_pointer), returns T itself.
*/
template<typename _Tp>
struct remove_pointer
: public __remove_pointer_helper<typename remove_const<_Tp>::type >
{ };
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
/**
\brief template which always yields a false value
\tparam T Some type. It sould be a type expression involving template
parameters of the class or function using AlwaysFalse.
Suppose you have a template class. You want to document the required
members of this class in the non-specialized template, but you know that
actually instantiating the non-specialized template is an error. You
can try something like this:
\code
template<typename T>
struct Traits {
static_assert(false,
"Instanciating this non-specialized template is an "
"error. You should use one of the specializations "
"instead.");
//! The type used to frobnicate T
typedef void FrobnicateType;
};
\endcode
This will trigger static_assert() as soon as the compiler reads the
definition for the Traits template, since it knows that "false" can
never become true, no matter what the template parameters of Traits are.
As a workaround you can use AlwaysFalse: replace <tt>false</tt> by
<tt>AlwaysFalse<T>::value</tt>, like this:
\code
template<typename T>
struct Traits {
static_assert(AlwaysFalse<T>::value,
"Instanciating this non-specialized template is an "
"error. You should use one of the specializations "
"instead.");
//! The type used to frobnicate T
typedef void FrobnicateType;
};
\endcode
Since there might be an specialization of AlwaysFalse for template
parameter T, the compiler cannot trigger static_assert() until the
type of T is known, that is, until Traits<T> is instantiated.
*/
template<typename T>
struct AlwaysFalse {
//! always a false value
static const bool value = false;
};
/**
\brief template which always yields a true value
\tparam T Some type. It sould be a type expression involving template
parameters of the class or function using AlwaysTrue.
\note This class exists mostly for consistency with AlwaysFalse.
*/
template<typename T>
struct AlwaysTrue {
//! always a true value
static const bool value = true;
};
Steffen Müthing
committed
#if defined(DOXYGEN) or HAVE_IS_INDEXABLE_SUPPORT
#ifndef DOXYGEN
namespace detail {
template<typename T, typename I, typename = int>
struct _is_indexable
: public std::false_type
{};
template<typename T, typename I>
Steffen Müthing
committed
struct _is_indexable<T,I,typename std::enable_if<(sizeof(Std::declval<T>()[Std::declval<I>()]) > 0),int>::type>
: public std::true_type
{};
Steffen Müthing
committed
#endif // DOXYGEN
//! Type trait to determine whether an instance of T has an operator[](I), i.e. whether
//! it can be indexed with an index of type I.
Steffen Müthing
committed
/**
* \warning Not all compilers support testing for arbitrary index types. In particular, there
* are problems with GCC 4.4 and 4.5.
*/
template<typename T, typename I = std::size_t>
struct is_indexable
Steffen Müthing
committed
: public detail::_is_indexable<T,I>
Steffen Müthing
committed
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
#else // defined(DOXYGEN) or HAVE_IS_INDEXABLE_SUPPORT
// okay, here follows a mess of compiler bug workarounds...
// GCC 4.4 dies if we try to subscript a simple type like int and
// both GCC 4.4 and 4.5 don't like using arbitrary types as subscripts
// for macros.
// So we make sure to only ever attempt the SFINAE for operator[] for
// class types, and to make sure the compiler doesn't become overly eager
// we have to do some lazy evaluation tricks with nested templates and
// stuff.
// Let's get rid of GCC 4.4 ASAP!
namespace detail {
// simple wrapper template to support the lazy evaluation required
// in _is_indexable
template<typename T>
struct _lazy
{
template<typename U>
struct evaluate
{
typedef T type;
};
};
// default version, gets picked if SFINAE fails
template<typename T, typename = int>
struct _is_indexable
: public std::false_type
{};
// version for types supporting the subscript operation
template<typename T>
struct _is_indexable<T,decltype(Std::declval<T>()[0],0)>
: public std::true_type
{};
// helper struct for delaying the evaluation until we are sure
// that T is a class (i.e. until we are outside std::conditional
// below)
struct _check_for_index_operator
{
template<typename T>
struct evaluate
: public _is_indexable<T>
{};
};
}
// The rationale here is as follows:
// 1) If we have an array, we assume we can index into it. That isn't
// true if I isn't an integral type, but that's why we have the static assertion
// in the body - we could of course try and check whether I is integral, but I
// can't be arsed and want to provide a motivation to switch to a newer compiler...
// 2) If we have a class, we use SFINAE to check for operator[]
// 3) Otherwise, we assume that T does not support indexing
//
// In order to make sure that the compiler doesn't accidentally try the SFINAE evaluation
// on an array or a scalar, we have to resort to lazy evaluation.
template<typename T, typename I = std::size_t>
struct is_indexable
: public std::conditional<
std::is_array<T>::value,
detail::_lazy<std::true_type>,
typename std::conditional<
std::is_class<T>::value,
detail::_check_for_index_operator,
detail::_lazy<std::false_type>
>::type
>::type::template evaluate<T>::type
{
static_assert(std::is_same<I,std::size_t>::value,"Your compiler is broken and does not support checking for arbitrary index types");
};
#endif // defined(DOXYGEN) or HAVE_IS_INDEXABLE_SUPPORT
/** @} */
}
#endif