2

I'm trying to figure out if boost-unit can be used in one of my projects. With most of the functions I'm quite satisfied. But one features I'm really unable to make on my own.

Especially, I'm looking for an easy to use function that gives me the power of a certain base unit of a given quantity. It's somehow the reverse of the pow function. Actually, io.hpp implements something similar, but I neither want to copy and paste all the stuff there, nor I want to delve into this template code.

Is there a simple workaround for the getPow function depicted below?

#include <boost/units/systems/si.hpp>
#include <boost/units/io.hpp>
#include <iostream>

using namespace boost::units;

template<typename U, typename V> 
int getPow(quantity<U>& q, V) {
    int ret = 0;
    // What do I have to write here?
    return ret;
}

int main(int argc, char** args) {
    auto q = 1.*si::meter*si::meter/si::second;
    std::cout << q << std::endl;
    std::cout << getPow(q, si::meter) << std::endl; // Should output 2
    std::cout << getPow(q, si::second) << std::endl; // Should output -1
}
Aleph0
  • 5,816
  • 4
  • 29
  • 80
  • 1
    Maybe you can test `symbol_string(U())` and `symbol_string(V())` to see the unit, and parse the string of the unit to check the pow? – Mine Jul 08 '16 at 09:08
  • Good advice. Even though a bit complicated. :-) – Aleph0 Jul 08 '16 at 09:35

1 Answers1

1

The following code implements what you need. I have not used boost::units before, so there might be a more idiomatic way to solve the problem.

#include <type_traits>
#include <iostream>

#include <boost/mpl/at.hpp>
#include <boost/mpl/is_sequence.hpp>
#include <boost/mpl/find_if.hpp>

#include <boost/units/systems/si.hpp>
#include <boost/units/io.hpp>

using namespace boost::units;

template <typename T>
using get_dimension_t = typename T::unit_type::dimension_type;

template<typename T>
struct get_tag
{
    using type = typename T::tag_type;
};

template <typename T>
using get_tag_t = typename T::tag_type;

template< class ... > using void_t = void;

template<typename U, typename V> 
struct Exponent
{
    template <typename Dim, typename Enable = void>
    struct get
    {
        static constexpr int value = 0;
    };

    template <typename Dim>
    struct get<Dim, void_t<typename Dim::value_type>>
    {
         using Value = typename Dim::value_type;
         static constexpr int value = Value::Numerator / Value::Denominator;
    };

    using dimension_V = get_dimension_t<V>;

    using tag_to_search_for = typename get_tag<typename boost::mpl::at_c<dimension_V, 0>::type>::type;

    using dimension_U = get_dimension_t<U>;

    using iter = typename boost::mpl::find_if<dimension_U, std::is_same<get_tag<boost::mpl::_1>, tag_to_search_for> >::type;

    using Dim = typename boost::mpl::deref<iter>::type;

    constexpr static int value = get<Dim>::value;

};

template <typename U, typename V>
constexpr auto getExponent(U&& u, V&& v)
{
    return Exponent<std::decay_t<U>, std::decay_t<V>>::value;
}


int main(int argc, char** args) {
    auto q = 1.*si::meter*si::meter/si::second;
    std::cout << q << std::endl;
    std::cout << getExponent(q, si::meter) << std::endl; // Should output 2
    std::cout << getExponent(q, si::second) << std::endl; // Should output -1

    auto r = 1.*si::radian;
    std::cout << getExponent(r, si::meter) << std::endl; // Should output 0
}

live example

m.s.
  • 16,063
  • 7
  • 53
  • 88
  • Wow! I really liked this solution. Boost.Mpl really simplifies template metaprogramming and template aliases are just great to enhance readability. I really have to train my abilities using this library. Your solution is working for my posted examples, but there is still a drawback in case `q` is something like `auto q = 1.*si::radian`. Actually, I don't know beforehand what `q` might be, so that I also have to handle this case. I think the `boost::mpl::find_if` cannot find `si::meter` in this case, so that `Dim` is left undefined. Hence `getPow(q, si::meter)` will not compile. – Aleph0 Jul 11 '16 at 13:46
  • @FrankSimon what should `getExponent(1.*si::radian, si::meter)` return? zero? – m.s. Jul 13 '16 at 09:19
  • Yes. It should return `0`. Yesterday I tried to fix the remaining part by myself, but still failed. My attempt was to use `using Contains = std::is_same::type;` to check if the quantity is dimensionless. Afterwards I tried to utilize `ResultType = mpl::eval_if::type` in order to extract the result. But I'm not able to figure out what to plug in for `A` and `B`. Any idea?? – Aleph0 Jul 13 '16 at 09:23
  • @FrankSimon I updated my answer so now it will return `0` – m.s. Jul 13 '16 at 09:54
  • 1
    Much more simpler, than I would have thought or even imagined. That is so cool. Many thanks. Seems that you are using template metaprogramming as a daily task. :-) Too bad that one can only upvote once here. :-) – Aleph0 Jul 13 '16 at 09:57