[config][mpl][type_traits][integer] BOOST_STATIC_CONSTANT should use constexpr
Hi,
BOOST_STATIC_CONSTANT could be improved to make use of constexpr. This
is needed for instance to make integral_constant compliant with C++11.
The patch consists in changing const by BOOST_CONSTEXPR_OR_CONST in
boost/config/suffix.hpp
// BOOST_STATIC_CONSTANT workaround
--------------------------------------- //
// On compilers which don't allow in-class initialization of static integral
// constant members, we must use enums as a workaround if we want the
constants
// to be available at compile-time. This macro gives us a convenient way to
// declare such constants.
# ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION
# define BOOST_STATIC_CONSTANT(type, assignment) enum { assignment }
# else
# define BOOST_STATIC_CONSTANT(type, assignment) static
*BOOST_CONSTEXPR_OR_CONST* type assignment
# endif
and in each out of line static constant definition as in
template
2013/4/20 Vicente J. Botet Escriba
Hi, <...> The main problem is that this is a *breaking change*: the list of concerned libraries is quite big (mpl, type_traits/integer_traits, ...) and users of BOOST_STATIC_CONSTANT would need to make this modification also.
Can you please show, what code may it break? -- Best regards, Antony Polukhin
Le 20/04/13 10:39, Antony Polukhin a écrit :
2013/4/20 Vicente J. Botet Escriba
: Hi, <...> The main problem is that this is a *breaking change*: the list of concerned libraries is quite big (mpl, type_traits/integer_traits, ...) and users of BOOST_STATIC_CONSTANT would need to make this modification also. Can you please show, what code may it break?
My sentence "Library providing a template class containing a field with such a change that can be specialized by the user would need to either announce a breaking change or just don't use the new macro. " was wrong. Anyway, the breaking change for the user is the following: If a user had a template such as template <typename T> struct trait { BOOST_STATIC_CONSTANT(bool, value=false); }; template <typename T> const bool trait::value; it would need now to change it to template <typename T> const BOOST_CONSTEXPR_OR_CONST trait::value; The good thing is that the breaking change would be signaled at compile time. Would it be preferable to add BOOST_STATIC_CONSTANT_11 and change every use of BOOST_STATIC_CONSTANT by BOOST_STATIC_CONSTANT_11 and deprecate BOOST_STATIC_CONSTANT? Let me know if my analysis is wrong. Best, Vicente
2013/4/20 Vicente J. Botet Escriba
Would it be preferable to add BOOST_STATIC_CONSTANT_11 and change every use of BOOST_STATIC_CONSTANT by BOOST_STATIC_CONSTANT_11 and deprecate BOOST_STATIC_CONSTANT?
I would rather change the definitioon of BOOST_STATIC_CONSTANT to # ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION # define BOOST_STATIC_CONSTANT(type, assignment) enum { assignment } # else # define BOOST_STATIC_CONSTANT(type, assignment) static *BOOST_CONSTEXPR_OR_CONST* type assignment # endif Code from example: template <typename T> const bool trait::value; newer was correct, because compilers with BOOST_NO_INCLASS_MEMBER_INITIALIZATION define it to enum. So proposed change shall not break correct code. -- Best regards, Antony Polukhin
Le 20/04/13 11:15, Antony Polukhin a écrit :
2013/4/20 Vicente J. Botet Escriba
: Would it be preferable to add BOOST_STATIC_CONSTANT_11 and change every use of BOOST_STATIC_CONSTANT by BOOST_STATIC_CONSTANT_11 and deprecate BOOST_STATIC_CONSTANT? I would rather change the definitioon of BOOST_STATIC_CONSTANT to
# ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION # define BOOST_STATIC_CONSTANT(type, assignment) enum { assignment } # else # define BOOST_STATIC_CONSTANT(type, assignment) static *BOOST_CONSTEXPR_OR_CONST* type assignment # endif
Code from example:
template <typename T> const bool trait::value;
newer was correct, because compilers with BOOST_NO_INCLASS_MEMBER_INITIALIZATION define it to enum. So proposed change shall not break correct code.
Humm,
I had made the change in BOOST_STATIC_CONSTANT and I'm getting error for
example in
namespace boost {
namespace type_traits {
template
2013/4/20 Vicente J. Botet Escriba
#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION template
bool const ice_eq ::value; // ********* template bool const ice_ne ::value; // ********* #endif for which I have needed to change const by BOOST_CONSTEXPR_OR_CONST. <...> What am I missing?
Looks like it shall be #ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION not #ifndef But how did it work before? Or nobody tested it on ancient compilers? -- Best regards, Antony Polukhin
#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION template
bool const ice_eq ::value; // ********* template bool const ice_ne ::value; // ********* #endif for which I have needed to change const by BOOST_CONSTEXPR_OR_CONST. <...> What am I missing?
Looks like it shall be #ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION not #ifndef
But how did it work before? Or nobody tested it on ancient compilers?
No that's correct as is: * if BOOST_NO_INCLASS_MEMBER_INITIALIZATION is defined, then the constant is really an enum and there should be no out-of-line definitions. * if BOOST_NO_INCLASS_MEMBER_INITIALIZATION is *not* defined, then the constant *should* have the out-of-line definition in case it's address is taken/used. As for the original question, this is such a pervasive breaking change, I think a new macro is inevitable I'm afraid :-( John.
2013/4/20 John Maddock
No that's correct as is:
* if BOOST_NO_INCLASS_MEMBER_INITIALIZATION is defined, then the constant is really an enum and there should be no out-of-line definitions. * if BOOST_NO_INCLASS_MEMBER_INITIALIZATION is *not* defined, then the constant *should* have the out-of-line definition in case it's address is taken/used.
Oh, now I see, thanks.
As for the original question, this is such a pervasive breaking change, I think a new macro is inevitable I'm afraid :-(
New macro will do exactly what the old macro did, but using C++11 feature. Code in all Boost libraries will be changed to use new macro. So the old macro will be required only for users code that use macro and attempts to take address of constant... To me, it looks less obscuring to have one macro and a note for users to use BOOST_CONSTEXPR_OR_CONST for out-of-line definition; than two macro (which one shall be used by default?) that do the same thing but very slightly differ in details. -- Best regards, Antony Polukhin
As for the original question, this is such a pervasive breaking change, I think a new macro is inevitable I'm afraid :-(
New macro will do exactly what the old macro did, but using C++11 feature. Code in all Boost libraries will be changed to use new macro. So the old macro will be required only for users code that use macro and attempts to take address of constant...
To me, it looks less obscuring to have one macro and a note for users to use BOOST_CONSTEXPR_OR_CONST for out-of-line definition; than two macro (which one shall be used by default?) that do the same thing but very slightly differ in details.
Here's a radical idea: are there any compilers still around that we care about which require the enum workaround? Certainly not VC6 ;-) So we could recomend libraries use: template <class T> struct foo { BOOST_STATIC_CONSTEXPR bool value = some_value; }; template <class T> BOOST_CONSTEXPR_OR_CONST bool foo<T>::value; Thoughts? John.
Le 21/04/13 13:08, John Maddock a écrit :
As for the original question, this is such a pervasive breaking change, I think a new macro is inevitable I'm afraid :-(
New macro will do exactly what the old macro did, but using C++11 feature. Code in all Boost libraries will be changed to use new macro. So the old macro will be required only for users code that use macro and attempts to take address of constant...
To me, it looks less obscuring to have one macro and a note for users to use BOOST_CONSTEXPR_OR_CONST for out-of-line definition; than two macro (which one shall be used by default?) that do the same thing but very slightly differ in details.
Here's a radical idea: are there any compilers still around that we care about which require the enum workaround? Certainly not VC6 ;-)
So we could recomend libraries use:
template <class T> struct foo { BOOST_STATIC_CONSTEXPR bool value = some_value; or static BOOST_CONSTEXPR_OR_CONST bool value = some_value;
};
template <class T> BOOST_CONSTEXPR_OR_CONST bool foo<T>::value;
Thoughts?
I'm not against this radical alternative, and I even like it. I don't know which compilers need the enum workaround and if Boost should support them. What I want is just that integral_constant use them so that all the traits in Boost conforms to the C++11 standard. I have applied some changes to [config][mpl][type_traits][integer] and the number of changes to make integral_constant work is not so big. I will comeback later with more information. Best, Vicente
I'm not against this radical alternative, and I even like it. I don't know which compilers need the enum workaround and if Boost should support them.
The list seems to be: VC7 and earlier Vacpp 5.02 and earlier (currently 11.1) Sunpro 5.6 and earlier. MPW all versions?
What I want is just that integral_constant use them so that all the traits in Boost conforms to the C++11 standard.
Out of curiosity, can you detect the difference? What can you do with constexpr values that you can't with static const ones? John.
Le 21/04/13 17:45, John Maddock a écrit :
I'm not against this radical alternative, and I even like it. I don't know which compilers need the enum workaround and if Boost should support them.
The list seems to be:
VC7 and earlier Vacpp 5.02 and earlier (currently 11.1) Sunpro 5.6 and earlier. MPW all versions?
What I want is just that integral_constant use them so that all the traits in Boost conforms to the C++11 standard.
Out of curiosity, can you detect the difference? What can you do with constexpr values that you can't with static const ones?
Even if most of the uses of integral_constant is to use its member
::value integral constant defines also the operator(), so it is a
nullary functor.
integral_constant
2013/4/22 Vicente J. Botet Escriba
Out of curiosity, can you detect the difference? What can you do with
constexpr values that you can't with static const ones?
Even if most of the uses of integral_constant is to use its member ::value integral constant defines also the operator(), so it is a nullary functor.
integral_constant
a; constexpr int f(); template <int I> void g();
template <typename F> void h(F&& fct) { g
(); } h(&f); h(a); The last sentence will not compile if the operator() is not a constexpr and it can not be a constexpr if ::value is not a consexpr, or can it?
Really?? I thought a static constant can be used in constexpr functions, just like in C++03 it could be used as a non-type template parameter or static array size. Am I wrong here? Regards, Kris
Le 22/04/13 16:24, Krzysztof Czainski a écrit :
2013/4/22 Vicente J. Botet Escriba
Out of curiosity, can you detect the difference? What can you do with
constexpr values that you can't with static const ones?
Even if most of the uses of integral_constant is to use its member ::value integral constant defines also the operator(), so it is a nullary functor.
integral_constant
a; constexpr int f(); template <int I> void g();
template <typename F> void h(F&& fct) { g
(); } h(&f); h(a); The last sentence will not compile if the operator() is not a constexpr and it can not be a constexpr if ::value is not a consexpr, or can it?
Really?? I thought a static constant can be used in constexpr functions, just like in C++03 it could be used as a non-type template parameter or static array size. Am I wrong here?
You are right, but I was not using an static constant but a static function. Best, Vicente
Even if most of the uses of integral_constant is to use its member ::value integral constant defines also the operator(), so it is a nullary functor.
OK, but we can add that now without changing BOOST_STATIC_CONSTANT.
The last sentence will not compile if the operator() is not a constexpr and it can not be a constexpr if ::value is not a consexpr, or can it?
Sure it can, the member value is an interal-constant-expression however we define it so it can be used in all constexpr functions. John.
2013/4/21 John Maddock
I'm not against this radical alternative, and I even like it. I don't know which compilers need the enum workaround and if Boost should support them.
The list seems to be:
VC7 and earlier Vacpp 5.02 and earlier (currently 11.1) Sunpro 5.6 and earlier. MPW all versions?
Deprecating BOOST_STATIC_CONSTANT macro (and removing it in future releases) will cause a lot of code changes in existing Boosts and users projects. I still think that making BOOST_STATIC_CONSTANT macro to use constexpr by default will cause less troubles and code changes. All libraries will be updated automatically and we'll avoid situation where some libraries use old macro, some use new macro. User won't be confused by 'oh, can I call operator() at compile time for trait X or not?'. Note about that change on the title page and in BOOST_STATIC_CONSTANT docs will help users to adopt changes smooth. -- Best regards, Antony Polukhin
Deprecating BOOST_STATIC_CONSTANT macro (and removing it in future releases) will cause a lot of code changes in existing Boosts and users projects.
I'm not suggesting we remove it, just mark it as "don't use in new code" and provide patches for library authors to switch to the new macro.
I still think that making BOOST_STATIC_CONSTANT macro to use constexpr by default will cause less troubles and code changes.
You can't do that without substantial code changes in library (and possibly user) code, because the out-of-line definition needed changes. So you would have to change both the macro, and many uses of it all at once, manage merging all that to release, and deal with angry users wondering why their code has suddenly broken. I accept not all uses of BOOST_STATIC_CONSTANT provide an out-of-line definition, but they probably should. IMO such a change is a non-starter. John.
Le 20/04/13 12:12, Antony Polukhin a écrit :
2013/4/20 Vicente J. Botet Escriba
: <...> #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION template
bool const ice_eq ::value; // ********* template bool const ice_ne ::value; // ********* #endif for which I have needed to change const by BOOST_CONSTEXPR_OR_CONST. <...> What am I missing? Looks like it shall be #ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION not #ifndef
But how did it work before? Or nobody tested it on ancient compilers? The documentation says
|BOOST_NO_INCLASS_MEMBER_INITIALIZATION| Compiler Compiler violates std::9.4.2/4. and 9.4.2 Static data members [class.static.data] 2 The declaration of a static data member in its class definition is not a definition and may be of an incomplete type other than cv-qualified void. The definition for a static data member shall appear in a namespace scope enclosing the member's class definition. I think that the definition must be done when |BOOST_NO_INCLASS_MEMBER_INITIALIZATION is not defined, but the initialization could be done on the declaration. Could someone clarify this point? Best, Vicente |
participants (4)
-
Antony Polukhin
-
John Maddock
-
Krzysztof Czainski
-
Vicente J. Botet Escriba