I needed to sort types in a tuple by alignment, and as a result I just want to get a new tuple type.
Wrote some implementation. But in one case (when using consteval auto sort_tuple(auto tuple)
) gcc and clang give error, and in another case (when using most enclosing function as lambda) msvc gives error but in different place for (seemingly) different reason. Code (godbolt):
#include <cstddef>
#include <concepts>
#include <utility>
#include <array>
#include <algorithm>
using Index = std::size_t;
using Alignment = std::size_t;
struct Key {
Index old;
Alignment a;
};
template<::Key k, typename T> struct Elem {};
template<typename ...Elems> struct Map : Elems... {};
template<typename ...Ts> struct Tuple {};
struct alignas(2) s_1 {};
struct alignas(2) s_2 {};
struct alignas(2) s_3 {};
struct alignas(2) s_4 {};
using T1 = Tuple<char, short, s_1, s_2, s_3, s_4, int, double>;
#define CLANG_GCC_DONT_WORK
//#define MSVC_DOESNT_WORK_BUT_DIFFERENT_PLACE
#if defined(CLANG_GCC_DONT_WORK)
consteval auto sort_tuple(auto tuple)
{
#elif defined(MSVC_DOESNT_WORK_BUT_DIFFERENT_PLACE)
using Sorted = decltype([]() consteval {
auto tuple = T1{};
#endif
constexpr auto size = []<typename ...Ts>(::Tuple<Ts...>&){
return sizeof...(Ts);
}(tuple);
constexpr auto unsorted = []<
typename ...Ts,
auto ...Is
>(::Tuple<Ts...>&, std::index_sequence<Is...>&&){
return std::array<::Key, size>{::Key{Is, alignof(Ts)}...};
}(tuple, std::make_index_sequence<size>{});
using Unsorted = decltype([]<typename ...Ts, auto ...Is>(
::Tuple<Ts...>&, std::index_sequence<Is...>&&
){
return ::Map<
::Elem<::Key{Is, alignof(Ts)}, Ts>...
>{};
}(tuple, std::make_index_sequence<size>{}));
constexpr auto sorted = [](auto unsorted){
std::sort(
unsorted.begin(), unsorted.end(),
[](const auto& lhs, const auto& rhs){
return lhs.a > rhs.a;
}
);
return unsorted;
}(unsorted);
// gcc and clang complaining here
// (when using CLANG_GCC_DONT_WORK macro),
// I'm using trick with template argument deduction,
// and those, it seems, can't deduce
//
// also "sorted" std::array passed as template parameter,
// but when passing it through capture
// all compilers give error at this place
using Sorted = decltype([]<auto sorted, auto ...Is>(
std::index_sequence<Is...>&&
){
return ::Tuple<
decltype([]<::Key k, typename T>(
[[maybe_unused]] ::Elem<k, T>&& deduced_by_compiler
){
return T{};
}.template operator()<sorted[Is]>(Unsorted{}))...
>{};
}.template operator()<sorted>(std::make_index_sequence<size>{}));
return Sorted{};
#if defined(CLANG_GCC_DONT_WORK)
};
using Sorted = decltype(sort_tuple(T1{}));
#elif defined(MSVC_DOESNT_WORK_BUT_DIFFERENT_PLACE)
}());
#endif
int main() {}
So:
Also if somebody happen to know a working implementation of such sorting in C++20 and below, I would appreciate if you link it in comments or something
Can't say this code 100% standard complaint, but it definitely exposes some bugs from every of these three compilers. Here's links to bug reports with minimized examples created from initial example, if interested:
With regard to "fixes", MSVC already works pretty much. And @Jarod42 able to make Clang happy (Godbolt).
Here's working code for GCC (Godbolt):
#include <cstddef>
#include <concepts>
#include <utility>
#include <array>
#include <algorithm>
using Index = std::size_t;
using Alignment = std::size_t;
struct Key {
Index old;
Alignment a;
};
template<::Key k, typename T> struct Elem {};
template<typename ...Elems> struct Map : Elems... {};
template<typename ...Ts> struct Tuple {};
struct alignas(2) s_1 {};
struct alignas(2) s_2 {};
struct alignas(2) s_3 {};
struct alignas(2) s_4 {};
using T1 = Tuple<char, short, s_1, s_2, s_3, s_4, int, double>;
template<::Key...> struct Key_seq {};
template<auto array, auto len>
using Make_key_seq = decltype([]<auto ...Is>(std::index_sequence<Is...>&&){
return ::Key_seq<array[Is]...>{};
}(std::make_index_sequence<len>{}));
template<typename ...Ts_>
consteval auto sort_tuple(::Tuple<Ts_...>&& tuple)
{
constexpr auto size = []<typename ...Ts>(::Tuple<Ts...>&){
return sizeof...(Ts);
}(tuple);
constexpr auto unsorted = []<
typename ...Ts,
auto ...Is
>(::Tuple<Ts...>&, std::index_sequence<Is...>&&){
return std::array<::Key, size>{::Key{Is, alignof(Ts)}...};
}(tuple, std::make_index_sequence<size>{});
constexpr auto sorted = [](auto unsorted){
std::sort(
unsorted.begin(), unsorted.end(),
[](const auto& lhs, const auto& rhs){
return lhs.a > rhs.a;
}
);
return unsorted;
}(unsorted);
// changed this part, that now uses 'Make_key_seq'
using Sorted = decltype([]<auto ...Keys, auto ...Is>(
::Key_seq<Keys...>&&,
std::index_sequence<Is...>&&
){
// needed to move it inside here, otherwise error
using Unsorted = decltype([]<typename ...Ts>(::Tuple<Ts...>&){
return ::Map<
::Elem<::Key{Is, alignof(Ts)}, Ts>...
>{};
}(tuple));
return ::Tuple<
decltype([]<::Key k, typename T>(
[[maybe_unused]] ::Elem<k, T>&& deduced_by_comp
){
return T{};
}.template operator()<Keys>(Unsorted{}))...
>{};
}(::Make_key_seq<sorted, size>{}, std::make_index_sequence<size>{}));
return Sorted{};
};
using Sorted = decltype(sort_tuple(T1{}));
using Correct = Tuple<double, int, short, s_1, s_2, s_3, s_4, char>;
static_assert(std::same_as<Correct, Sorted>);
auto main() -> int;