diff --git a/cmake/modules/CheckCXX11Features.cmake b/cmake/modules/CheckCXX11Features.cmake index 611cacf72724fe9cc2460b348b1683c9dec4fc74..85ad3689ddc3d7d0768e5c0014716a8b13fb9fbf 100644 --- a/cmake/modules/CheckCXX11Features.cmake +++ b/cmake/modules/CheckCXX11Features.cmake @@ -217,6 +217,55 @@ check_cxx_source_compiles(" " HAVE_STD_DECLVAL ) +# full support for is_indexable (checking whether a type supports operator[]) +check_cxx_source_compiles(" + #include <utility> + #include <type_traits> + #include <array> + + template <class T> + typename std::add_rvalue_reference<T>::type declval(); + + namespace detail { + + template<typename T, typename I, typename = int> + struct _is_indexable + : public std::false_type + {}; + + template<typename T, typename I> + struct _is_indexable<T,I,typename std::enable_if<(sizeof(declval<T>()[declval<I>()]) > 0),int>::type> + : public std::true_type + {}; + + } + + template<typename T, typename I = std::size_t> + struct is_indexable + : public detail::_is_indexable<T,I> + {}; + + struct foo_type {}; + + int main() + { + double x; + std::array<double,4> y; + double z[5]; + foo_type f; + + static_assert(not is_indexable<decltype(x)>::value,\"scalar type\"); + static_assert(is_indexable<decltype(y)>::value,\"indexable class\"); + static_assert(is_indexable<decltype(z)>::value,\"array\"); + static_assert(not is_indexable<decltype(f)>::value,\"not indexable class\"); + static_assert(not is_indexable<decltype(y),foo_type>::value,\"custom index type\"); + + return 0; + } +" HAVE_IS_INDEXABLE_SUPPORT + ) + + cmake_pop_check_state() # find the threading library diff --git a/config.h.cmake b/config.h.cmake index ce65990631fbd8d02036cb3f6f230bfb078533b8..9f6b370c1fd29e27f1d26c75db5e68ccd9a50db0 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -116,6 +116,9 @@ /* Define to 1 if C++11 std::declval() is supported */ #cmakedefine HAVE_STD_DECLVAL 1 +/* Define to 1 if the compiler properly supports testing for operator[] */ +#cmakedefine HAVE_IS_INDEXABLE_SUPPORT 1 + /* does the compiler support the keyword 'final'? */ #cmakedefine HAVE_KEYWORD_FINAL 1 diff --git a/dune/common/typetraits.hh b/dune/common/typetraits.hh index 64f745c6e6254141201ea30687353c9d93ee8603..933a43797806ad3ebf0eb60a31c22e950c6bd33d 100644 --- a/dune/common/typetraits.hh +++ b/dune/common/typetraits.hh @@ -6,6 +6,7 @@ #include <type_traits> #include <dune/common/deprecated.hh> +#include <dune/common/std/utility.hh> namespace Dune { @@ -404,23 +405,123 @@ namespace Dune static const bool value = true; }; - namespace { - template<typename T, typename I, decltype(*(static_cast<T*>(nullptr))[*(static_cast<T*>(nullptr))],0) = 0> - auto _is_indexable(T*) -> std::true_type; +#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> - auto _is_indexable(void*) -> std::false_type; + struct _is_indexable<T,I,typename std::enable_if<(sizeof(Std::declval<T>()[Std::declval<I>()]) > 0),int>::type> + : public std::true_type + {}; } +#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. + /** + * \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 - : public decltype(_is_indexable<T,I>(static_cast<T*>(nullptr))) + : public detail::_is_indexable<T,I> {}; + +#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 diff --git a/m4/CMakeLists.txt b/m4/CMakeLists.txt index e91561816f602a71fa0c6727bf6ce645fd4fd157..749751f13364cf60271f8d2e436512cdb9413af4 100644 --- a/m4/CMakeLists.txt +++ b/m4/CMakeLists.txt @@ -8,6 +8,7 @@ install(PROGRAMS ax_lang_compiler_ms.m4 boost_fusion.m4 cxx11_compiler.m4 + cxx11_is_indexable_support.m4 cxx0x_nullptr.m4 cxx11_constexpr.m4 cxx11_final.m4 diff --git a/m4/Makefile.am b/m4/Makefile.am index a2f87b8d3b4f5913b3cc696dd9358556a1987896..ea8865717ba68265572098b3def163debb257bf6 100644 --- a/m4/Makefile.am +++ b/m4/Makefile.am @@ -14,6 +14,7 @@ ALLM4S = \ cxx0x_nullptr.m4 \ cxx11_constexpr.m4 \ cxx11_final.m4 \ + cxx11_is_indexable_support.m4 \ cxx11_noexcept.m4 \ cxx11_range_based_for.m4 \ cxx11_std_declval.m4 \ diff --git a/m4/cxx11_is_indexable_support.m4 b/m4/cxx11_is_indexable_support.m4 new file mode 100644 index 0000000000000000000000000000000000000000..ff5aba8f00348a7181ec0de9ab345d7d7b6de537 --- /dev/null +++ b/m4/cxx11_is_indexable_support.m4 @@ -0,0 +1,64 @@ +# tests whether the compiler properly supports testing for operator[] +# the associated macro is called HAVE_IS_INDEXABLE_SUPPORT + +AC_DEFUN([DUNE_CXX11_IS_INDEXABLE_SUPPORT_CHECK],[ + AC_CACHE_CHECK([whether the compiler properly supports testing for operator[[]]], + dune_cv_cxx11_is_indexable_support, + [ + AC_REQUIRE([AC_PROG_CXX]) + AC_REQUIRE([CXX11]) + AC_LANG_PUSH([C++]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM( + [ + #include <utility> + #include <type_traits> + #include <array> + + template <class T> + typename std::add_rvalue_reference<T>::type declval(); + + namespace detail { + + template<typename T, typename I, typename = int> + struct _is_indexable + : public std::false_type + {}; + + template<typename T, typename I> + struct _is_indexable<T,I,typename std::enable_if<(sizeof(declval<T>()[[declval<I>()]]) > 0),int>::type> + : public std::true_type + {}; + + } + + template<typename T, typename I = std::size_t> + struct is_indexable + : public detail::_is_indexable<T,I> + {}; + + struct foo_type {}; + + ],[ + double x; + std::array<double,4> y; + double z[[5]]; + foo_type f; + + static_assert(not is_indexable<decltype(x)>::value,"scalar type"); + static_assert(is_indexable<decltype(y)>::value,"indexable class"); + static_assert(is_indexable<decltype(z)>::value,"array"); + static_assert(not is_indexable<decltype(f)>::value,"not indexable class"); + static_assert(not is_indexable<decltype(y),foo_type>::value,"custom index type"); + + return 0; + ])], + dune_cv_cxx11_is_indexable_support=yes, + dune_cv_cxx11_is_indexable_support=no) + AC_LANG_POP + ]) + if test "$dune_cv_cxx11_is_indexable_support" = yes; then + AC_DEFINE(HAVE_IS_INDEXABLE_SUPPORT, 1, + [Define to 1 if the compiler properly supports testing for operator[[]]]) + fi +]) diff --git a/m4/dune_common.m4 b/m4/dune_common.m4 index a065f2e2c66aeb6aeb1c22543f9fb0d657c912e2..9dcf65e6e85128c186a545677cf2caed1724fd53 100644 --- a/m4/dune_common.m4 +++ b/m4/dune_common.m4 @@ -28,6 +28,7 @@ AC_DEFUN([DUNE_COMMON_CHECKS], AC_REQUIRE([DUNE_CHECKFINAL]) AC_REQUIRE([DUNE_CHECKUNUSED]) AC_REQUIRE([DUNE_CXX11_STD_DECLVAL_CHECK]) + AC_REQUIRE([DUNE_CXX11_IS_INDEXABLE_SUPPORT_CHECK]) AC_REQUIRE([DUNE_CHECK_CXA_DEMANGLE]) AC_REQUIRE([DUNE_SET_MINIMAL_DEBUG_LEVEL]) AC_REQUIRE([DUNE_PATH_XDR]) @@ -40,7 +41,7 @@ AC_DEFUN([DUNE_COMMON_CHECKS], ]) DUNE_ADD_MODULE_DEPS([dune-common], [STDTHREAD], [${STDTHREAD_CPPFLAGS}], [${STDTHREAD_LDFLAGS}], [${STDTHREAD_LIBS}]) - + dnl check for programs AC_REQUIRE([AC_PROG_CC]) # add -Wall if the compiler is gcc