C++ library to compile-time merge sets to a single array

The name of the pictureThe name of the pictureThe name of the pictureClash 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.







share|improve this question















  • 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

















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.







share|improve this question















  • 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













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.







share|improve this question











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.









share|improve this question










share|improve this question




share|improve this question









asked Mar 2 at 10:35









Stefan

3891212




3891212







  • 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













  • 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








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
















active

oldest

votes











Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















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



































active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes










 

draft saved


draft discarded


























 


draft saved


draft discarded














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













































































Popular posts from this blog

Greedy Best First Search implementation in Rust

Function to Return a JSON Like Objects Using VBA Collections and Arrays

C++11 CLH Lock Implementation