Morning, folks!
I'm refactoring an event queue. I'm poking around to see if I can make event ids unique at compile time. What I've come up with works with clang 4.0.0, but gives a compile error with g++ 6.3.1.
The idea is to use the address of a static member variable to uniquely identify individual types, then use tagging to generate these unique types from a class template.
Using the address of a static member as a type id is a reasonably common technique, but using templates to do it means being clear of the ODR. MSN cites the standard here to suggest that this is a valid approach:
Compile-time constant id
My problem is doing this constexpr. If I remove constexpr and test this at runtime, everything works as expected. Doing this constexpr, however, fails a static assert in g++ saying, "error: non-constant condition for static assertion".
After researching quite a bit, it seems the most similar problems are:
Pointer arithmetic or calling non-constexpr members: Why is this not a constant expression?
There is no pointer arithmetic, everything is constexpr.
Using reinterpret_cast in a constexpr expression: Constexpr pointer value
This is using pointers, but the types are verbatim; so no conversions are applied.
Using incomplete types: Why is this constexpr static member function not seen as constexpr when called?
I'm reasonably sure this type is complete.
Most of these problems are g++ being nonconforming and clang++ erroring out. This is the opposite.
I'm stumped. Here's a stripped-down version of what I've got, annotated with what doesn't compile in g++ in the static asserts:
template <typename tag>
struct t
{
constexpr static char const storage{};
};
template <typename tag>
constexpr char const t<tag>::storage;
struct tag_0 {};
struct tag_1 {};
static_assert(&t<tag_0>::storage == &t<tag_0>::storage, "This always compiles.");
static_assert(&t<tag_1>::storage == &t<tag_1>::storage, "So does this.");
static_assert(&t<tag_0>::storage != &t<tag_1>::storage, "This does not compile with g++.");
static_assert(!(&t<tag_0>::storage == &t<tag_1>::storage), "Neither does this.");
constexpr auto id_0 = &t<tag_0>::storage; // This does.
constexpr auto id_1 = &t<tag_1>::storage; // This does.
static_assert(id_0 != id_1, "This also does not.");
And here are the compiler outputs:
~$ clang++ --version
clang version 4.0.0 (tags/RELEASE_400/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
~$ clang++ -std=c++14 -c example.cpp
~$ g++ --version
g++ (GCC) 6.3.1 20170306
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
example.cpp:14:1: error: non-constant condition for static assertion
static_assert(&t<tag_0>::storage != &t<tag_1>::storage, "This does not compile with g++.");
^~~~~~~~~~~~~
example.cpp:14:34: error: ‘((& t<tag_0>::storage) != (& t<tag_1>::storage))’ is not a constant expression
static_assert(&t<tag_0>::storage != &t<tag_1>::storage, "This does not compile with g++.");
~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
example.cpp:15:1: error: non-constant condition for static assertion
static_assert(!(&t<tag_0>::storage == &t<tag_1>::storage), "Neither does this.");
^~~~~~~~~~~~~
example.cpp:15:15: error: ‘((& t<tag_0>::storage) != (& t<tag_1>::storage))’ is not a constant expression
static_assert(!(&t<tag_0>::storage == &t<tag_1>::storage), "Neither does this.");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
example.cpp:19:1: error: non-constant condition for static assertion
static_assert(id_0 != id_1, "This also does not.");
^~~~~~~~~~~~~
example.cpp:19:20: error: ‘((& t<tag_0>::storage) != (& t<tag_1>::storage))’ is not a constant expression
static_assert(id_0 != id_1, "This also does not.");
~~~~~^~~~~~~
~$
I'm curious why this specific approach doesn't compile with gcc, but does with clang, because this conflicts with how I understand constexpr.
(I'm not asking if this is a good design, or if there are other ways to accomplish this. I have other ways to accomplish this.)
Thanks!
EDIT: A comparable example without templates that does compile with both compilers might be:
struct t1
{
static constexpr int const v{};
};
constexpr int t1::v;
struct t2
{
static constexpr int const v{};
};
constexpr int t2::v;
static_assert(&t1::v != &t2::v, "compiles with both");
You're trying to compare two distinct pointers to class members as constexpr and it isn't specified in standard, if compiler should evaluate it as constexpr (addresses of objects might not be yet known?). MSVC and clang does that, gcc doesn't. The result for comparision of pointer to itself is defined.
Related
We are currently in the working of including the latest intel compiler into our pipeline for a C++14 project, but I cannot figure out two specific sections that include NaN comparisons in constexpr classes, one of which is featured below as a minimum (non-)working example. The code compiles just fine with g++, but fails with icpc (code and output see below). The numeric_limits are constexpr in C++14 and also reimplementing them gave the same error, but if I comment out the two lines featuring NaN, the code compiles with icpc
mwe.h
#include <cassert>
#include <limits>
#include <math.h>
inline constexpr double toRadians(const double inDegrees) noexcept {
return M_PI * inDegrees / 180;
}
template<typename T>
class ExpandedAbsoluteEqualityComparator {
private:
const T absoluteTolerance_;
public:
constexpr ExpandedAbsoluteEqualityComparator(const T absoluteTolerance)
: absoluteTolerance_(absoluteTolerance)
{
assert(
absoluteTolerance > 0
&& absoluteTolerance != std::numeric_limits<T>::infinity()
&& absoluteTolerance != std::numeric_limits<T>::quiet_NaN()
&& absoluteTolerance != std::numeric_limits<T>::signaling_NaN()
);
}
};
class SomeClass {
public:
//! 1° absolute tolerance floating point comparison helper for angle groups
static constexpr ExpandedAbsoluteEqualityComparator<const double> fpComparator {
toRadians(1.0)
};
};
mwe.cpp
#include "mwe.h"
int main() {return 0;}
compilation
g++ -pedantic -std=c++14 mwe.cpp # works (version 10.1.0)
icpc -std=c++14 mwe.cpp # fails (version icpc (ICC) 2021.4.0 20210910)
intel compilation error
In file included from mwe.cpp(1):
mwe.h(30): error: expression must have a constant value
static constexpr ExpandedAbsoluteEqualityComparator<const double> fpComparator {
^
mwe.h(18): note: floating-point values cannot be compared
assert(
^
compilation aborted for mwe.cpp (code 2)
Changing to
//&& absoluteTolerance != std::numeric_limits<T>::quiet_NaN()
//&& absoluteTolerance != std::numeric_limits<T>::signaling_NaN()
lets me compile with icpc
So still pretty sure this is an actual fault in the intel compiler.
Since I didn't want to remove the assert statement or the NaN check in the assert, the quick fix I ended up using was to replace the NaN comparison
absoluteTolerance != std::numeric_limits<T>::quiet_NaN() && absoluteTolerance != std::numeric_limits<T>::signaling_NaN()
with an equal to self comparison
absoluteTolerance == absoluteTolerance
assert is NOT on the list of things which are allowed in constexpr functions. It's a macro, so different compilers may expand it in different ways. Some of those expansions might just happen to be allowed in a constexpr function, but this is Unspecified. It is not a fault of any compiler if they chose to implement assert in a way that's incompatible with constexpr.
static_assert IS on the list of things allowed in constexpr functions.
#Holt, you are correct. Using -DNDEBUG you can get rid of the error.
Compilation command:icpc -DNDEBUG -std=c++14 mwe.cpp
You must #define NDEBUG (or use the flag -DNDEBUG) this will disable assert.
The assert statement so far is executed at run time.
I happen to find that GCC and Clang differ in the compilation of the following code:
struct Foo
{
int mem = 42;
};
int main()
{
constexpr Foo foo;
static_assert(__builtin_constant_p(foo));
return 0;
}
I compile with g++ -std=c++17 and clang++ -std=c++17.
In particular,
g++ g++-9 (Homebrew GCC 9.3.0_1) 9.3.0 compiles, whereas
clang++ Apple clang version 11.0.3 (clang-1103.0.32.62) fails to compile, complaining that
error: static_assert failed due to requirement '__builtin_constant_p(foo)'
static_assert(__builtin_constant_p(foo));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~
I didn't find any hint that there should be any difference regarding __builtin_constant_p.
For __builtin_constant_p
GCC says
You can use the built-in function __builtin_constant_p to determine if a value is known to be constant at compile time ...
Clang
says
Clang supports a number of builtin library functions with the same syntax as GCC, including things like __builtin_nan, __builtin_constant_p, __builtin_choose_expr, __builtin_types_compatible_p, __builtin_assume_aligned, __sync_fetch_and_add, etc.
Question: While I know __builtin_constant_p is a compiler extension, which one should be the correct one?
Both are poorly documented so I doubt there is a proper answer to your question.
If you need a workaround: it seems that clang gives up if the argument is not an int.
So this works:
struct Foo
{
int mem = 42;
};
int main()
{
constexpr Foo foo;
static_assert(__builtin_constant_p(foo.mem));
return 0;
}
I would like to communicate some Informations between threads.
Atomics sound like the thing to use.
I had a look at this. And found that a simple struct like
struct MyType{
int val_a,val_b;
float vals_c[5];
};
should fullfill the assert:
static_assert(
std::is_trivially_copyable<MyType>::value &&
std::is_copy_constructible<MyType>::value &&
std::is_move_constructible<MyType>::value &&
std::is_copy_assignable<MyType>::value &&
std::is_move_assignable<MyType>::value,
"MyType is not suitable for std::atomic");
)
But a simple program as
//... MyType and static_assert
int main(int argc,char *argv[]){
MyType a;
std::atomic<MyType> b;
b = a;
a = b;
return 0;
}
fails to compile with:
undefined reference to `__atomic_store'
undefined reference to `__atomic_load'
I am using gcc version 5.4 on a 64-bit ubuntu 16.04.
Flags used are: -pipe -g -std=gnu++11 -Wall -W -fPIC
Is it that this is a total wrong use of std::atomic? What are the requirements to MyType? Or is just something missing from this setup?
As always, documentation is your friend:
The primary std::atomic template may be instantiated with any TriviallyCopyable type T satisfying both CopyConstructible and CopyAssignable. The program is ill-formed if any of following values is false:
std::is_trivially_copyable<T>::value
std::is_copy_constructible<T>::value
std::is_move_constructible<T>::value
std::is_copy_assignable<T>::value
std::is_move_assignable<T>::value
So, your assert is good.
Later it says:
On gcc and clang, some of the functionality described here requires linking against -latomic.
So, if the program still fails to build, you may need to link against libatomic.
If it still doesn't work, you probably have a compiler bug.
I have this code which compiles fine on Ubuntu, but when I try to compile it on MacOS I get this error:
Constexpr variable 'HeuristicNames' must be initialized by a constant expression
#define LSHPair std::pair<const char *, LISTSCHED_HEURISTIC>
static constexpr LSHPair HeuristicNames[] = {
LSHPair("CP", LSH_CP), LSHPair("LUC", LSH_LUC),
LSHPair("UC", LSH_UC), LSHPair("NID", LSH_NID),
LSHPair("CPR", LSH_CPR), LSHPair("ISO", LSH_ISO),
LSHPair("SC", LSH_SC), LSHPair("LS", LSH_LS),
LSHPair("LLVM", LSH_LLVM)};
LISTSCHED_HEURISTIC is an enum.
I take this error to mean that some part of the right hand side of the assignment is not a constexpr, so the resulting variable can't be a constexpr. However I don't have a firm enough grasp of the rules around constexpr to understand why, or how to fix it.
I also don't get why this is different on MacOS than on Ubuntu. Can anyone shed some light on this?
First of all you do not need macro.
You could define type
using LSHPair = std::pair<const char *, LISTSCHED_HEURISTIC>;
Or just use brace initialization, what is much more clean:
using LSHPair = std::pair<const char *, LISTSCHED_HEURISTIC>;
static constexpr LSHPair HeuristicNames[] = {
{"CP", LSH_CP},
{"LUC", LSH_LUC},
{"UC", LSH_UC},
{"NID", LSH_NID},
{"CPR", LSH_CPR},
{"ISO", LSH_ISO},
{"SC", LSH_SC},
{"LS", LSH_LS},
{"LLVM", LSH_LLVM}
};
As #CuriouslyRecurringThoughts point out constructor of std::pair is a constexpr since c++14.
I've test this on my Mac OS and apparently clang there works a bit differently then on Linux (as shown on compiler explorer):
Marek R$ g++ main.cpp -std=c++11
main.cpp:17:26: error: constexpr variable 'HeuristicNames' must be initialized by a constant expression
static constexpr LSHPair HeuristicNames[] = {
^ ~
main.cpp:18:5: note: non-constexpr constructor 'pair<char const (&)[3], LISTSCHED_HEURISTIC, false>' cannot be used in a constant expression
{"CP", LSH_CP},
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/utility:446:5: note: declared here
pair(_U1&& __u1, _U2&& __u2)
^
1 error generated.
Marek R$ g++ main.cpp -std=c++14
Marek R$
So MacOS clang is right.
Problem must be on header files and versioning them depending on C++ standard.
Most probably same header files of standard library are used for clang and gcc are used on Linux. On Mac std::pair constructor is prefixed by macro _LIBCPP_CONSTEXPR_AFTER_CXX11 which definition changes deeding on C++ standard enabled. On Linux you have to check by yourself how it is done.
In C++, it is possible for pointer values to be compile-time constants. This is true, otherwise, non-type template parameters and constexpr won't work with pointers. However, as far as I know, addresses of functions and objects of static storage are known (at least) at link-time rather than compile-time. Following is an illustration:
main.cpp
#include <iostream>
template <int* p>
void f() { std::cout << p << '\n'; }
extern int a;
int main() {
f<&a>();
}
a.cpp
int a = 0;
I'm just wondering how the address of a could possibly be known when compiling main.cpp. I hope somebody could explain this a little to me.
In particular, consider this
template <int* p, int* pp>
constexpr std::size_t f() {
return (p + 1) == (pp + 7) ? 5 : 10;
}
int main() {
int arr[f<&a, &b>()] = {};
}
How should the storage for arr be allocated?
PLUS: This mechanism seems to be rather robust. Even when I enabled Randomized Base Address, the correct output is obtained.
The compiler doesn't need to know the value of &a at compile time any more than it needs the value of function addresses.
Think of it like this: the compiler will instantiate your function template with &a as a parameter and generate "object code" (in whatever format it uses to pass to the linker). The object code will look like (well it won't, but you get the idea):
func f__<funky_mangled_name_to_say_this_is_f_for_&a>__:
reg0 <- /* linker, pls put &std::cout here */
reg1 <- /* hey linker, stuff &a in there ok? */
call std::basic_stream::operator<<(int*) /* linker, fun addr please? */
[...]
If you instantiate f<b&>, assuming b is another global static, compiler does the same thing:
func f__<funky_mangled_name_to_say_this_is_f_for_&b>__:
reg0 <- /* linker, pls put &std::cout here */
reg1 <- /* hey linker, stuff &b in there ok? */
call std::basic_stream::operator<<(int*) /* linker, fun addr please? */
[...]
And when your code calls for calling either of those:
fun foo:
call f__<funky_mangled_name_to_say_this_is_f_for_&a>__
call f__<funky_mangled_name_to_say_this_is_f_for_&b>__
Which exact function to call is encoded in the mangled function name.
The generated code doesn't depend on the runtime value of &a or &b.
The compiler knows there will be such things at runtime (you told it so), that's all it needs. It'll let the linker fill in the blanks (or yell at you if you failed to deliver on your promise).
For your addition I'm afraid I'm not familiar enough about the constexpr rules, but the two compilers I have tell me that this function will be evaluated at runtime, which, according to them, makes the code non-conforming. (If they're wrong, then the answer above is, at least, incomplete.)
template <int* p, int* pp>
constexpr std::size_t f() {
return (p + 1) == (pp + 7) ? 5 : 10;
}
int main() {
int arr[f<&a, &b>()] = {};
}
clang 3.5 in C++14 standards conforming mode:
$ clang++ -std=c++14 -stdlib=libc++ t.cpp -pedantic
t.cpp:10:10: warning: variable length arrays are a C99 feature [-Wvla-extension]
int arr[f<&a, &b>()];
^
1 warning generated.
GCC g++ 5.1, same mode:
$ g++ -std=c++14 t.cpp -O3 -pedantic
t.cpp: In function 'int main()':
t.cpp:10:22: warning: ISO C++ forbids variable length array 'arr' [-Wvla]
int arr[f<&a, &b>()];
As far as I know, the variables of static storage and functions are stored simply as symbols/place holders in the symbol table while compiling. It is in the linking phase when the place holders are resolved.
The compiler outputs machine code keeping the placeholders intact. Then the linker replaces the placeholders of the variables / functions with their respective memory locations. So in this case too, if you just compile main.cpp without compiling a.cpp and linking with it, you are bound to face linker error, as you can see here http://codepad.org/QTdJCgle (I compiled main.cpp only)