view src/units.cc @ 11:6461c5c09e5c

units: Minor bug fixes
author Lewin Bormann <lbo@spheniscida.de>
date Wed, 25 Mar 2020 19:56:46 +0100
parents 43e0e42c373d
children 1ebc743c3d85
line wrap: on
line source

#include <boost/mpl/at.hpp>
#include <boost/static_assert.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/vector_c.hpp>
#include <iostream>
#include <sstream>

namespace mpl = boost::mpl;

template <typename A1, typename A2>
struct plus {
    typedef typename mpl::integral_c<decltype(A1::value + A2::value),
                                     A1::value + A2::value>::type type;
};

template <typename A1, typename A2>
struct minus {
    typedef typename mpl::integral_c<
        typename std::make_signed<decltype(A1::value - A2::value)>::type,
        A1::value - A2::value>::type type;
};

template <typename A1>
struct negate {
    typedef typename mpl::integral_c<
        typename std::make_signed<decltype(A1::value)>::type, -A1::value>::type
        type;
};

/// `quote`
template <template <typename A1, typename... Rest> typename Op>
struct make_lambda {
    template <typename A1, typename... Rest>
    struct apply {
        typedef typename Op<A1, Rest...>::type type;
    };
};

// Unit definitions
typedef mpl::vector_c<int, 1, 0, 0, 0, 0, 0, 0> METER;
typedef mpl::vector_c<int, 0, 1, 0, 0, 0, 0, 0> KILOGRAM;
typedef mpl::vector_c<int, 0, 0, 1, 0, 0, 0, 0> SECOND;
typedef mpl::vector_c<int, 0, 0, 0, 1, 0, 0, 0> AMPERE;
typedef mpl::vector_c<int, 0, 0, 0, 0, 1, 0, 0> CANDELA;
typedef mpl::vector_c<int, 0, 0, 0, 0, 0, 1, 0> SRAD;
typedef mpl::vector_c<int, 0, 0, 0, 0, 0, 0, 1> KELVIN;

template <typename U>
struct unit_name {
    std::string name(void) {
        std::ostringstream s;
#define EXTRACT_EXPONENT(n) int U##n = mpl::at<U, mpl::int_<n>>::type::value;
        EXTRACT_EXPONENT(0);
        EXTRACT_EXPONENT(1);
        EXTRACT_EXPONENT(2);
        EXTRACT_EXPONENT(3);
        EXTRACT_EXPONENT(4);
        EXTRACT_EXPONENT(5);
        EXTRACT_EXPONENT(6);
#define FORMAT_UNIT(USYM, UNAME)         \
    if (USYM) {                          \
        s << #UNAME;                     \
        if (USYM != 1) s << "^" << USYM; \
        s << " ";                        \
    }
        FORMAT_UNIT(U0, m);
        FORMAT_UNIT(U1, kg);
        FORMAT_UNIT(U2, s);
        FORMAT_UNIT(U3, A);
        FORMAT_UNIT(U4, cd);
        FORMAT_UNIT(U5, srad);
        FORMAT_UNIT(U6, K);
        return s.str();
    }
};

template <typename U1, typename U2>
struct multiply_units {
    typedef typename mpl::transform<U1, U2, make_lambda<plus>>::type type;
};

template <typename U1, typename U2>
struct divide_units {
    typedef typename mpl::transform<U1, U2, make_lambda<minus>>::type type;
};

template <typename U>
struct invert_units {
    typedef typename mpl::transform<U, make_lambda<negate>>::type type;
};

// Quantity

template <typename N, typename U, std::enable_if_t<std::is_arithmetic<N>::value, int> = 0>
struct quantity {
    N value;

    // quantity(const quantity<N,U>& other) : value(other.value),
    // unit(other.unit) {} const quantity<N,U>& operator=(const quantity<N,U>&
    // other) { value = other.value; unit = other.unit; }
    quantity(N val) : value(val) {}
    template<typename U2>
    quantity(const quantity<N, U2>& other) : value(other.value) {
        BOOST_STATIC_ASSERT(mpl::equal<U, U2>::type::value);
    }
    operator std::string(void) {
        std::ostringstream o;
        o << value << " " << unit_name<U>().name();
        return o.str();
    }

    template <typename N2>
    quantity<typename std::common_type<N, N2>::type, U> operator+(
        const quantity<N2, U>& other) const {
        auto q = quantity<typename std::common_type<N, N2>::type, U>(
            value + other.value);
        return q;
    }

    template <typename N2, typename U2>
    quantity<typename std::common_type<N, N2>::type,
             typename multiply_units<U, U2>::type>
    operator*(const quantity<N2, U2>& other) const {
        typedef typename multiply_units<U, U2>::type resultunit;
        auto q = quantity<typename std::common_type<N, N2>::type, resultunit>(
            value * other.value);
        return q;
    }

    template <typename N2, typename U2>
    quantity<typename std::common_type<N, N2>::type,
             typename divide_units<U, U2>::type>
    operator/(const quantity<N2, U2>& other) const {
        typedef typename divide_units<U, U2>::type resultunit;
        auto q = quantity<typename std::common_type<N, N2>::type, resultunit>(
            value / other.value);
        return q;
    }
};

// Scalar multiplication/division
template <typename N1, typename N2, typename U,
          typename std::enable_if<std::is_arithmetic<N1>::value, int>::type = 0>
quantity<typename std::common_type<N1, N2>::type, U> operator*(
    const quantity<N2, U>& second, const N1& first) {
    auto q = quantity<typename std::common_type<N1, N2>::type, U>(second.value *
                                                                  first);
    return q;
}
template <typename N1, typename N2, typename U,
          typename std::enable_if<std::is_arithmetic<N1>::value, int>::type = 0>
quantity<typename std::common_type<N1, N2>::type, U> operator*(
    const N1& first, const quantity<N2, U>& second) {
    return second * first;
}

template <typename N1, typename N2, typename U,
          typename std::enable_if<std::is_arithmetic<N1>::value, int>::type = 0>
quantity<typename std::common_type<N1, N2>::type, U> operator/(
    quantity<N1, U> first, N2 second) {
    auto q = quantity<typename std::common_type<N1, N2>::type, U>(first.value /
                                                                  second);
    return q;
}
template <typename N1, typename N2, typename U,
          typename std::enable_if<std::is_arithmetic<N1>::value, int>::type = 0>
quantity<typename std::common_type<N1, N2>::type,
         typename mpl::transform<U, make_lambda<negate>>::type>
operator/(N1 first, quantity<N2, U> second) {
    typedef typename mpl::transform<U, make_lambda<negate>>::type inverted;
    return quantity<typename std::common_type<N1, N2>::type, inverted>(
        first / second.value);
}

static const quantity<int, METER> meter(1);
static const quantity<int, SECOND> second(1);
static const quantity<int, KILOGRAM> kilogram(1);
static const quantity<int, AMPERE> ampere(1);
static const quantity<int, CANDELA> candela(1);
static const quantity<int, SRAD> srad(1);
static const quantity<int, KELVIN> kelvin(1);

static const quantity<int, divide_units<multiply_units<KILOGRAM, METER>::type, multiply_units<SECOND, SECOND>::type>::type> newton(1);

int main(void) {
    auto force = 1 * kilogram * meter / (second*second);
    std::cout << std::string(4 * newton + force) << std::endl;
    return 0;
}

void test(void) {
    typedef mpl::vector_c<int, 1, 2, 3> a;
    typedef mpl::vector_c<int, 2, 3, 4> b;
    typedef make_lambda<mpl::plus> plus;

    typedef mpl::transform<a, b, plus>::type result;
    typedef mpl::at<result, mpl::int_<2>>::type first;
    std::cout << first::value << std::endl;
}