Limiting range of value types in C++

前端 未结 9 693
伪装坚强ぢ
伪装坚强ぢ 2020-11-28 10:33

Suppose I have a LimitedValue class which holds a value, and is parameterized on int types \'min\' and \'max\'. You\'d use it as a container for holding values which can on

相关标签:
9条回答
  • 2020-11-28 11:00

    I wrote a C++ class that imitates the functionality of Ada's range.

    It is based on templates, similar to the solutions provided here.

    If something like this is to be used in a real project, it will be used in a very fundamental way. Subtle bugs or misunderstandings can be disastrous.

    Therefore, although it is a small library without a lot of code, in my opinion provision of unit tests and clear design philosophy are very important.

    Feel free to try it and please tell me if you find any problems.

    https://github.com/alkhimey/ConstrainedTypes

    http://www.nihamkin.com/2014/09/05/range-constrained-types-in-c++/

    0 讨论(0)
  • 2020-11-28 11:08

    This is actually a complex matter and I have tackled it for a while...

    Now I have a publicly available library that will allow you to limit floating points and integers in your code so you can make more sure that they are valid at all time.

    Not only that you can turn off the limits in your final release version and that means the types pretty much become the same as a typedef.

    Define your type as:

    typedef controlled_vars::limited_fauto_init<float, 0, 360> angle_t;
    

    And when you don't define the CONTROLLED_VARS_DEBUG and CONTROLLED_VARS_LIMITED flags, you get pretty much the same as this:

    typedef float angle_t;
    

    These classes are generated so they include all the necessary operators for you to not suffer too much when using them. That means you can see your angle_t nearly as a float.

    angle_t a;
    a += 35;
    

    Will work as expected (and throw if a + 35 > 360).

    http://snapwebsites.org/project/controlled-vars

    I know this was posted in 2008... but I don't see any good link to a top library that offers this functionality!?

    0 讨论(0)
  • 2020-11-28 11:08

    I'd like to offer an alternate version for Kasprzol's solution: The proposed approach always uses bounds of type int. You can get some more flexibility and type safety with an implementation such as this:

    template<typename T, T min, T max>
    class Bounded {
    private:
        T _value;
    public:
        Bounded(T value) : _value(min) {
            if (value <= max && value >= min) {
                _value = value;
           } else {
               // XXX throw your runtime error/exception...
           }
        }
        Bounded(const Bounded<T, min, max>& b)
            : _value(b._value){ }
    };
    

    This will allow the type checker to catch obvious miss assignments such as:

    Bounded<int, 1, 5> b1(1);
    Bounded<int, 1, 4> b2(b1); // <-- won't compile: type mismatch
    

    However, the more advanced relationships where you want to check whether the range of one template instance is included within the range of another instance cannot be expressed in the C++ template mechanism.

    Every Bounded specification becomes a new type. Thus the compiler can check for type mismatches. It cannot check for more advanced relationships that might exist for those types.

    0 讨论(0)
  • 2020-11-28 11:10

    The bounded::integer library does what you want (for integer types only). http://doublewise.net/c++/bounded/

    (In the interests of full disclosure, I am the author of this library)

    It differs from other libraries that attempt to provide "safe integers" in a significant way: it tracks integer bounds. I think this is best shown by example:

    auto x = bounded::checked_integer<0, 7>(f());
    auto y = 7_bi;
    auto z = x + y;
    // decltype(z) == bounded::checked_integer<7, 14>
    static_assert(z >= 7_bi);
    static_assert(z <= 14_bi);
    

    x is an integer type that is between 0 and 7. y is an integer type between 7 and 7. z is an integer type between 7 and 14. All of this information is known at compile time, which is why we are able to static_assert on it, even though the value of z is not a compile-time constant.

    z = 10_bi;
    z = x;
    static_assert(!std::is_assignable<decltype((z)), decltype(0_bi)>::value);
    

    The first assignment, z = 10_bi, is unchecked. This is because the compiler can prove that 10 falls within the range of z.

    The second assignment, z = x, checks that the value of x is within the range of z. If not, it throws an exception (the exact behavior depends on the type of integer you use, there are many policies of what to do).

    The third line, the static_assert, shows that it is a compile-time error to assign from a type that has no overlap at all. The compiler already knows this is an error and stops you.

    The library does not implicitly convert to the underlying type, as this can cause many situations where you try to prevent something but it happens due to conversions. It does allow explicit conversion.

    0 讨论(0)
  • 2020-11-28 11:11

    One thing to remember about templates is that each invocation of a unique set of template parameters will wind up generating a "unique" class for which comparisons and assignments will generate a compile error. There may be some meta-programming gurus that might know how to work around this but I am not one of them. My approach would be to implement these in a class with run-time checks and overloaded comparison and assignment operators.

    0 讨论(0)
  • 2020-11-28 11:18

    The Boost Constrained Value library(1) allows you to add constrains to data types.

    But you have to read the advice "Why C++'s floating point types shouldn't be used with bounded objects?" when you like to use it with float types (as illustrated in your example).

    (1) The Boost Constrained Value library is not an official Boost library yet.

    0 讨论(0)
提交回复
热议问题