2013-10-03 7 views
2

Я хотел бы реализовать блок percent с Boost.Units, чтобы безразмерная величина (например, отношение) могла быть представлена ​​в виде процента. Я успешно реализовал преобразование между единицами массовой плотности, но то же самое не работает для безразмерных единиц. Вот мой код (при условии, using namespace boost::units;):Использование Boost.Units для определения процента

// 
// gram per milliliter (g mL^-1) 
// 
namespace my { 
     struct gram_per_milliliter_base_unit : 
       base_unit<gram_per_milliliter_base_unit, mass_density_dimension, 1> 
     { 
       static std::string name() {return "gram per milliliter";} 
       static std::string symbol() {return "g mL^-1";} 
     }; 
     typedef gram_per_milliliter_base_unit::unit_type gram_per_milliliter_unit; 
     BOOST_UNITS_STATIC_CONSTANT(gram_per_milliliter, gram_per_milliliter_unit); 
     BOOST_UNITS_STATIC_CONSTANT(grams_per_milliliter, gram_per_milliliter_unit); 
} 
BOOST_UNITS_DEFINE_CONVERSION_FACTOR(
     my::gram_per_milliliter_base_unit, si::mass_density, double, 1.0e3 
); // 1 g mL^-1 == 1e3 kg m^-3 (SI unit) 
BOOST_UNITS_DEFAULT_CONVERSION(my::gram_per_milliliter_base_unit, si::mass_density); 

// 
// percentage (%) 
// 
namespace my { 
     struct percent_base_unit : 
       base_unit<percent_base_unit, dimensionless_type, 2> 
     { 
       static std::string name() {return "percent";} 
       static std::string symbol() {return "%";} 
     }; 
     typedef percent_base_unit::unit_type percent_unit; 
     BOOST_UNITS_STATIC_CONSTANT(percent, percent_unit); 
} 
BOOST_UNITS_DEFINE_CONVERSION_FACTOR(
     my::percent_base_unit, si::dimensionless, double, 1.0e-2 
); // 1 % == 1e-2 (SI dimensionless unit) 
BOOST_UNITS_DEFAULT_CONVERSION(my::percent_base_unit, si::dimensionless); 

В разделе «грамм на миллилитр» работает, как ожидалось: я могу скомпилировать этот код (предполагая, что using namespace my; а):

quantity<gram_per_milliliter_unit> q1my(3*grams_per_milliliter); 
quantity<si::mass_density> q1si(q1my); 
quantity<gram_per_milliliter_unit> q1back(q1si); 

Но следующий сбой компилировать на обоих преобразований:

quantity<percent_unit> q2my(3*percent); 
quantity<si::dimensionless> q2si(q2my); 
quantity<percent_unit> q2back(q2si); 

G ++ выходы: no matching function for call to 'conversion_factor(..., ...)'.

Связано это с тем фактом, что dimensionless_type, по-видимому, является маркером для конца списков типов?

Любая помощь или предложение были бы весьма признательны. Спасибо.

ответ

4

Связано ли это с тем, что размерность_типа, по-видимому, является маркером для конца списков типов?

Сорт. dimensionless_type неявно входит в каждую измерительную систему и извлекается из них одинаково, см. boost/units/dimensionless_units.hpp.

В вашем примере «процент», думаю, что ваша новая система измерения будет, и как бы вы указать его в соответствии с обычными правилами наддува единиц:

namespace my { 
    ... // define your own unit tag types 
    typedef make_system</* your system's units with dimensions*/>::type system; 
    ... // unit typedefs, consts, etc. 
} 

Так что, если вы говорите, ваш процент будет безразмерным, но отличным от оригинального безразмерного, вы игнорируете вышеупомянутую концепцию. Таким образом, вы не можете определить 2 безразмерных измерения внутри системы.

Любая помощь или предложение были бы весьма признательны.

Я вижу 3 варианта здесь:

  1. Override основной тип количество и принимать либо процент или обычный номер в CTOR. См http://www.boost.org/doc/libs/release/doc/html/boost_units/Examples.html#boost_units.Examples.UDTExample

  2. Если вы хотите отобразить вещи в процентах, вы можете попробовать использовать функцию автоматического масштабирования (никогда не делал это сам, но есть пример, а также - http://www.boost.org/doc/libs/release/doc/html/boost_units/Examples.html#boost_units.Examples.autoscale).

  3. Вы можете создать специальное настраиваемое измерение «процент» и явно преобразовать в/из процентных величин. Это, вероятно, ближе всего к вашим первоначальным намерениям, но автоматическое преобразование не всегда произойдет, поскольку библиотека не предназначена для «размерного анализа безразмерных величин».Вы можете увидеть, как уродливый результат, если вы пытаетесь заставить систему в автоматические преобразования:

    // 
    // percentage (%) 
    // 
    namespace my { 
        struct percent_base_dimension : 
         base_dimension<percent_base_dimension, 1> {}; 
        typedef percent_base_dimension::dimension_type percent_type; 
    
        struct percent_base_unit : 
         base_unit<percent_base_unit, percent_type, 1> 
        { 
         static std::string name() {return "percent";} 
         static std::string symbol() {return "%";} 
        }; 
        typedef make_system<percent_base_unit>::type system; 
        typedef percent_base_unit::unit_type percent_unit; 
        BOOST_UNITS_STATIC_CONSTANT(percent, percent_unit); 
    } 
    
    namespace boost { namespace units { 
    
    template<class T0, class T1> 
    struct conversion_helper<quantity<my::percent_unit, T0>, quantity<si::dimensionless, T1> > 
    { 
        static quantity<si::dimensionless, T1> convert(const quantity<my::percent_unit, T0>& source) 
        { 
         return(quantity<si::dimensionless, T1>::from_value(1e-2 * source.value())); 
        } 
    }; 
    
    template<class T0, class T1> 
    struct conversion_helper<quantity<si::dimensionless, T0>, quantity<my::percent_unit, T1> > 
    { 
        static quantity<my::percent_unit, T1> convert(const quantity<si::dimensionless, T0>& source) 
        { 
         return(quantity<my::percent_unit, T1>::from_value(1e+2 * source.value())); 
        } 
    }; 
    
    } } 
    
    int main() 
    { 
        using namespace my; 
    
        quantity<percent_unit> q2my(3*percent); 
    
        //quantity<si::dimensionless> q2si(q2my); 
        //The converter won't be picked up due to an explicit disable_if in quantity.hpp: 
        // typename boost::disable_if<detail::is_dimensionless_system<System2> >::type* = 0 
        //so we manually force the conversion here: 
        auto conv = conversion_helper<quantity<percent_unit>, quantity<si::dimensionless> >::convert; 
        quantity<si::dimensionless> q2si(conv(q2my)); 
    
        quantity<percent_unit> q2back(q2si); 
    
        std::cout 
         << "q2my: " << q2my << std::endl 
         << "q2si: " << q2si << std::endl 
         << "q2back: " << q2back << std::endl 
         ; 
    } 
    

    Так что это хорошая идея, чтобы сделать это вручную, что-то вроде

    namespace my { 
        template<class Y> 
        quantity<si::dimensionless, Y> units(const quantity<percent_unit, Y>& source) 
        { 
         return(quantity<si::dimensionless, Y>::from_value(1e-2 * source.value())); 
        } 
    
        template<class Y> 
        quantity<percent_unit, Y> percentage(const quantity<si::dimensionless, Y>& source) 
        { 
         return(quantity<percent_unit, Y>::from_value(1e+2 * source.value())); 
        } 
    
    } 
    
    int main() 
    { 
        using namespace my; 
    
        quantity<percent_unit> q2my(3*percent); 
        quantity<si::dimensionless> q2si(my::units(q2my)); 
        quantity<percent_unit> q2back(my::percentage(q2si)); 
    
        std::cout 
         << "q2my: " << q2my << std::endl 
         << "q2si: " << q2si << std::endl 
         << "q2back: " << q2back << std::endl 
         ; 
    } 
    

    или, еще лучше, используя преимущества проверки типов (так что вы можете только сделать ошибку в коэффициенте преобразования здесь):

    template<class Y> 
    quantity<si::dimensionless, Y> units(const quantity<percent_unit, Y>& source) 
    { 
        return quantity<si::dimensionless, Y>(1e-2 * source/percent); 
    } 
    
    template<class Y> 
    quantity<percent_unit, Y> percentage(const quantity<si::dimensionless, Y>& source) 
    { 
        return quantity<percent_unit, Y>(1e+2 * source * percent); 
    } 
    
+0

Какой отличный ответ! Благодарю. –