Search code examples
c++templatesboostboost-units

Get power of base unit of a given quantity with boost-unit


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
}

Solution

  • 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