std::between ?

151 views
Skip to first unread message

Addy

unread,
Sep 27, 2018, 11:53:21 AM9/27/18
to ISO C++ Standard - Future Proposals
I do this pattern often:

if (0 < x && x < 100)

And would love a simple `std::between()` that would take two arguments and an optional third (compare function) argument.


templated<typename T, typename Compare>
bool std::between(T lower, T upper, Compare comp)
{
    return comp(lower) && comp(upper);
}

Does anything like this exist? Should I propose it!?

Jake Arkinstall

unread,
Sep 27, 2018, 12:07:24 PM9/27/18
to std-pr...@isocpp.org
It has come up before. The issue is that a<b<c is evaluated as (a<b)<c, where (a<b) is a bool. We would thus need a special case to deal with this.

Personally, I don't imagine that people are actually using a<b<c to compare the comparison (a<b) against c. Its possible, though, and so adding a special case for comparisons will break something somewhere. I want it, it's lovely syntax, but backwards compatibility is lovely too, even in cases where legacy code is doing something bizarre.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/7b85d688-9013-407d-b7d3-10d28494e960%40isocpp.org.

d25f...@outlook.com

unread,
Sep 27, 2018, 12:19:15 PM9/27/18
to 'Giuseppe D'Angelo' via ISO C++ Standard - Future Proposals

Tony V E

unread,
Sep 27, 2018, 1:47:25 PM9/27/18
to Standard Proposals
We could argue about the order of params, and what std::between(x, -100, 0) means, or you could look at std::clamp().

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/7b85d688-9013-407d-b7d3-10d28494e960%40isocpp.org.


--
Be seeing you,
Tony

Tony V E

unread,
Sep 27, 2018, 1:47:57 PM9/27/18
to Standard Proposals


Darn, I meant std::between(x, 100, -100);

Adrian H

unread,
Sep 27, 2018, 6:07:42 PM9/27/18
to ISO C++ Standard - Future Proposals
I guess this could also be done with operator overloading.  Something like:

#include <iostream>
#include <type_traits>

template<typename T>
struct chain_impl {
  chain_impl(T&& obj, bool val) : obj(std::move(obj)), val(val) {}
  T&& obj;
  bool val;
  explicit operator bool() {
      return val;
  }
};

template<typename T>
struct chain_impl<T&> {
  chain_impl(T& obj, bool val) : obj(obj), val(val) {}
  T& obj;
  bool val;
  explicit operator bool() {
      return val;
  }
};

template <typename T>
decltype(auto) make_chain(T&&  obj, bool val = true) {
  return chain_impl<std::conditional_t<std::is_lvalue_reference<T>::value, T&, T&&>>(std::forward<T>(obj), val);
}

template <typename T0, typename T1>
auto operator< (chain_impl<T0>&& lhs, T1&& rhs) {
    if (lhs.val) {
        auto result = make_chain(rhs, lhs.obj < rhs);
        return make_chain(std::forward<T1>(rhs), lhs.val && result.val);
    }
    return make_chain(std::forward<T1>(rhs), false);
}

int main()
{
  std::cout << (make_chain(1) < 2 < 3 ? 1 : 0) << "\n";
  int a=1, b=2, c=3;
  std::cout << (make_chain(a) < c < b ? 1 : 0) << "\n";
}

But that will do extra trivial comparisons even if the test could be short circuited, which is unfortunate.

A



--
========================================
            Adrian Hawryluk
         BSc. Computer Science
----------------------------------------
           Specialising in:
        OOD Methodologies in UML
  OOP Methodologies in C, C++ and more
        RT Embedded Programming
            GUI Development
========================================

Daniel Gutson

unread,
Sep 28, 2018, 7:03:19 AM9/28/18
to std-proposals
I stepped into this too many times.
One of the issues are the four < vs <= combinations.

The way I solved is by creating a temporal object that allows chain comparison, and a cast-to-bool operator.

if (chain(3) < x < 4) 



--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/7b85d688-9013-407d-b7d3-10d28494e960%40isocpp.org.


--
Who’s got the sweetest disposition?
One guess, that’s who?
Who’d never, ever start an argument?
Who never shows a bit of temperament?
Who's never wrong but always right?
Who'd never dream of starting a fight?
Who get stuck with all the bad luck?

Jack Adrian Zappa

unread,
Sep 28, 2018, 10:26:13 AM9/28/18
to ISO C++ Standard - Future Proposals


On Friday, September 28, 2018 at 7:03:19 AM UTC-4, Daniel Gutson wrote:
I stepped into this too many times.
One of the issues are the four < vs <= combinations.

The way I solved is by creating a temporal object that allows chain comparison, and a cast-to-bool operator.

if (chain(3) < x < 4) 

So, something similar to the solution I provided?  As I said, that solution would probably result in possible unnecessary comparisons (no logical short circuiting) and additional storage for bool propagation (though I've not looked at what the optimizer would do).  Would prolly be better to implement in the language to avoid those issues, even if it is only syntactic sugar, as it can make the code more readable.

Daniel Gutson

unread,
Sep 28, 2018, 10:31:57 AM9/28/18
to std-proposals


El vie., 28 sept. 2018 11:26, Jack Adrian Zappa <adrian....@gmail.com> escribió:


On Friday, September 28, 2018 at 7:03:19 AM UTC-4, Daniel Gutson wrote:
I stepped into this too many times.
One of the issues are the four < vs <= combinations.

The way I solved is by creating a temporal object that allows chain comparison, and a cast-to-bool operator.

if (chain(3) < x < 4) 

So, something similar to the solution I provided?  As I said, that solution would probably result in possible unnecessary comparisons (no logical short circuiting)

No, the chain object contains a boolean and the last right hand operand as its only state. It should do nothing after the boolean becomes false.


Adrian H

unread,
Sep 28, 2018, 10:40:29 AM9/28/18
to ISO C++ Standard - Future Proposals


On Friday, September 28, 2018 at 10:31:57 AM UTC-4, Daniel Gutson wrote:


El vie., 28 sept. 2018 11:26, Jack Adrian Zappa <adrian....@gmail.com> escribió:


On Friday, September 28, 2018 at 7:03:19 AM UTC-4, Daniel Gutson wrote:
I stepped into this too many times.
One of the issues are the four < vs <= combinations.

The way I solved is by creating a temporal object that allows chain comparison, and a cast-to-bool operator.

if (chain(3) < x < 4) 

So, something similar to the solution I provided?  As I said, that solution would probably result in possible unnecessary comparisons (no logical short circuiting)

No, the chain object contains a boolean and the last right hand operand as its only state. It should do nothing after the boolean becomes false.

Could you provide your solution?  I'd be interested in finding out how you dealt with short circuiting.  Also, I take it that your solution wouldn't allow comparisons of different types such as a double and a float, or would it convert all objects to the type of first one chained? 

Daniel Gutson

unread,
Sep 28, 2018, 10:53:35 AM9/28/18
to std-proposals
El vie., 28 de sep. de 2018 a la(s) 11:40, Adrian H (adrian....@gmail.com) escribió:


On Friday, September 28, 2018 at 10:31:57 AM UTC-4, Daniel Gutson wrote:


El vie., 28 sept. 2018 11:26, Jack Adrian Zappa <adrian....@gmail.com> escribió:


On Friday, September 28, 2018 at 7:03:19 AM UTC-4, Daniel Gutson wrote:
I stepped into this too many times.
One of the issues are the four < vs <= combinations.

The way I solved is by creating a temporal object that allows chain comparison, and a cast-to-bool operator.

if (chain(3) < x < 4) 

So, something similar to the solution I provided?  As I said, that solution would probably result in possible unnecessary comparisons (no logical short circuiting)

No, the chain object contains a boolean and the last right hand operand as its only state. It should do nothing after the boolean becomes false.

Could you provide your solution?  I'd be interested in finding out how you dealt with short circuiting. 

Sure, I'm in a conference but I will do later.
 
Also, I take it that your solution wouldn't allow comparisons of different types such as a double and a float, or would it convert all objects to the type of first one chained? 

Both the chain class and the comparison operators are templates. The result of the operator is a new (temporal) chain object with the template argument deduced. Let me provide the code later. 
 

Adrian H

unread,
Sep 28, 2018, 11:18:05 AM9/28/18
to ISO C++ Standard - Future Proposals


On Friday, September 28, 2018 at 10:53:35 AM UTC-4, Daniel Gutson wrote:


El vie., 28 de sep. de 2018 a la(s) 11:40, Adrian H (adrian....@gmail.com) escribió:


On Friday, September 28, 2018 at 10:31:57 AM UTC-4, Daniel Gutson wrote:


El vie., 28 sept. 2018 11:26, Jack Adrian Zappa <adrian....@gmail.com> escribió:


On Friday, September 28, 2018 at 7:03:19 AM UTC-4, Daniel Gutson wrote:
I stepped into this too many times.
One of the issues are the four < vs <= combinations.

The way I solved is by creating a temporal object that allows chain comparison, and a cast-to-bool operator.

if (chain(3) < x < 4) 

So, something similar to the solution I provided?  As I said, that solution would probably result in possible unnecessary comparisons (no logical short circuiting)

No, the chain object contains a boolean and the last right hand operand as its only state. It should do nothing after the boolean becomes false.

Could you provide your solution?  I'd be interested in finding out how you dealt with short circuiting. 

Sure, I'm in a conference but I will do later.
 
Also, I take it that your solution wouldn't allow comparisons of different types such as a double and a float, or would it convert all objects to the type of first one chained? 

Both the chain class and the comparison operators are templates. The result of the operator is a new (temporal) chain object with the template argument deduced. Let me provide the code later. 

Cool.  Still sounds very similar to what I posted:
Looking forward to seeing the difference.

Ivan G.

unread,
Sep 28, 2018, 11:20:53 AM9/28/18
to ISO C++ Standard - Future Proposals
I'd like to see some kind of a range operator, for example, 100..200 returns special integral range type.
Then use some another operator to check whether a number is within this range, for example:
    if (x & -100..100)
   {
   }







четверг, 27 сентября 2018 г., 19:07:24 UTC+3 пользователь Jake Arkinstall написал:

Daniel Gutson

unread,
Sep 28, 2018, 11:25:30 AM9/28/18
to std-proposals
El vie., 28 de sep. de 2018 a la(s) 12:18, Adrian H (adrian....@gmail.com) escribió:


On Friday, September 28, 2018 at 10:53:35 AM UTC-4, Daniel Gutson wrote:


El vie., 28 de sep. de 2018 a la(s) 11:40, Adrian H (adrian....@gmail.com) escribió:


On Friday, September 28, 2018 at 10:31:57 AM UTC-4, Daniel Gutson wrote:


El vie., 28 sept. 2018 11:26, Jack Adrian Zappa <adrian....@gmail.com> escribió:


On Friday, September 28, 2018 at 7:03:19 AM UTC-4, Daniel Gutson wrote:
I stepped into this too many times.
One of the issues are the four < vs <= combinations.

The way I solved is by creating a temporal object that allows chain comparison, and a cast-to-bool operator.

if (chain(3) < x < 4) 

So, something similar to the solution I provided?  As I said, that solution would probably result in possible unnecessary comparisons (no logical short circuiting)

No, the chain object contains a boolean and the last right hand operand as its only state. It should do nothing after the boolean becomes false.

Could you provide your solution?  I'd be interested in finding out how you dealt with short circuiting. 

Sure, I'm in a conference but I will do later.
 
Also, I take it that your solution wouldn't allow comparisons of different types such as a double and a float, or would it convert all objects to the type of first one chained? 

Both the chain class and the comparison operators are templates. The result of the operator is a new (temporal) chain object with the template argument deduced. Let me provide the code later. 

Cool.  Still sounds very similar to what I posted:

Actually it is now that I read it more carefully:
 

#include <iostream>
#include <type_traits>

template<typename T>
struct chain_impl {
  chain_impl(T&& obj, bool val) : obj(std::move(obj)), val(val) {}
  T&& obj;
  bool val;
  explicit operator bool() {
      return val;
  }
};

template<typename T>
struct chain_impl<T&> {
  chain_impl(T& obj, bool val) : obj(obj), val(val) {}
  T& obj;
  bool val;
  explicit operator bool() {
      return val;
  }
};

template <typename T>
decltype(auto) make_chain(T&&  obj, bool val = true) {
  return chain_impl<std::conditional_t<std::is_lvalue_reference<T>::value, T&, T&&>>(std::forward<T>(obj), val);
}

template <typename T0, typename T1>
auto operator< (chain_impl<T0>&& lhs, T1&& rhs) {
    if (lhs.val) {

Why you say that comparisons are performed? You are breaking the chain here with this evaluation. So yes, operator will keep be called, but it's just this if.
 

Adrian H

unread,
Sep 28, 2018, 6:47:14 PM9/28/18
to ISO C++ Standard - Future Proposals
So maybe not a comparison, but still a flag check for each comparison, regardless if it reached a potential short circuit point. Also, an extra Boolean would be needed to be allocated for each term.

Unless you're saying that the optimizer will be able to detect this and short circuit the operation and not allocate the Booleans? That would be nice, but I don't have that much faith in the optimizer. So unless it can optimize that code and data away then this feature would have to be on the compiler level so as not to add extra crap to the final binary.

>  
>
>         auto result = make_chain(rhs, lhs.obj < rhs);
>         return make_chain(std::forward<T1>(rhs), lhs.val && result.val);
>     }
>     return make_chain(std::forward<T1>(rhs), false);
> }
>
>
> int main()
> {
>   std::cout << (make_chain(1) < 2 < 3 ? 1 : 0) << "\n";
>   int a=1, b=2, c=3;
>
>   std::cout << (make_chain(a) < c < b ? 1 : 0) << "\n";
> }
>
>
> Looking forward to seeing the difference.

Sorry. I don't understand your comment here.

Adrian H

unread,
Oct 2, 2018, 11:31:32 AM10/2/18
to ISO C++ Standard - Future Proposals
Wow.  I take it back.  The optimizer seems more than capable of removing all of the cruft. gcc 8.2 output:


shows that there doesn't seem to be any boolean mid term storage and short circuiting does appear to be happening.  I'm really impressed.

Evan MS VC++ 2017 doesn't do too bad of a job.  Though it does leave a bunch of unnecessary code in the binary, it doesn't actually execute it.  https://godbolt.org/z/N58hN4

Truly impressive.  Seems that there is really no need for this feature after all.


A

Adrian H

unread,
Oct 2, 2018, 11:34:15 AM10/2/18
to ISO C++ Standard - Future Proposals
On Tuesday, October 2, 2018 at 11:31:32 AM UTC-4, Adrian H wrote:
Wow.  I take it back.  The optimizer seems more than capable of removing all of the cruft. gcc 8.2 output:


shows that there doesn't seem to be any boolean mid term storage and short circuiting does appear to be happening.  I'm really impressed.

Evan MS VC++ 2017 doesn't do too bad of a job.  Though it does leave a bunch of unnecessary code in the binary, it doesn't actually execute it.  https://godbolt.org/z/N58hN4

Truly impressive.  Seems that there is really no need for this feature after all.
 
Maybe something like this this should be added to the std lib then.

Reply all
Reply to author
Forward
0 new messages