In our last exciting episode "Neal D. Becker" wrote: > User-Agent: KMail/1.5.3
> I'm thinking that it would be useful to implement a traits class that would > give the return type of mixed scalar-complex arithmetic operations. This > would allow one to write generic algorithms that operate on both scalar and > complex types, inferring the return type. > Any opinions? Good idea. That code probably exists in many places in boost in slightly different variations. I cleaned up a bit the code used in lambda for another project a while back. I'm including that code + some test cases. If you're willing to boostify the code, and write some docs, that'd be great. If you decide not to use the attached code, the code anyway collects the rules from the standard into one place. The right place for the code would be the type traits library I guess. Cheers, Jaakko Järvi The code: ------- type_promotion.hpp --------------- #include <boost/mpl/if.hpp> #include <boost/mpl/bool.hpp> #include <boost/static_assert.hpp> #include <boost/type_traits/is_reference.hpp> #include <boost/type_traits/is_const.hpp> #include <boost/type_traits/is_volatile.hpp> // complex needs to be forward declared namespace std { template<class T> class complex; } namespace detail { template <class T> struct promote_code; template <class A, class B> struct promotor; template <class T> struct promote_to_int; // A is guaranteed to have a higher promote code // we need to do this, since otherwise it would be really hard // to avoid ambigouous partial specializations for complex number promotion template <class A, class B> struct ordered_promotor { typedef A type; }; // The unsigned int promotion rule is this: // unsigned int to long if a long can hold all values of unsigned int, // otherwise go to unsigned long. template<> struct ordered_promotor<long, unsigned int> { typedef boost::mpl::if_< boost::mpl::bool_<(sizeof(long) <= sizeof(unsigned int))>, unsigned long, long >::type type; }; // complex numbers are a special case // this is not really the way complex type promotion is implemented // in the standard library. Actually, arithmetic ops for complex numbers // are only defined between: // complex<T> op complex<T> // complex<T> op T // T op complex<T> // but it does no harm to be a bit more thorough. // e.g. complex<float> + double -> complex<double> template <class A, class B> struct ordered_promotor<std::complex<A>, B> { typedef std::complex<typename promotor<A, B>::type> type; }; template <class A, class B> struct ordered_promotor<std::complex<A>, std::complex<B> > { typedef std::complex<typename promotor<A, B>::type> type; // we know that this will be complex<A> as A and B are ordered }; // gives the two types promoted template <class A, class B> struct promotor { private: // first step is to promote all integral types smaller than int to // int typedef typename promote_to_int<A>::type A2; typedef typename promote_to_int<B>::type B2; BOOST_STATIC_CONSTANT(int, A_code = promote_code<A2>::value); BOOST_STATIC_CONSTANT(int, B_code = promote_code<B2>::value); // check that our machinery knows about both type A and B BOOST_STATIC_ASSERT(A_code != -1 || !"Invalid left scalar argument type"); BOOST_STATIC_ASSERT(B_code != -1 || !"Invalid right scalar argument type"); public: typedef typename ordered_promotor< typename boost::mpl::if_<boost::mpl::bool_<(A_code >= B_code)>, A2, B2>::type, typename boost::mpl::if_<boost::mpl::bool_<(A_code < B_code)>, B2, A2>::type >::type type; }; template <class A> struct promote_code { static const int value = -1; }; // this means that a code is not defined for A // For the next 5 types // the promotion order is not important, but they must have distinct // values. template <> struct promote_code<bool> { static const int value = 10; }; template <> struct promote_code<char> { static const int value = 20; }; template <> struct promote_code<unsigned char> { static const int value = 30; }; template <> struct promote_code<signed char> { static const int value = 40; }; template <> struct promote_code<short int> { static const int value = 50; }; // ---------- template <> struct promote_code<int> { static const int value = 100; }; template <> struct promote_code<unsigned int> { static const int value = 200; }; template <> struct promote_code<long> { static const int value = 300; }; template <> struct promote_code<unsigned long> { static const int value = 400; }; template <> struct promote_code<float> { static const int value = 500; }; template <> struct promote_code<double> { static const int value = 600; }; template <> struct promote_code<long double> { static const int value = 700; }; // TODO: wchar_t template <> struct promote_code< std::complex<float> > { static const int value = 800; }; template <> struct promote_code< std::complex<double> > { static const int value = 900; }; template <> struct promote_code< std::complex<long double> > { static const int value = 1000; }; // -- int promotion ------------------------------------------- template <class T> struct promote_to_int { typedef T type; }; template <> struct promote_to_int<bool> { typedef int type; }; template <> struct promote_to_int<char> { typedef int type; }; template <> struct promote_to_int<unsigned char> { typedef int type; }; template <> struct promote_to_int<signed char> { typedef int type; }; template <> struct promote_to_int<short int> { typedef int type; }; // The unsigned short int promotion rule is this: // unsigned short int to signed int if a signed int can hold all values // of unsigned short int, otherwise go to unsigned int. template <> struct promote_to_int<unsigned short int> { typedef boost::mpl::if_< boost::mpl::bool_<(sizeof(int) <= sizeof(unsigned short int))>, unsigned int, int>::type type; }; } // end namespace detail // ------------------------------------------------------------------ // binary scalar traits -------------------------------------------- // ------------------------------------------------------------------ template<class A, class B> struct binary_scalar_traits { // These traits should always be called with plain types BOOST_STATIC_ASSERT(!(boost::is_reference<A>::value)); BOOST_STATIC_ASSERT(!(boost::is_const<A>::value)); BOOST_STATIC_ASSERT(!(boost::is_volatile<A>::value)); BOOST_STATIC_ASSERT(!(boost::is_reference<B>::value)); BOOST_STATIC_ASSERT(!(boost::is_const<B>::value)); BOOST_STATIC_ASSERT(!(boost::is_volatile<B>::value)); // promote to higher type typedef typename detail::promotor<A, B>::type type; }; -- type_promotion.cpp ------------------------------------------------------------------------- #include "type_promotion.hpp" #include <boost/type_traits/is_same.hpp> #include <complex> #include "boost/cstdlib.hpp" #define BIN_PROM(T1, T2) binary_scalar_traits<T1, T2 >::type #define PROMOTION_TEST(T1, T2, TRESULT) \ BOOST_STATIC_ASSERT( \ (boost::is_same< BIN_PROM(T1, T2), TRESULT >::value)) using std::complex; PROMOTION_TEST(char, char, int); PROMOTION_TEST(char, int, int); PROMOTION_TEST(int, char, int); PROMOTION_TEST(short int, int, int); PROMOTION_TEST(int, short int, int); PROMOTION_TEST(bool, bool, int); PROMOTION_TEST(char, bool, int); PROMOTION_TEST(char, bool, int); PROMOTION_TEST(float, float, float); PROMOTION_TEST(float, double, double); PROMOTION_TEST(double, float, double); PROMOTION_TEST(complex<float>, complex<float>, complex<float> ); PROMOTION_TEST(complex<double>, complex<double>, complex<double> ); PROMOTION_TEST(complex<long double>, complex<long double>, complex<long double> ); PROMOTION_TEST(float, complex<float>, complex<float>); PROMOTION_TEST(double, complex<double>, complex<double>); PROMOTION_TEST(long double, complex<long double>, complex<long double>); PROMOTION_TEST(complex<float>, float, complex<float>); PROMOTION_TEST(complex<double>, double, complex<double>); PROMOTION_TEST(complex<long double>, double, complex<long double>); int main(int argc, char** argv) { return boost::exit_success; } _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost