Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
587 views
in Technique[技术] by (71.8m points)

c++ - preferred mechanism to attach a type to a scalar?

[ edit: changed meters/yards to foo/bar; this isn't about converting meters to yards. ]

What's the best way to attach a type to a scalar such as a double? The typical use-case is units-of-measure (but I'm not looking for an actual implementation, boost has one).

This would appear to be a simple as:

template <typename T>
struct Double final
{
    typedef T type;
    double value;
};

namespace tags
{
    struct foo final {};
    struct bar final {};
}
constexpr double FOOS_TO_BARS_ = 3.141592654;
inline Double<tags::bar> to_bars(const Double<tags::foo>& foos)
{
    return Double<tags::bar> { foos.value * FOOS_TO_BARS_ };
}

static void test(double value)
{
    using namespace tags;
    const Double<foo> value_in_foos{ value };    
    const Double<bar> value_in_bars = to_bars(value_in_foos);
}

Is that really the case? Or are there hidden complexities or other important considerations to this approach?

This would seem far, far superior to

   inline double foos_to_bars(double foos)
   {
      return foos * FOOS_TO_BARS_;
   }

without adding hardly any complexity or overhead.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I'd go with a ratio-based approach, much like std::chrono. (Howard Hinnant shows it in his recent C++Con 2016 talk about <chrono>)

template<typename Ratio = std::ratio<1>, typename T = double>
struct Distance
{
    using ratio = Ratio;
    T value;
};

template<typename To, typename From>
To distance_cast(From f)
{
    using r = std::ratio_divide<typename To::ratio, typename From::ratio>;
    return To{ f.value * r::den / r::num };
}

using yard = Distance<std::ratio<10936133,10000000>>;
using meter = Distance<>;
using kilometer = Distance<std::kilo>;
using foot = Distance<std::ratio<3048,10000>>;

demo

This is a naive implementation and probably could be improved a lot (at the very least by allowing implicit casts where they're safe), but it's a proof of concept and it's trivially extensible.

Pros:

  • meter m = yard{10} is either a compile time error or a safe implicit conversion,
  • pretty type names, you'd have to work against the solution very hard to make an invalid conversion
  • simple to use

Cons:

  • Possible integer overflows/precision problems (may be alleviated by quality of implementation?)
  • may be non-trivial to implement correctly

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...