For writing SIMD code, I'd like to use templates to generate vector types with certain alignment. However, clang seems to ignore the alignment attribute when used with an alias template instead of a type alias.
Consider this code (godbolt):
#include <iostream>
#include <cstdint>
template <typename T=void>
using TemplatedT __attribute__((aligned(8))) = uint32_t;
using ManualT __attribute__((aligned(8))) = uint32_t;
int main() {
std::cout << "alignof Template: " << alignof(TemplatedT<>) << std::endl;
std::cout << "alignof Manual: " << alignof(ManualT) << std::endl;
}
With gcc 12.2, both aliases have an alignment of 8, as I would expect.
With clang 15 and trunk, the templated alias has an alignment of 4, ignoring the attribute.
Is this a bug? I looked through the open llvm issues but couldn't find a matching one.
Is there any workaround I can use?
Edit: As #HolyBlackCat proposed in the comments, extracting the templating to a wrapping struct seems to be a workaround:
template<typename ScalarT>
struct Vec {
using T __attribute__((aligned(8))) = ScalarT;
};
// and then use Vec<uint32_t>::T
Edit2:
I've opened an issue with the LLVM project at https://github.com/llvm/llvm-project/issues/59788.
Related
Update: Just updated MSVC compiler to 17.4.5.
MSVC now says that A and Az are compatible types in Release mode but not in Debug mode when std::vector is included in the structs. Really weird. May be related to the size difference of vectors. Vectors in Release mode consist of the expected 3 pointers. In debug mode it's expanded by an additional pointer which appears to be added in for some sort of additional use verification.
According to this description of layout compatible types, struct A and struct Az should qualify as the struct contents are identical.
Two types cv1 T1 and cv2 T2 are layout-compatible types if T1 and T2 are the same type, layout-compatible enumerations, or layout-compatible standard-layout class types.
Clang fails to compile as std::is_layout_compatible isn't implimented. With both vector and int, gcc works and returns true for is_layout_compatible while MSVC returns true in Release mode but false in Debug mode.
compiler explorer int and vector
compiler explorer int only
#include <memory>
#include <iostream>
#include <vector>
#include <type_traits>
struct Az {
std::vector<int> v;
int i;
};
struct A {
std::vector<int> v;
int i;
};
int main()
{
std::cout << "sizeof:" << sizeof(A) << " Layout_compatible:" << std::is_layout_compatible_v<A, Az> << '\n';
}
MSVC 2022 Output Debug mode:
sizeof:40 Layout_compatible:0
MSVC 2022 Output Release mode:
sizeof:32 Layout_compatible:1
Consider the following code snippet:
struct v : std::variant<int, std::vector<v>> { };
int main()
{
std::visit([](auto){ }, v{0});
}
clang++ 7 with -stdlib=libc++ -std=c++2a compiles the code;
g++ 9 with -std=c++2a fails to compile the code, with the following error:
/opt/compiler-explorer/gcc-trunk-20180711/include/c++/9.0.0/variant:94:29:
error: incomplete type 'std::variant_size' used in nested name
specifier
inline constexpr size_t variant_size_v = variant_size<_Variant>::value;
^~~~~~~~~~~~~~
live example on godbolt.org
Are both implementations conforming to the Standard?
If not, what implementation is correct here, and why?
[variant.visit] in C++17 doesn't use variant_size_v, but it does in the current working draft as a result of an editorial change. I don't see any indication that LWG reviewed the change before it went in, but it has looked at this part of the standard several times since then and has yet to object to it, so I'm going to postulate that it is in fact required.
Meanwhile, LWG issue 3052, which has been referred to LEWG, would explicitly require std::variant. When that issue is resolved - one way or the other - it should resolve this too.
Looks like it is a bug in gcc implementation. According to cppreference, it is called as if calling invoke on a std::get. std::get<> is defined for anything which is convertible to std::variant (since it accepts a std::variant argument by forwarding reference). Your structure is convertible to std::variant, and so std::get itself works on your structure in gcc.
The fact that the gcc implementation chose to use a std::variant_size as part of its implementation of visit is their implementation detail, and the fact that it doesn't (and shouldn't) work for your struct is irrelevant.
Conclusion: It is a bug in gcc due to an oversight in implementation.
I came across this issue as well recently. I kind of came up with a workaround which basically specialises variant_size and variant_alternative for the class that inherits from the variant..
link on godbolt
Its not pretty and it injects stuff into the std namespace. I'm not a metaprogramming expert (yet!) so its something I hacked together. Maybe someone else can improve on this?
#include <variant>
#include <string>
#include <vector>
#include <iostream>
#include <utility>
#include <type_traits>
using var = std::variant<int, bool, float, std::string>;
struct myvar : public var {
using var::var;
using var::operator=;
};
namespace std{
template<>
struct variant_size<myvar> : variant_size<var> {
};
template<std::size_t I>
struct variant_alternative<I,myvar> : variant_alternative<I,var> {
};
}
int main(){
constexpr int vs = std::variant_size<var>::value;
myvar s = std::string{"boo!"};
std::visit([](auto&& e){std::cout << e << "\n";}, s);
std::cout << vs;
}
In my project I need separate thread-local storage for each instance of a data member. Because I ran into problems while implementing this functionality, I extracted a simplified version of the code into the following C++14 program:
#include <iostream>
#include <unordered_map>
#include <vector>
template<class T> class ThreadLocalMember
{
public:
T& local() { return store.map[this]; }
private:
struct Store
{
Store() { std::cout << "construct" << std::endl; }
~Store() { std::cout << "destruct" << std::endl; }
std::unordered_map<ThreadLocalMember<T>*, T> map;
};
static thread_local Store store;
};
template <class T> thread_local typename ThreadLocalMember<T>::Store ThreadLocalMember<T>::store;
int main()
{
ThreadLocalMember<int> counter;
std::cout << "point 1" << std::endl;
int result = counter.local();
std::cout << "point 2; result: " << result << std::endl;
return result;
}
The expected output is
point 1
construct
point 2; result: 0
destruct
However, when compiled with clang Apple LLVM version 9.1.0 (clang-902.0.39.1) on MacOS High Sierra 10.13.4 using
clang++ -std=c++14 -O3 ThreadLocalMember.cpp -o test
(or with -O1 or -O2) the output is:
point 1
Illegal instruction: 4
It seems that the constructor of the thread_local variable is never executed and the program crashes when the variable is first accessed.
The problem goes away when
the program is compiled without optimisation (which is not acceptable in production mode)
the template class is replaced by a regular class (a possible workaround but very annoying)
the thread_local keyword is removed in both places (but then the program no longer does what I need it to do when there are multiple threads)
The program also compiles and runs fine when using gcc 5.4.0 on Ubuntu 16, with or without optimisation flag.
Is there something wrong with my code, or am I looking at a clang compiler bug?
This appears to be a visual-studio problem. This code runs fine in gcc but fails to compile in Visual Studio:
#include <iostream>
#include <type_traits>
#include <typeinfo>
using namespace std;
true_type foo();
template <typename T>
struct bar{
using def = conditional_t<decltype(foo())::value, char, void>;
};
int main() {
cout << typeid(bar<int>::def).name() << endl;
cout << decltype(foo())::value << endl;
}
The error given is:
error C2146: syntax error: missing > before identifier value
Live Example
Is there a bug fix for this or a workaround?
In your question you're using decltype(foo()):
using def = conditional_t<decltype(foo())::value, char, void>;
^^^^^
while on Ideone, decltype(foo):
using def = conditional_t<decltype(foo)::value, char, void>;
^^^^^
They're different things. In the first case, you're getting the type of the result of calling foo. In the second one you're getting the type of a function itself.
Okay, since that time things changed dramatically.
The code was edited and was supposed to compile well, but compilations with Visual Studio were failing, while clang was very happy with this code and wasn't showing any errors and even warnings.
So, given that clang (the latest version, using --std=c++14 -Wall -Wextra) finds this code correct, I believe this should be a bug in VS.
I'm not sure if it can help you, but I am able to work around the problem like this:
First define:
template <typename I> using id = I;
Then replace every instance of decltype(foo())::value with
id<decltype(foo())>::value
Alternately, you could use std::common_type_t the same way:
std::common_type_t<foo()>::value
Or, my psychic powers predict you might just want to define a separate type for decltype<foo()>, for convenience:
using id = decltype(foo());
then replace all instances of decltype(foo())::value with id::value.
I manage to use Boost MPL vectors and lists just fine, but I just can't figure out maps. When I try to insert into one, I get "too few arguments" from clang 3.1 (gcc 4.7 says something similar). There is a version of insert where the second argument is POS, which is supposed to be ignored, so I tried inserting a dummy type (int) there, but that just gives a new and confusing error.
include <iostream>
#include <boost/mpl/key_type.hpp>
#include <boost/mpl/map.hpp>
using namespace boost;
using namespace mpl;
int main(){
typedef pair<int_<3>, int_<6>> obj;
std::cout << key_type<map<>, obj >::type::value << std::endl; //works
std::cout << has_key<insert<map<>, obj>::type, obj)::type::value << std::endl; //complains on "too few template arguments for class template 'insert'
std::cout << has_key<insert<map<>, int, obj>::type, obj)::type::value << std::endl; // gives "implicit instantiation of undefined template 'boost::mpl::insert<..."
}
MPL errors aren't exactly helpful, even with clang, so I just don't understand what I am doing wrong here? I am sure it is something silly.
http://www.boost.org/doc/libs/1_51_0/libs/mpl/doc/refmanual/map.html
Add
#include <boost/mpl/insert.hpp>
and correct brackets, from ')' to '>'
http://liveworkspace.org/code/afb6632c3eb800412ea551f50c07fb0a