C++ library to compile-time merge sets to a single array
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
7
down vote
favorite
I built this small library to create a compile-time assembled array from several hard-coded sequences (of unequal length) of a type T
:
mergeable_set.hpp
#pragma once
template <typename T>
class mergeable
public:
template <T... Ts>
struct set
static constexpr T values Ts...;
constexpr const auto& operator(const int i) const noexcept return values[i];
constexpr auto size() const noexcept return sizeof...(Ts);
class iterator
public:
constexpr explicit iterator(set& _ref, const int i = 0) : ref_ref, indexi
constexpr iterator& operator++() noexcept ++index; return *this;
constexpr bool operator!=(const iterator& other) const noexcept return index != other.index;
constexpr auto& operator* () noexcept return ref[index];
private:
set& ref;
int index;
;
class citerator
public:
constexpr citerator(const set& _ref, const int i) : ref_ref, indexi
constexpr citerator& operator++() noexcept ++index; return *this;
constexpr bool operator!=(const citerator& other) const noexcept return index != other.index;
constexpr const auto& operator* () const noexcept return ref[index];
private:
const set& ref;
int index;
;
constexpr iterator begin() noexcept return iterator*this, 0;
constexpr citerator begin() const noexcept return citerator*this, 0;
constexpr iterator end() noexcept return iterator*this, sizeof...(Ts);
constexpr citerator end() const noexcept return citerator*this, sizeof...(Ts);
;
private:
template <typename, typename...> struct concat;
template<T... A, T... B>
struct concat<set<A...>, set<B...>>
using type = set<A..., B...>;
;
template<T... A>
struct concat<set<A...>>
using type = set<A...>;
;
template <typename...> struct _combine ;
template <typename A, typename B, typename... C>
struct _combine<A, B, C...>
using type = typename concat<A, typename _combine<B, C...>::type>::type;
;
template <typename A, typename B>
struct _combine<A, B>
using type = typename concat<A, B>::type;
;
template <typename A>
struct _combine<A>
using type = typename concat<A>::type;
;
public:
template <typename...U>
using combine = typename _combine<U...>::type;
;
Usable like this:
#include "mergeable_set.hpp"
int main()
mergeable<int>::set<1, 2, 3> a;
mergeable<int>::set<4, 5, 6> b;
mergeable<int>::set<1, 2, 3> c;
mergeable<int>::set<4, 5, 6> d;
mergeable<int>::combine<decltype(a),
decltype(b),
decltype(c),
decltype(d)> z;
int temp = 0;
for (auto e : z)
temp += e;
volatile int l = temp;
With z
behaving like a plain array (const int z[12]
).
Interestingly this exposes some strange behaviour in GCC. When compiling x86
code any array over 7 elements generates SIMD bulk add instructions. While this sounds great, disabling SSE actually compiles out all instructions, straight up to the final result. The latter is obviously preferable. Did I just hit a logic bug in GCC here?
A live example of this effect: https://godbolt.org/g/4LAJXV
Remove -mno-sse
to see the difference.
I'd love to hear what can be improved to make this into a more general purpose library.
c++ c++14 template-meta-programming
add a comment |Â
up vote
7
down vote
favorite
I built this small library to create a compile-time assembled array from several hard-coded sequences (of unequal length) of a type T
:
mergeable_set.hpp
#pragma once
template <typename T>
class mergeable
public:
template <T... Ts>
struct set
static constexpr T values Ts...;
constexpr const auto& operator(const int i) const noexcept return values[i];
constexpr auto size() const noexcept return sizeof...(Ts);
class iterator
public:
constexpr explicit iterator(set& _ref, const int i = 0) : ref_ref, indexi
constexpr iterator& operator++() noexcept ++index; return *this;
constexpr bool operator!=(const iterator& other) const noexcept return index != other.index;
constexpr auto& operator* () noexcept return ref[index];
private:
set& ref;
int index;
;
class citerator
public:
constexpr citerator(const set& _ref, const int i) : ref_ref, indexi
constexpr citerator& operator++() noexcept ++index; return *this;
constexpr bool operator!=(const citerator& other) const noexcept return index != other.index;
constexpr const auto& operator* () const noexcept return ref[index];
private:
const set& ref;
int index;
;
constexpr iterator begin() noexcept return iterator*this, 0;
constexpr citerator begin() const noexcept return citerator*this, 0;
constexpr iterator end() noexcept return iterator*this, sizeof...(Ts);
constexpr citerator end() const noexcept return citerator*this, sizeof...(Ts);
;
private:
template <typename, typename...> struct concat;
template<T... A, T... B>
struct concat<set<A...>, set<B...>>
using type = set<A..., B...>;
;
template<T... A>
struct concat<set<A...>>
using type = set<A...>;
;
template <typename...> struct _combine ;
template <typename A, typename B, typename... C>
struct _combine<A, B, C...>
using type = typename concat<A, typename _combine<B, C...>::type>::type;
;
template <typename A, typename B>
struct _combine<A, B>
using type = typename concat<A, B>::type;
;
template <typename A>
struct _combine<A>
using type = typename concat<A>::type;
;
public:
template <typename...U>
using combine = typename _combine<U...>::type;
;
Usable like this:
#include "mergeable_set.hpp"
int main()
mergeable<int>::set<1, 2, 3> a;
mergeable<int>::set<4, 5, 6> b;
mergeable<int>::set<1, 2, 3> c;
mergeable<int>::set<4, 5, 6> d;
mergeable<int>::combine<decltype(a),
decltype(b),
decltype(c),
decltype(d)> z;
int temp = 0;
for (auto e : z)
temp += e;
volatile int l = temp;
With z
behaving like a plain array (const int z[12]
).
Interestingly this exposes some strange behaviour in GCC. When compiling x86
code any array over 7 elements generates SIMD bulk add instructions. While this sounds great, disabling SSE actually compiles out all instructions, straight up to the final result. The latter is obviously preferable. Did I just hit a logic bug in GCC here?
A live example of this effect: https://godbolt.org/g/4LAJXV
Remove -mno-sse
to see the difference.
I'd love to hear what can be improved to make this into a more general purpose library.
c++ c++14 template-meta-programming
4
I'm talking from the ignorance but, would it be possible to combineconstexpr
with initializer lists so that you can achieve the same thing without having so many types?
â Jorge Bellón
Mar 2 at 11:17
3
Since I cant edit my previous comment, I'll add it up here. I've tried myself and it is completely possible to do it with arrays. However, it's limited to C++17 onwards, due tooperator
not being constexpr until then. If you are interested you can check it out here: godbolt.org/g/qfnn54 (it seems not to have the SIMD problem you mention when using-O3
).
â Jorge Bellón
Mar 2 at 12:34
add a comment |Â
up vote
7
down vote
favorite
up vote
7
down vote
favorite
I built this small library to create a compile-time assembled array from several hard-coded sequences (of unequal length) of a type T
:
mergeable_set.hpp
#pragma once
template <typename T>
class mergeable
public:
template <T... Ts>
struct set
static constexpr T values Ts...;
constexpr const auto& operator(const int i) const noexcept return values[i];
constexpr auto size() const noexcept return sizeof...(Ts);
class iterator
public:
constexpr explicit iterator(set& _ref, const int i = 0) : ref_ref, indexi
constexpr iterator& operator++() noexcept ++index; return *this;
constexpr bool operator!=(const iterator& other) const noexcept return index != other.index;
constexpr auto& operator* () noexcept return ref[index];
private:
set& ref;
int index;
;
class citerator
public:
constexpr citerator(const set& _ref, const int i) : ref_ref, indexi
constexpr citerator& operator++() noexcept ++index; return *this;
constexpr bool operator!=(const citerator& other) const noexcept return index != other.index;
constexpr const auto& operator* () const noexcept return ref[index];
private:
const set& ref;
int index;
;
constexpr iterator begin() noexcept return iterator*this, 0;
constexpr citerator begin() const noexcept return citerator*this, 0;
constexpr iterator end() noexcept return iterator*this, sizeof...(Ts);
constexpr citerator end() const noexcept return citerator*this, sizeof...(Ts);
;
private:
template <typename, typename...> struct concat;
template<T... A, T... B>
struct concat<set<A...>, set<B...>>
using type = set<A..., B...>;
;
template<T... A>
struct concat<set<A...>>
using type = set<A...>;
;
template <typename...> struct _combine ;
template <typename A, typename B, typename... C>
struct _combine<A, B, C...>
using type = typename concat<A, typename _combine<B, C...>::type>::type;
;
template <typename A, typename B>
struct _combine<A, B>
using type = typename concat<A, B>::type;
;
template <typename A>
struct _combine<A>
using type = typename concat<A>::type;
;
public:
template <typename...U>
using combine = typename _combine<U...>::type;
;
Usable like this:
#include "mergeable_set.hpp"
int main()
mergeable<int>::set<1, 2, 3> a;
mergeable<int>::set<4, 5, 6> b;
mergeable<int>::set<1, 2, 3> c;
mergeable<int>::set<4, 5, 6> d;
mergeable<int>::combine<decltype(a),
decltype(b),
decltype(c),
decltype(d)> z;
int temp = 0;
for (auto e : z)
temp += e;
volatile int l = temp;
With z
behaving like a plain array (const int z[12]
).
Interestingly this exposes some strange behaviour in GCC. When compiling x86
code any array over 7 elements generates SIMD bulk add instructions. While this sounds great, disabling SSE actually compiles out all instructions, straight up to the final result. The latter is obviously preferable. Did I just hit a logic bug in GCC here?
A live example of this effect: https://godbolt.org/g/4LAJXV
Remove -mno-sse
to see the difference.
I'd love to hear what can be improved to make this into a more general purpose library.
c++ c++14 template-meta-programming
I built this small library to create a compile-time assembled array from several hard-coded sequences (of unequal length) of a type T
:
mergeable_set.hpp
#pragma once
template <typename T>
class mergeable
public:
template <T... Ts>
struct set
static constexpr T values Ts...;
constexpr const auto& operator(const int i) const noexcept return values[i];
constexpr auto size() const noexcept return sizeof...(Ts);
class iterator
public:
constexpr explicit iterator(set& _ref, const int i = 0) : ref_ref, indexi
constexpr iterator& operator++() noexcept ++index; return *this;
constexpr bool operator!=(const iterator& other) const noexcept return index != other.index;
constexpr auto& operator* () noexcept return ref[index];
private:
set& ref;
int index;
;
class citerator
public:
constexpr citerator(const set& _ref, const int i) : ref_ref, indexi
constexpr citerator& operator++() noexcept ++index; return *this;
constexpr bool operator!=(const citerator& other) const noexcept return index != other.index;
constexpr const auto& operator* () const noexcept return ref[index];
private:
const set& ref;
int index;
;
constexpr iterator begin() noexcept return iterator*this, 0;
constexpr citerator begin() const noexcept return citerator*this, 0;
constexpr iterator end() noexcept return iterator*this, sizeof...(Ts);
constexpr citerator end() const noexcept return citerator*this, sizeof...(Ts);
;
private:
template <typename, typename...> struct concat;
template<T... A, T... B>
struct concat<set<A...>, set<B...>>
using type = set<A..., B...>;
;
template<T... A>
struct concat<set<A...>>
using type = set<A...>;
;
template <typename...> struct _combine ;
template <typename A, typename B, typename... C>
struct _combine<A, B, C...>
using type = typename concat<A, typename _combine<B, C...>::type>::type;
;
template <typename A, typename B>
struct _combine<A, B>
using type = typename concat<A, B>::type;
;
template <typename A>
struct _combine<A>
using type = typename concat<A>::type;
;
public:
template <typename...U>
using combine = typename _combine<U...>::type;
;
Usable like this:
#include "mergeable_set.hpp"
int main()
mergeable<int>::set<1, 2, 3> a;
mergeable<int>::set<4, 5, 6> b;
mergeable<int>::set<1, 2, 3> c;
mergeable<int>::set<4, 5, 6> d;
mergeable<int>::combine<decltype(a),
decltype(b),
decltype(c),
decltype(d)> z;
int temp = 0;
for (auto e : z)
temp += e;
volatile int l = temp;
With z
behaving like a plain array (const int z[12]
).
Interestingly this exposes some strange behaviour in GCC. When compiling x86
code any array over 7 elements generates SIMD bulk add instructions. While this sounds great, disabling SSE actually compiles out all instructions, straight up to the final result. The latter is obviously preferable. Did I just hit a logic bug in GCC here?
A live example of this effect: https://godbolt.org/g/4LAJXV
Remove -mno-sse
to see the difference.
I'd love to hear what can be improved to make this into a more general purpose library.
c++ c++14 template-meta-programming
asked Mar 2 at 10:35
Stefan
3891212
3891212
4
I'm talking from the ignorance but, would it be possible to combineconstexpr
with initializer lists so that you can achieve the same thing without having so many types?
â Jorge Bellón
Mar 2 at 11:17
3
Since I cant edit my previous comment, I'll add it up here. I've tried myself and it is completely possible to do it with arrays. However, it's limited to C++17 onwards, due tooperator
not being constexpr until then. If you are interested you can check it out here: godbolt.org/g/qfnn54 (it seems not to have the SIMD problem you mention when using-O3
).
â Jorge Bellón
Mar 2 at 12:34
add a comment |Â
4
I'm talking from the ignorance but, would it be possible to combineconstexpr
with initializer lists so that you can achieve the same thing without having so many types?
â Jorge Bellón
Mar 2 at 11:17
3
Since I cant edit my previous comment, I'll add it up here. I've tried myself and it is completely possible to do it with arrays. However, it's limited to C++17 onwards, due tooperator
not being constexpr until then. If you are interested you can check it out here: godbolt.org/g/qfnn54 (it seems not to have the SIMD problem you mention when using-O3
).
â Jorge Bellón
Mar 2 at 12:34
4
4
I'm talking from the ignorance but, would it be possible to combine
constexpr
with initializer lists so that you can achieve the same thing without having so many types?â Jorge Bellón
Mar 2 at 11:17
I'm talking from the ignorance but, would it be possible to combine
constexpr
with initializer lists so that you can achieve the same thing without having so many types?â Jorge Bellón
Mar 2 at 11:17
3
3
Since I cant edit my previous comment, I'll add it up here. I've tried myself and it is completely possible to do it with arrays. However, it's limited to C++17 onwards, due to
operator
not being constexpr until then. If you are interested you can check it out here: godbolt.org/g/qfnn54 (it seems not to have the SIMD problem you mention when using -O3
).â Jorge Bellón
Mar 2 at 12:34
Since I cant edit my previous comment, I'll add it up here. I've tried myself and it is completely possible to do it with arrays. However, it's limited to C++17 onwards, due to
operator
not being constexpr until then. If you are interested you can check it out here: godbolt.org/g/qfnn54 (it seems not to have the SIMD problem you mention when using -O3
).â Jorge Bellón
Mar 2 at 12:34
add a comment |Â
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188666%2fc-library-to-compile-time-merge-sets-to-a-single-array%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
4
I'm talking from the ignorance but, would it be possible to combine
constexpr
with initializer lists so that you can achieve the same thing without having so many types?â Jorge Bellón
Mar 2 at 11:17
3
Since I cant edit my previous comment, I'll add it up here. I've tried myself and it is completely possible to do it with arrays. However, it's limited to C++17 onwards, due to
operator
not being constexpr until then. If you are interested you can check it out here: godbolt.org/g/qfnn54 (it seems not to have the SIMD problem you mention when using-O3
).â Jorge Bellón
Mar 2 at 12:34