Skip to content

Commit

Permalink
Disable explicit conversion for quantities of unrelated units
Browse files Browse the repository at this point in the history
This fixes boostorg#36.
  • Loading branch information
KaiHoewelmeyer-TomTom committed Feb 22, 2019
1 parent e895482 commit e53d8c9
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 8 deletions.
2 changes: 1 addition & 1 deletion include/boost/units/conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace boost {

namespace units {

template<class From, class To>
template<class From, class To, typename>
struct conversion_helper;

#ifdef BOOST_UNITS_DOXYGEN
Expand Down
18 changes: 17 additions & 1 deletion include/boost/units/detail/conversion_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <boost/mpl/divides.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/utility/enable_if.hpp>

#include <boost/units/heterogeneous_system.hpp>
#include <boost/units/homogeneous_system.hpp>
Expand Down Expand Up @@ -326,15 +327,30 @@ struct conversion_impl<0>
};
};

template<typename Unit1, typename Unit2>
class has_conversion_factor
{
typedef char yes[1];
typedef char no[2];

template<typename T, typename U> static yes& test(char(*)[sizeof(conversion_factor(T(), U()))]);

template<typename, typename> static no& test(...);

public:
enum { value = (sizeof(test<Unit1, Unit2>(0)) == sizeof(yes)) };
};

} // namespace detail

/// forward to conversion_factor (intentionally allowing ADL)
/// INTERNAL ONLY
template<class Unit1, class T1, class Unit2, class T2>
struct conversion_helper<quantity<Unit1, T1>, quantity<Unit2, T2> >
struct conversion_helper<quantity<Unit1, T1>, quantity<Unit2, T2>, typename boost::enable_if_c<detail::has_conversion_factor<Unit1, Unit2>::value>::type>
{
/// INTERNAL ONLY
typedef quantity<Unit2, T2> destination_type;

static BOOST_CONSTEXPR destination_type convert(const quantity<Unit1, T1>& source)
{
return(destination_type::from_value(static_cast<T2>(source.value() * conversion_factor(Unit1(), Unit2()))));
Expand Down
22 changes: 17 additions & 5 deletions include/boost/units/quantity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,22 @@ struct disable_if_is_same
typedef void type;
};

template<class T>
struct disable_if_is_same<T, T> {};
template<typename Quantity1, typename Quantity2>
struct has_conversion_helper
{
typedef char yes[1];
typedef char no[2];

template<typename T, typename U> static yes& test(char(*)[sizeof(conversion_helper<T,U>)]);

template<typename, typename> static no& test(...);

public:
enum { value = (sizeof(test<Quantity1, Quantity2>(0)) == sizeof(yes)) };
};

}

/// class declaration
template<class Unit,class Y>
class quantity
Expand Down Expand Up @@ -178,9 +189,10 @@ class quantity
#ifndef BOOST_NO_SFINAE

/// explicit conversion between different unit systems is allowed if implicit conversion is disallowed
template<class Unit2,class YY>
template<class Unit2,class YY>
explicit
BOOST_CONSTEXPR quantity(const quantity<Unit2,YY>& source,
BOOST_CONSTEXPR quantity(const quantity<Unit2,YY>& source,
typename boost::enable_if_c<detail::has_conversion_helper<quantity<Unit2,YY>,this_type>::value>::type* = 0,
typename boost::disable_if<
mpl::and_<
//is_implicitly_convertible should be undefined when the
Expand Down
2 changes: 1 addition & 1 deletion include/boost/units/units_fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ template<class T> struct is_quantity;
template<class T,class Dim> struct is_quantity_of_dimension;
template<class T,class System> struct is_quantity_of_system;

template<class From,class To> struct conversion_helper;
template<class From,class To, typename = void> struct conversion_helper;

template<class T> std::string to_string(const T&);
template<class T> std::string name_string(const T&);
Expand Down
17 changes: 17 additions & 0 deletions test/test_conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Test conversion between quantities.
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si.hpp>
#include <boost/units/systems/cgs.hpp>
#include <boost/units/base_units/metric/liter.hpp>
#include <boost/units/base_units/metric/minute.hpp>
#include <boost/units/base_units/si/second.hpp>
#include <boost/type_traits/is_constructible.hpp>

#include <iostream>

Expand Down Expand Up @@ -108,3 +112,16 @@ BOOST_AUTO_TEST_CASE(test_dimensionless_conversions) {
BOOST_CONSTEXPR_OR_CONST bu::quantity<bu::divide_typeof_helper<bu::cgs::mass, bu::si::mass>::type> dimensionless_test5(dimensionless_test4);
BOOST_UNITS_CHECK_CLOSE(dimensionless_test5.value(), 2e5);
}

BOOST_AUTO_TEST_CASE(test_unrelated_constructible) {
typedef boost::units::quantity<boost::units::metric::liter_base_unit::unit_type> liter;
typedef boost::units::quantity<boost::units::metric::minute_base_unit::unit_type> minute;
typedef boost::units::quantity<boost::units::si::second_base_unit::unit_type> second;
/* const minute a((liter())); */
BOOST_CHECK((!bu::detail::has_conversion_helper<liter, minute>::value));
BOOST_CHECK((bu::detail::has_conversion_helper<minute, second>::value));
typedef bu::conversion_helper<minute, second> cv;
cv();
const bool test_value = boost::is_constructible<liter, minute>::value;
BOOST_CHECK(!test_value);
}

0 comments on commit e53d8c9

Please sign in to comment.