Problem with C ++ template metaprogram when choosing type

The following code illustrates my problem

#include <type_traits> #include <limits> #include <cstdint> #include <boost/mpl/if.hpp> #include <boost/mpl/eval_if.hpp> #include <boost/mpl/identity.hpp> ///////////////////////////////////////////////////////////////// // safe_signed_range template < std::intmax_t MIN, std::intmax_t MAX > struct safe_signed_range { }; ///////////////////////////////////////////////////////////////// // safe_unsigned_range template < std::uintmax_t MIN, std::uintmax_t MAX > struct safe_unsigned_range { }; template<class T, class U> using calculate_max_t = typename boost::mpl::if_c< std::numeric_limits<T>::is_signed || std::numeric_limits<U>::is_signed, std::intmax_t, std::uintmax_t >::type; template<typename T, typename U> struct test { typedef calculate_max_t<T, U> max_t; static_assert(std::is_same<max_t, std::intmax_t>::value, "unexpected value for max_t"); static_assert(std::is_signed<max_t>::value, "check parameter"); /* typedef typename boost::mpl::if_c< std::is_signed<max_t>::value, safe_signed_range<std::numeric_limits<max_t>::min(), std::numeric_limits<max_t>::max()>, safe_unsigned_range<std::numeric_limits<max_t>::min(), std::numeric_limits<max_t>::max()> >::type type; */ typedef typename boost::mpl::eval_if_c< std::is_signed<max_t>::value, boost::mpl::identity<safe_signed_range<std::numeric_limits<max_t>::min(), std::numeric_limits<max_t>::max()> >, // error shows up here boost::mpl::identity<safe_unsigned_range<std::numeric_limits<max_t>::min(), std::numeric_limits<max_t>::max()> > >::type type; }; test<int, int> t1; //test<int, unsigned> t2; //test<unsigned, int> t3; //test<unsigned, unsigned> t4; int main(){ return 0; } 

an error appears with the Clang compiler as

/Users/robertramey/WorkingProjects/safe_numerics/test/test_z.cpp:116:50: Non-type template argument evaluates to -9223372036854775808, which cannot be narrowed to type 'std::uintmax_t' (aka 'unsigned long')

It seems that Boost Mpl is not choosing the right type. At first I suspected (and actually still suspect) that all the arguments in if are expanding, so I changed the use of eval_if, but still I have a problem. I turned on static_assert to check the parameters and can compromise it with the simplest tests - although it does not work in all combinations. If anyone can explain my mistake, I would be grateful.

+5
source share
1 answer

The problem is that you are creating a template (regardless of boost::mpl::identity ) that is not valid for the current type safe_unsigned_range . The solution to this is to defer the creation of the template based on the Boolean predicate passed to boost::mpl::eval_if_c .

To do this, we must first write our own version of identity for both types of ranges:

 template<typename Integer, Integer Top, Integer Bottom> struct defer_unsigned_lazily { using type = safe_unsigned_range<Top, Bottom>; }; template<typename Integer, Integer Top, Integer Bottom> struct defer_signed_lazily { using type = safe_signed_range<Top, Bottom>; }; 

The way this works is that the instance will not run until we make typename X::type essentially giving us lazy semantics like boost::mpl::identity .

Then we change the typedef like this:

 using limits = std::numeric_limits<max_t>; typedef typename boost::mpl::eval_if_c< std::is_signed<max_t>::value, defer_signed_lazily<max_t, limits::min(), limits::max()>, defer_unsigned_lazily<max_t, limits::min(), limits::max()> >::type type; 

After that, it should compile as expected on both Clang and GCC.

Demo

+6
source

All Articles