Boost logo

Boost :

From: Jaakko Jarvi (jajarvi_at_[hidden])
Date: 2003-08-13 18:06:18


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;
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk