Library support for relaxed constexpr

158 views
Skip to first unread message

rhalb...@gmail.com

unread,
Jan 12, 2015, 3:56:48 PM1/12/15
to std-pr...@isocpp.org
Now that Item 15 of Effective Modern C++ ("use constexpr whenever possible", see e.g. this recent presentation) has brought C++14 relaxed constexpr into the mainstream, it is perhaps time to introduce this feature into the Standard Library. 

I write "introduce" rather than "update" because AFAICS, there is currently *zero* usage of relaxed constexpr (e.g. non-const but constexpr member functions) in the current Draft Standard. 

As a proof of concept, I have written a fully constexpr version of std::array (available from BitBucket). In order to do so, I also had to transitively add the constexpr keyword to a bunch of other facilities apart from the header <array> (as Scott Meyers writes "constexpr begets constexpr")
  • from the header <algorithm>: fill_n, swap_ranges, equal and lexicographical_compare
  • from the header <iterator>: reverse_iterator
  • from the header <utility>: swap
Here I benefitted from the fact that the new "diamond operators"  such as std::less<> already have a constexpr templated application operator. I chose std::array for this exercise because of its relative simplicity and because it nicely complements the new compile-time integer sequence facilities. 

The only obstacle I encountered was a single dispatch of fill_n that (at least in libc++) delegates to std::memset for which I had no access to the underlying source code. With that caveat, it was a completely straightforward excercise of transitively adding the keyword constexpr. No other code mofications were necessary.

I don't claim to have made a full survey of the entire Standard Library, but a I believe a similar exercise is possible for std::complex, std::bitset (apart from the std::string constructors) and almost all the algorithms from <algorithm> and <numeric> (apart from a few allocating algorithms such as stable_sort, stable_partition and inplace_merge). 

Of course, constexpr-ifying the standard algorithms would greatly benefit from extra language support as well (constexpr lambdas, some form of heap allocation to allow compile-time std::string). Nevertheless, given the C++14 features, the Standard Library currently feels vastly underpowered to what the core language offers in terms of constexpr

My question is: how does the community and the committee feel about moving a greater part (the majority?) of the Standard Library towards C++14 relaxed constexpr? I am willing to write a proposal along these lines given enough support (or perhaps there are already efforts underway?)

Matthew Woehlke

unread,
Jan 12, 2015, 4:39:46 PM1/12/15
to std-pr...@isocpp.org
On 2015-01-12 15:56, rhalb...@gmail.com wrote:
> [...] it is perhaps time to introduce this feature
> into the Standard Library.
>
> I write "introduce" rather than "update" because AFAICS, there is currently
> *zero* usage of relaxed constexpr (e.g. non-const but constexpr member
> functions) in the current Draft Standard.

Some implementations have apparently started adding constexpr anyway.

If a concerted effort is made to 'fix' this, please don't forget places
like <limits>, <cmath>, etc. (I had to change some code that was
originally written against libstdc++ because libc++ does not have
constexpr of e.g. ldexp. Having constexpr of basic math functions and
number manipulation functions seems like a no-brainer...)

--
Matthew

Ville Voutilainen

unread,
Jan 12, 2015, 4:50:26 PM1/12/15
to std-pr...@isocpp.org
On 12 January 2015 at 22:56, <rhalb...@gmail.com> wrote:
> Of course, constexpr-ifying the standard algorithms would greatly benefit
> from extra language support as well (constexpr lambdas, some form of heap
> allocation to allow compile-time std::string). Nevertheless, given the C++14
> features, the Standard Library currently feels vastly underpowered to what
> the core language offers in terms of constexpr.


For what it's worth, I plan to propose allowing constexpr lambdas. I
have no plans
to touch dynamic allocation at this time, though. If you want a
string-like thing
that works in constant expressions, perhaps std::experimental::string_view
works.

sasho648

unread,
Jan 12, 2015, 5:02:08 PM1/12/15
to std-pr...@isocpp.org, rhalb...@gmail.com
This is a good idea but I think that we first need to fix those 'constexpr' before supporting them in libraries. Here is where I have written it's fix and where is also link for it's problems.

Thiago Macieira

unread,
Jan 12, 2015, 5:54:08 PM1/12/15
to std-pr...@isocpp.org
Note that the difference is whether the intrinsic functions are considered
constexpr or not. For GCC, __builtin_ldexp is constexpr but isn't for Clang:

$ cat test.cpp
constexpr int f() { return __builtin_ldexp(1, 2); }

$ gcc -c test.cpp

$ clang -c test.cpp
test.cpp:1:15: error: constexpr function never produces a constant expression
[-Winvalid-constexpr]
constexpr int f() { return __builtin_ldexp(1, 2); }
^
test.cpp:1:28: note: subexpression not valid in a constant expression
constexpr int f() { return __builtin_ldexp(1, 2); }
^

libstdc++ is written specifically for GCC, so it makes use of that fact.
template<typename _Tp>
inline _GLIBCXX_CONSTEXPR
typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
double>::__type
ldexp(_Tp __x, int __exp)
{ return __builtin_ldexp(__x, __exp); }

Not so with libc++, which supports multiple compilers and thus doesn't use the
intrinsic but instead calls the libm function:

template <class _A1>
inline _LIBCPP_INLINE_VISIBILITY
typename enable_if<is_integral<_A1>::value, double>::type
ldexp(_A1 __x, int __e) _NOEXCEPT {return ldexp((double)__x, __e);}

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

David Krauss

unread,
Jan 12, 2015, 8:18:18 PM1/12/15
to std-pr...@isocpp.org

On 2015–01–13, at 6:43 AM, Thiago Macieira <thi...@macieira.org> wrote:

Note that the difference is whether the intrinsic functions are considered 
constexpr or not. For GCC, __builtin_ldexp is constexpr but isn't for Clang:

For GCC, most of <cmath> is constexpr because it has supported compile-time evaluation of constant floating-point expressions since time immemorial. Only in C++11 (once constexpr extensions were forbidden) did this extension become nonconforming.

I almost finished a paper for Urbana, “Refining floating-point constexpr,” but ultimately I had enough other material, and the implementations kept evolving under my feet. I should finish it and find a presenter for Lenexa. Co-authors are welcome!

Thiago Macieira

unread,
Jan 12, 2015, 10:02:02 PM1/12/15
to std-pr...@isocpp.org
I believe you should leave those functions in cmath be optionally constexpr.
Compilers do not have to make them constexpr if they are not equipped for
evaluating them at compile time, like GCC was.

Richard Smith

unread,
Jan 12, 2015, 10:47:06 PM1/12/15
to std-pr...@isocpp.org
On Mon, Jan 12, 2015 at 7:01 PM, Thiago Macieira <thi...@macieira.org> wrote:
On Tuesday 13 January 2015 09:18:13 David Krauss wrote:
> > On 2015–01–13, at 6:43 AM, Thiago Macieira <thi...@macieira.org> wrote:
> >
> > Note that the difference is whether the intrinsic functions are considered
>
> > constexpr or not. For GCC, __builtin_ldexp is constexpr but isn't for
Clang:
> For GCC, most of <cmath> is constexpr because it has supported compile-time
> evaluation of constant floating-point expressions since time immemorial.
> Only in C++11 (once constexpr extensions were forbidden) did this extension
> become nonconforming.
>
> I almost finished a paper for Urbana, “Refining floating-point constexpr,”
> but ultimately I had enough other material, and the implementations kept
> evolving under my feet. I should finish it and find a presenter for Lenexa.
> Co-authors are welcome!

I believe you should leave those functions in cmath be optionally constexpr.
Compilers do not have to make them constexpr if they are not equipped for
evaluating them at compile time, like GCC was.

It doesn't seem unreasonable to require more of C++17 implementations. I think it's much better to make the functions unconditionally constexpr and require the implementations to do the right thing.

One consideration, though: C supports controlling the floating point environment in various ways, and C++ pays lipservice to this by providing the <cfenv> header. If we ever start also supporting "#pragma STDC FENV_ACCESS" and following the C rules here (which seems like a very bad idea, but we're already half-way there) then that will have "interesting" interactions with compile-time evaluation of floating-point expressions.

Myriachan

unread,
Jan 12, 2015, 10:54:32 PM1/12/15
to std-pr...@isocpp.org
On Monday, January 12, 2015 at 5:18:18 PM UTC-8, David Krauss wrote:
For GCC, most of <cmath> is constexpr because it has supported compile-time evaluation of constant floating-point expressions since time immemorial. Only in C++11 (once constexpr extensions were forbidden) did this extension become nonconforming.


Is it still the case that implementations aren't allowed to declare standard functions as constexpr that are not explicitly constexpr in the Standard?  What this thread is about is otherwise confusing me...sorry.

Melissa

David Krauss

unread,
Jan 13, 2015, 12:20:24 AM1/13/15
to std-pr...@isocpp.org

On 2015–01–13, at 11:54 AM, Myriachan <myri...@gmail.com> wrote:

Is it still the case that implementations aren't allowed to declare standard functions as constexpr that are not explicitly constexpr in the Standard?  What this thread is about is otherwise confusing me…sorry.

Yes, it’s still the case. My recollection is that the restriction was added by a DR which was resolved after C++11, but it might have been before.

Considering the potential for language fragmentation, the decision is unlikely to be reversed.


On 2015–01–13, at 11:47 AM, Richard Smith <ric...@metafoo.co.uk> wrote:

One consideration, though: C supports controlling the floating point environment in various ways, and C++ pays lipservice to this by providing the <cfenv> header. If we ever start also supporting "#pragma STDC FENV_ACCESS" and following the C rules here (which seems like a very bad idea, but we're already half-way there) then that will have "interesting" interactions with compile-time evaluation of floating-point expressions.

Nothing that #pragma STDC FP_CONTRACT doesn’t already do. Such interactions are acknowledged and dealt with simply. C99/C11 §6.5/8:

Otherwise, whether and how expressions are contracted is implementation-defined. *

This license is specifically intended to allow implementations to exploit fast machine instructions that combine multiple C operators. As contractions potentially undermine predictability, and can even decrease accuracy for containing expressions, their use needs to be well-defined and clearly documented.

The difference is that FP_CONTRACT can be turned off but constexpr can’t. If we set aside the C/C++ rivalry, would it be too unreasonable to let <cmath> be non-constexpr when FP_CONTRACT OFF is in effect in the scope enclosing the call? (Despite being a pragma, it is scoped.)

Vlad from Moscow

unread,
Jan 13, 2015, 4:20:34 AM1/13/15
to std-pr...@isocpp.org
I am sorry for my silly question. What is  Urbana?

David Krauss

unread,
Jan 13, 2015, 5:13:05 AM1/13/15
to std-pr...@isocpp.org

> On 2015–01–13, at 5:20 PM, Vlad from Moscow <vlad....@mail.ru> wrote:
>
> I am sorry for my silly question. What is Urbana?

The most recent ISO C++ meeting. It was the first meeting I attended, and I presented 7 papers, which was already a lot. (Also, it is a small city in Illinois, USA, which contains most of a large university.)

Lenexa is the next meeting. I will not attend it.

Vlad from Moscow

unread,
Jan 13, 2015, 5:18:12 AM1/13/15
to std-pr...@isocpp.org
Thanks. I had thought at first that iit is a journal on computing.:)

Richard Smith

unread,
Jan 13, 2015, 4:57:28 PM1/13/15
to std-pr...@isocpp.org
(I was talking about FENV_ACCESS, not FP_CONTRACT. Dealing with FP_CONTRACT is easy; just don't do contractions in constexpr evaluation.) On reflection, I think this goes further than <cmath>, since fesetround affects the results of (for instance) floating point addition, too. So there's no *new* problem here. =)

David Krauss

unread,
Jan 13, 2015, 5:01:51 PM1/13/15
to std-pr...@isocpp.org
On 2015–01–14, at 5:57 AM, Richard Smith <ric...@metafoo.co.uk> wrote:

(I was talking about FENV_ACCESS, not FP_CONTRACT. Dealing with FP_CONTRACT is easy; just don't do contractions in constexpr evaluation.)

Yes, and I’m talking about the intersection of FENV_ACCESS and FP_CONTRACT.

You mentioned interaction between constexpr and FENV_ACCESS. Expressions which are constexpr/contracted will not set overflow bits. FP_CONTRACT is the C99 analogue to constexpr, although it provides few guarantees.

On reflection, I think this goes further than <cmath>, since fesetround affects the results of (for instance) floating point addition, too. So there's no *new* problem here. =)

When things are done at compile time, all bets are off.
Reply all
Reply to author
Forward
0 new messages