I have a bunch of structs holding pre-sorted std::array
s of varying number of size_t
s. As a toy example, suppose we've got the following three structs:
struct F_A { static constexpr std::array<size_t, 4> bounds = { 0, 100, 200, 300 }; };
struct F_B { static constexpr std::array<size_t, 5> bounds = { 0, 125, 250, 300, 500 }; };
struct F_C { static constexpr std::array<size_t, 4> bounds = { 100, 250, 300, 301 }; };
The goal is to perform the equivalent of an N
-way std::set_union
at compilation time; e.g., given the above structs, I want to be able to write
constexpr auto bounds = merge_bounds<F_A,F_B,F_C>();
and end up with bounds
as a constexpr std::array<size_t, 8>
containing values 0, 100, 125, 200, 250, 300, 301, 500
.
It was pretty easy to make this work for merging the bounds
arrays from a pair of structs; however, I am at a bit of a loss regarding how best to generalize this to use variadic templates and parameter packs. To get the version with pairs working, I resort to first "simulating" a merge to determine how long the merged array will be before actually doing the merge, but this approach gets awfully hairy when combining with parameter packs. (I suspect even my code for pairs is far less elegant than it would be if I had a better handle on some of the relevant language features...)
Here's an MWE demonstrating my functioning code for pairs:
#include <cstdlib>
#include <iostream>
#include <array>
struct F_A { static constexpr std::array<size_t, 4> bounds = { 0, 100, 200, 300 }; };
struct F_B { static constexpr std::array<size_t, 5> bounds = { 0, 125, 250, 300, 500 }; };
struct F_C { static constexpr std::array<size_t, 4> bounds = { 100, 250, 300, 301 }; };
template <typename F0, typename F1>
inline static constexpr auto merged_size()
{
constexpr auto bnd0 = F0::bounds;
constexpr auto bnd1 = F1::bounds;
size_t i = 0, i0 = 0, i1 = 0;
while (i0 < bnd0.size() && i1 < bnd1.size())
{
if (bnd0[i0] < bnd1[i1]) { i++; i0++; }
else if (bnd0[i0] > bnd1[i1]) { i++; i1++; }
else { i++; i0++; i1++; }
}
while (i0 < bnd0.size()) { i++; i0++; }
while (i1 < bnd1.size()) { i++; i1++; }
return i;
}
template <typename F0, typename F1, size_t N = merged_size<F0,F1>()>
inline static constexpr auto merge_bounds()
{
std::array<size_t, N> merged = { 0 };
constexpr auto bnd0 = F0::bounds;
constexpr auto bnd1 = F1::bounds;
size_t i = 0, i0 = 0, i1 = 0;
while (i0 < bnd0.size() && i1 < bnd1.size())
{
if (bnd0[i0] < bnd1[i1]) { merged[i++] = bnd0[i0++]; }
else if (bnd0[i0] > bnd1[i1]) { merged[i++] = bnd1[i1++]; }
else { merged[i++] = bnd0[i0++]; i1++; }
}
while (i0 < bnd0.size()) { merged[i++] = bnd0[i0++]; }
while (i1 < bnd1.size()) { merged[i++] = bnd1[i1++]; }
return std::move(merged);
}
int main(int argc, char * argv[])
{
std::cout << merged_size<F_A,F_B>() << "," << merged_size<F_B,F_C>() << "," << merged_size<F_A,F_C>() << std::endl;
for (auto i : merge_bounds<F_A,F_B>()) std::cout << i << " ";
std::cout <<"\n";
for (auto i : merge_bounds<F_B,F_C>()) std::cout << i << " ";
std::cout <<"\n";
for (auto i : merge_bounds<F_A,F_C>()) std::cout << i << " ";
std::cout <<"\n";
return 0;
}
How can I generalize merge_bounds
to allow an arbitrary number of such structs to be specified as template parameters?