BOOST_IS_CONSTANT_EVALUATED_INT(integer-expression)(integer-expression)
A couple of questions regarding the above new boost config macro. a) I note that this "breaks the pattern" of BOOST_NO_... which I was expecting. I've got no complaint about this, just like being consistent when possible. b) My intended usage is something like: template<typename T> constexpr interval<T> get_interval(const & T t){ return BOOST_IS_CONSTANT_EVALUATED_INT(t) ? interval<T>(t, t) : interval<T>( std::numeric_limits<T>::min(), std::numeric_limits<T>::max() ); } is this the intended way to use this? If not, what would be the correct usage. c) In my Jamfiles I generally have a few tests which run conditionally on the config macro values like this: test-bsl-run_files test_array : A : : requires cxx11_hdr_array ; #BOOST_NO_CXX11_HDR_ARRAY What should I use with the above macro? Particularly confusing is the usage of _NO_ in the macro and the exclusion of _no_ in the requires variable. Should it be something like this. test test_is_constant_evaluted : A : : requires cxx20_no_is_constant_evaluated_int ; #BOOST_IS_CONSTANT_EVALUATED_INT d) Looks like part of this already checked into config.hpp on the develop branch. I would like to add something to my current develop to permit me to start using this now until you get the final code checked in. Any suggestions about the easiest way to do this? I'm ok with inserting some code temporarily but would prefer not to have to sprinkle it all over the place. Robert Ramey
On 22/08/2021 19:14, Robert Ramey via Boost wrote:
A couple of questions regarding the above new boost config macro.
a) I note that this "breaks the pattern" of BOOST_NO_... which I was expecting. I've got no complaint about this, just like being consistent when possible.
It's a "helper macro", we have BOOST_NO_CXX20_IS_CONSTANT_EVALUATED, but this is a strict C++20 only check. For BOOST_IS_CONSTANT_EVALUATED/BOOST_IS_CONSTANT_EVALUATED_INT we have BOOST_IS_CONSTANT_EVALUATED_VERSION with values 0,1,2 depending on whether we have no support, partial support, or full support.
b) My intended usage is something like:
template<typename T> constexpr interval<T> get_interval(const & T t){ return BOOST_IS_CONSTANT_EVALUATED_INT(t) ? interval<T>(t, t) : interval<T>( std::numeric_limits<T>::min(), std::numeric_limits<T>::max() ); }
is this the intended way to use this? If not, what would be the correct usage.
Yes.... but the function should really return the same value in both constexpr and runtime usage. This is especially true for BOOST_IS_CONSTANT_EVALUATED_INT which may evaluate to true simply because optimizations are on and the compiler has decided that the argument is known at compile time. This has caused some discussion on the PR to the effect that possibly BOOST_IS_CONSTANT_EVALUATED_INT should not be provided.
c) In my Jamfiles I generally have a few tests which run conditionally on the config macro values like this:
test-bsl-run_files test_array : A : : requires cxx11_hdr_array ; #BOOST_NO_CXX11_HDR_ARRAY
What should I use with the above macro? Particularly confusing is the usage of _NO_ in the macro and the exclusion of _no_ in the requires variable. Should it be something like this.
test test_is_constant_evaluted : A : : requires cxx20_no_is_constant_evaluated_int ; #BOOST_IS_CONSTANT_EVALUATED_INT
Good question, the feature would definitely work with [ requires cxx20_is_constant_evaluated ], note the spelling, but that would restrict tests to true C++20 ones.
d) Looks like part of this already checked into config.hpp on the develop branch. I would like to add something to my current develop to permit me to start using this now until you get the final code checked in. Any suggestions about the easiest way to do this? I'm ok with inserting some code temporarily but would prefer not to have to sprinkle it all over the place.
It's a PR awaiting comment at present: https://github.com/boostorg/config/pull/394, nothing in develop yet. However, should you wish to *experiment* in the mean time, then this is available in develop of Boost.Math: https://github.com/boostorg/math/blob/develop/include/boost/math/tools/is_co.... I note that this has BOOST_MATH_NO_CONSTEXPR_DETECTION which might be the right kind of macro to transplant to Boost.Config rather than BOOST_IS_CONSTANT_EVALUATED_VERSION. So.... a design question for everyone: is porting constexpr-detection to older gcc versions that have __builtin_constant_p worth the hassle of a slightly changed interface and possibly also semantics? Best, John. -- This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus
On 8/22/21 11:59 AM, John Maddock via Boost wrote:
b) My intended usage is something like:
template<typename T> constexpr interval<T> get_interval(const T & t){ return BOOST_IS_CONSTANT_EVALUATED_INT(t) ? interval<T>(t, t) : interval<T>( std::numeric_limits<T>::min(), std::numeric_limits<T>::max() ); }
is this the intended way to use this? If not, what would be the
correct usage. Yes.... but the function should really return the same value in both constexpr and runtime usage. LOL - for me, the whole point of this is to use a different value depending on whether or not the value is known at compile time. Maybe someone wants to opine on this. This is especially true for BOOST_IS_CONSTANT_EVALUATED_INT which may evaluate to true simply because optimizations are on and the compiler has decided that the argument is known at compile time. This has caused some discussion on the PR to the effect that possibly BOOST_IS_CONSTANT_EVALUATED_INT should not be provided.
I note that this has BOOST_MATH_NO_CONSTEXPR_DETECTION which might be the right kind of macro to transplant to Boost.Config rather than BOOST_IS_CONSTANT_EVALUATED_VERSION.
With the current design - not strictly dependent upon is_constant_evaluated() - that would seem less confusing to me.
So.... a design question for everyone: is porting constexpr-detection to older gcc versions that have __builtin_constant_p worth the hassle of a slightly changed interface and possibly also semantics?
I don't have a strong opinion. Somehow I'm thinking that at most one other person will have an opinion on this. Robert Ramey
On Sun, Aug 22, 2021 at 10:29 PM Robert Ramey via Boost < boost@lists.boost.org> wrote:
Yes.... but the function should really return the same value in both constexpr and runtime usage.
LOL - for me, the whole point of this is to use a different value depending on whether or not the value is known at compile time. Maybe someone wants to opine on this.
I think it would help discussion if you or John could provide a complete use case when different result is desired for a function depending on if it is running at compile time or not? I never had the need for different result based on if function is running at compile time, I have read the paper that proposed std::is_constant_evaluated ( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0595r2.html ), and I did not find that example, except maybe their mention of constexpr std::string, but I presume that is mostly hacks to go around constexpr limitations, I think they still want to to implement same logic for std::string.
On 23/08/2021 00:36, Ivan Matek via Boost wrote:
On Sun, Aug 22, 2021 at 10:29 PM Robert Ramey via Boost < boost@lists.boost.org> wrote:
Yes.... but the function should really return the same value in both constexpr and runtime usage.
LOL - for me, the whole point of this is to use a different value depending on whether or not the value is known at compile time. Maybe someone wants to opine on this.
I think it would help discussion if you or John could provide a complete use case when different result is desired for a function depending on if it is running at compile time or not?
I could be wrong, but I don't believe that that would be a valid use case, like you, the intended usage is to optimize runtime and compile time usage, ie: constexpr int f(int i) { if(std::is_constant_evaluated()) { // constexpr friendly code here } else { // Maybe use a function that's super fast but not constexpr safe } } John. -- This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus
On 8/23/21 10:20 AM, John Maddock via Boost wrote:
I could be wrong, but I don't believe that that would be a valid use case, like you, the intended usage is to optimize runtime and compile time usage, ie:
constexpr int f(int i)
{
if(std::is_constant_evaluated())
{
// constexpr friendly code here
}
else
{
// Maybe use a function that's super fast but not constexpr safe
}
}
John.
I guess if it doesn't work, it's not valid. Certainly from what little user documentation there is - cpp reference, there's no information on this topic. My user case looks like: template<typename T> struct interval { T m_min; T m_ max; interval(const T & min, const T & max) : m_min(min), m_max(max) {} } template<typename T> constexpr interval<T> get_interval(const T & t) { return std::is_constant_evaluated() ? interval<T>(t, t); // we know the value of t at compile time so the range is effectively [t, t] : interval<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::min()); // otherwise we won't know the value until runtime so it could be in the range [...] ; } So I'm returning different values depending on whether we're compiling or execution. In fact, if we know the value of t at compile time, the runtime code will never be executed. So there would never be an ambiguity, but of course the compiler can't see that so maybe that's why the compiler is unhappy with this. constexpr is a very useful facility. But like everything else in C++, adding such a thing to the standard without experience, can create a lot of problems. Robert Ramey
On Tue, Aug 24, 2021 at 6:01 PM Robert Ramey via Boost < boost@lists.boost.org> wrote:
I guess if it doesn't work, it's not valid. Certainly from what little user documentation there is - cpp reference, there's no information on this topic. My user case looks like:
template<typename T> struct interval { T m_min; T m_ max; interval(const T & min, const T & max) : m_min(min), m_max(max) {} }
template<typename T> constexpr interval<T> get_interval(const T & t) { return std::is_constant_evaluated() ? interval<T>(t, t); // we know the value of t at compile time so the range is effectively [t, t] : interval<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::min()); // otherwise we won't know the value until runtime so it could be in the range [...] ; }
So I'm returning different values depending on whether we're compiling or execution. In fact, if we know the value of t at compile time, the runtime code will never be executed. So there would never be an ambiguity, but of course the compiler can't see that so maybe that's why the compiler is unhappy with this.
Still not 100% clear what bigger goal is, for me example code compiles https://www.godbolt.org/z/sbKGh53ec when I make constructor constexpr. I presume you want to use the returned interval to construct some types(for example something like safe_signed_range ) and that is where the problems may occur. As far as I know this is impossible in C++, to have constexpr arguments, unless you use them as template arguments, e.g. func<3>(), but if you have func(3) there is no way to get that 3 during compile time. So even if this new boost macro is added that will not help you since even if compiler knows that 3 is 3 it will not let you to use it at compile time due to C++ language rules https://www.godbolt.org/z/hWPTGvPss. So I am not an expert, but I think it is indeed not possible to do what you want.
On 8/24/21 11:33 AM, Ivan Matek via Boost wrote:
On Tue, Aug 24, 2021 at 6:01 PM Robert Ramey via Boost < boost@lists.boost.org> wrote:
I guess if it doesn't work, it's not valid. Certainly from what little user documentation there is - cpp reference, there's no information on this topic. My user case looks like:
template<typename T> struct interval { T m_min; T m_ max; interval(const T & min, const T & max) : m_min(min), m_max(max) {} }
template<typename T> constexpr interval<T> get_interval(const T & t) { return std::is_constant_evaluated() ? interval<T>(t, t); // we know the value of t at compile time so the range is effectively [t, t] : interval<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::min()); // otherwise we won't know the value until runtime so it could be in the range [...] ; }
So I'm returning different values depending on whether we're compiling or execution. In fact, if we know the value of t at compile time, the runtime code will never be executed. So there would never be an ambiguity, but of course the compiler can't see that so maybe that's why the compiler is unhappy with this.
Still not 100% clear what bigger goal is, for me example code compiles https://www.godbolt.org/z/sbKGh53ec when I make constructor constexpr.
Hmmm - it wasn't clear to me that that each function has to be constexpr if the class is.
I presume you want to use the returned interval to construct some types(for example something like safe_signed_range ) and that is where the problems may occur. Correct. Not that the type being returned is always the same. It's just the values of the member variables which change.
As far as I know this is impossible in C++, to have constexpr arguments, unless you use them as template arguments, e.g. func<3>(), but if you have func(3) there is no way to get that 3 during compile time.
Given the problems the compiler has with this code, looks like you're correct.
So even if this new boost macro is added that will not help you since even if compiler knows that 3 is 3 it will not let you to use it at compile time due to C++ language rules https://www.godbolt.org/z/hWPTGvPss.
Here's a much simpler example. In this case, it doesn't look like it doesn't work on clang ever. https://www.godbolt.org/z/c4s61fTvd
So I am not an expert, but I think it is indeed not possible to do what you want.
It's not clear to my why not. I'll dig into this some more. I think I can achieve what I need without using a function. Thanks to everyone for their interest and help. Robert Ramey
On 8/24/21 2:28 PM, Robert Ramey via Boost wrote:
Here's a much simpler example. In this case, it doesn't look like it doesn't work on clang ever.
https://www.godbolt.org/z/c4s61fTvd
So I am not an expert, but I think it is indeed not possible to do what you want.
It's not clear to my why not. I'll dig into this some more. I think I can achieve what I need without using a function.
Thanks to everyone for their interest and help.
Robert Ramey
PS. Enhanced example which suggests (to me at least) that what I want to do is possible. at least in gcc 11 https://www.godbolt.org/z/Mn4ThdsG7
participants (3)
-
Ivan Matek
-
John Maddock
-
Robert Ramey