view src/units.cc @ 7:df07e266e3e5

Implement units library
author Lewin Bormann <lbo@spheniscida.de>
date Wed, 25 Mar 2020 15:44:04 +0100
parents
children 25fd2f822fd7
line wrap: on
line source

#include <boost/mpl/at.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;
};

/// `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> uM;
typedef mpl::vector_c<int, 0, 1, 0, 0, 0, 0, 0> uKg;
typedef mpl::vector_c<int, 0, 0, 1, 0, 0, 0, 0> uS;
typedef mpl::vector_c<int, 0, 0, 0, 1, 0, 0, 0> uA;
typedef mpl::vector_c<int, 0, 0, 0, 0, 1, 0, 0> uCd;
typedef mpl::vector_c<int, 0, 0, 0, 0, 0, 1, 0> uSrad;
typedef mpl::vector_c<int, 0, 0, 0, 0, 0, 0, 1> uK;

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

// Unit prototypes contain a unit and know their name.

template <typename U>
struct unit_proto {
    typedef U type;
    std::string sym;
    unit_proto(void) : sym(unit_name<U>().name()) {}
    unit_proto(const std::string& sym) : sym(sym) {}
};

static const unit_proto<uM> meter;
static const unit_proto<uKg> kilogram;
static const unit_proto<uS> second;
static const unit_proto<uA> ampere;
static const unit_proto<uCd> candela;
static const unit_proto<uSrad> sterad;
static const unit_proto<uK> kelvin;

// Quantity

template <typename N, typename U>
struct quantity {
    N value;
    unit_proto<U> unit;

    // 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, unit_proto<U> un) : value(val), unit(un) {}
    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) {
        auto q = quantity<typename std::common_type<N, N2>::type, U>(
            value + other.value, unit);
        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) {
        typedef typename multiply_units<U, U2>::type resultunit;
        auto q = quantity<typename std::common_type<N, N2>::type, resultunit>(
            value * other.value, unit_proto<resultunit>());
        return q;
    }

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

template <typename N, typename U>
quantity<N, U> make_quant(N val, unit_proto<U> unit_pt) {
    quantity<N, U> q(val, unit_pt);
    return q;
}

int main(void) {
    auto x = make_quant(33.0f, second);
    auto y = make_quant(30.0f, meter);
    auto z = (x * x) / (x * y * 4.5);
    std::cout << std::string(z) << 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;
}