Mercurial > lbo > hg > ctmp
changeset 7:df07e266e3e5
Implement units library
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Wed, 25 Mar 2020 15:44:04 +0100 |
parents | cc90585713b4 |
children | b5a45f1fd761 |
files | CMakeLists.txt src/units.cc |
diffstat | 2 files changed, 177 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Wed Mar 25 13:12:14 2020 +0100 +++ b/CMakeLists.txt Wed Mar 25 15:44:04 2020 +0100 @@ -7,3 +7,4 @@ add_executable(example src/example.cc) add_executable(fac src/fac.cc) add_executable(replace_type src/replace_type.cc) +add_executable(units src/units.cc)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/units.cc Wed Mar 25 15:44:04 2020 +0100 @@ -0,0 +1,176 @@ +#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; +} +