Range::contains() method

Just found out (the hard way :), that when using Range::contains() method, end of range is considered to be exclusive. For example, if i define range from 1 to 10, then 10 is not considered to be inside this range, which is not really true.

Any idea why is this so ??

Why is that not really true? I depends on how you define your range, and in the JUCE case the docs state that the end value is exclusive.

There’s no right or wrong way to define inclusivity, but the way we do it is by far the most common use-case for it.

I think the fact that none of us ever bothered to add a “containsInclusive” method says a lot about how rarely that tends to be needed in real situations.

I think when someone asks “Give me a number between 1 and 10”, only a pedant would exclude the numbers 1 and 10. So here lies the problem; it’s more about language, and the default understanding is that it’s inclusive. Of course if people don’t read the documentation fully then that’s a different story altogether. :smiley:

If there is a queue with 10 elements and you are element 10 - are you inside this queue ?? Of course you are. Or maybe not ?? :slight_smile:

Why would i need to define range with eleven elements, when 11th is not there??

Not if you use zero-based indexing, as C++ does.

I understand - it is not really a rocket science, but if class is there, why not use it…

It seems that you are confusing range with container. For instance, a std::vector v with 3 elements inside it has a size of 3, and I can access the third element by doing *(v.begin() + 3), but if I want to copy that whole vector, I might use the iterator range that represents it all, which is {v.begin(), v.end()}. v.end() is not inside the vector, though it is used to represent the range of the vector.

A very common use case are key/velocity ranges in multisamples. The ranges shown in the GUI (note number / velocity) are usually inclusive.

Thx for your help …

So, in practice - lets suppose we have 10 elements defined as range (1,10) …

How would you test in your code, if this range actually contains element 10 ???

Thx!

That’s 9 elements, not 10.

Hmm, is this like general rule for ranges? Why is that 9 elements and not 10? Sorry, i am really not trying to push it, but just want to understand.

Thx!

Edit:
Ok, i see, i have to increase end value for one with all my range objects, and then it should work as expected. Anyway i will add containsinclusive() method and it’s done :slight_smile:

It’s a general rule, probably resulting from early 0 indexed based programming languages. For example in Python range (0, 4) (or just range (4)) will iterate over the values [0, 1, 2, 3]. It’s the same in both Swift and Rust.

1 Like

Ah, ok, thank you for the explanation - everything is perfectly clear now.

Cheers!

Just got tripped up again by the Range class.

I’d agree with that, but I think one issue here is an inconsistent sense of “inclusivity” between Range::contains and Range::clipValue.

contains “Returns true if the given position lies inside this range.”
clipValue “Returns the nearest value to the one supplied, which lies within the range.”

const Range<int> midiValueRange (0, 127);
bool result = midiValueRange.contains (127);  // false
int clipped = midiValueRange.clipValue (127);  // 127

So 127 doesn’t “lie inside this range”…yet it does “lie within the range”.

3 Likes

Another case: NormalisableRange can be constructed from a Range. The Range is end-exclusive, whereas the NormalisableRange is end-inclusive. So the range of valid values changes through this conversion, without being documented.

3 Likes