I have written a class multi_array which is sort of an extension of std::array to multiple dimensions.
template <typename T, std::size_t... N>
class multi_array {
template <std::size_t... I, typename... Idx>
constexpr std::size_t linearized_index(meta::index_sequence<I...>,
Idx... idx) const {
std::size_t index = 0;
using unpack = std::size_t[];
(void)unpack{0UL,
((void)(index = (index + unpack{std::size_t(idx)...}[I]) *
meta::pack_element<I + 1, N...>::value),
0UL)...};
return index + unpack{std::size_t(idx)...}[sizeof...(idx) - 1];
}
// Storage
T m_data[meta::product<N...>::value];
//...
};
I have managed to get constexpr element access but only in C++14. The problem is the function linearized_index. It computes the linearized index at compile-time. In order to do so it reduces the tuple of indices and the tuple of dimension in a certain manner. For this reduction I need a local variable inside the function but this is not allowed in C++11. My environment does not permit the usage of C++14. Can I somehow rewrite this function to work with C++11?
I have prepared a full (not so minimal) example which compiles in C++14.
#include <cstddef> // std::size_t
namespace meta {
// product
template <std::size_t...>
struct product;
template <std::size_t head, std::size_t... dim>
struct product<head, dim...> {
static constexpr std::size_t const value = head * product<dim...>::value;
};
template <>
struct product<> {
static constexpr std::size_t const value = 1;
};
// pack_element
template <std::size_t index, std::size_t head, std::size_t... pack>
struct pack_element {
static_assert(index < sizeof...(pack) + 1, "index out of bounds");
static constexpr std::size_t const value =
pack_element<index - 1, pack...>::value;
};
template <std::size_t head, std::size_t... pack>
struct pack_element<0, head, pack...> {
static constexpr std::size_t const value = head;
};
// index_sequence
// https://stackoverflow.com/a/24481400
template <std::size_t... I>
struct index_sequence {};
template <std::size_t N, std::size_t... I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};
template <std::size_t... I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};
} // namespace meta
template <typename T, std::size_t... N>
class multi_array {
template <std::size_t... I, typename... Idx>
constexpr std::size_t linearized_index(meta::index_sequence<I...>,
Idx... idx) const {
std::size_t index = 0;
using unpack = std::size_t[];
(void)unpack{0UL,
((void)(index = (index + unpack{std::size_t(idx)...}[I]) *
meta::pack_element<I + 1, N...>::value),
0UL)...};
return index + unpack{std::size_t(idx)...}[sizeof...(idx) - 1];
}
// Storage
T m_data[meta::product<N...>::value];
public:
constexpr multi_array() {}
template <typename... U>
constexpr multi_array(U... data) : m_data{T(data)...} {}
template <typename... Idx>
constexpr T operator()(Idx... idx) const noexcept {
std::size_t index = linearized_index(
meta::make_index_sequence<sizeof...(idx) - 1>{}, idx...);
return m_data[index];
}
};
int main() {
constexpr multi_array<double, 2, 2> const b = {0, 0, 0, 1};
static_assert(b(1, 1) == 1, "!");
}
Live on Wandbox (C++14) and
Live on Wandbox (C++11)
The crucial part of your use of index is an iterative loop:
index = (index*a) + b
In your own C++14 solution, a trick of unpacking parameter pack is used. In C++11, you can formulate it in a recursive constexpr function:
struct mypair {
size_t a;
size_t b;
};
constexpr std::size_t foo(std::size_t init) {
return init;
}
template<class... Pair>
constexpr std::size_t foo(std::size_t init, mypair p0, Pair... ps) {
return foo((init+p0.a)*p0.b, ps...);
}
We use mypair instead of std::pair because the constructor of std::pair in C++11 is not constexpr. Then your iterative loop can be literally translated to:
template <std::size_t... I, typename... Idx>
constexpr std::size_t linearized_index(meta::index_sequence<I...>,
Idx... idx) const {
using unpack = std::size_t[];
return foo(0, mypair{unpack{std::size_t(idx)...}[I], meta::pack_element<I+1, N...>::value}...) + unpack{std::size_t(idx)...}[sizeof...(idx) - 1];
}
Live Demo
If in operator(), instead of calling
std::size_t index = linearized_index(
meta::make_index_sequence<sizeof...(idx) - 1>{}, idx...);
you call
std::size_t index = linearized_index<N...>(idx...);
or better (to make operator() constexpr)
return m_data[linearized_index<N...>(idx...)];
you can rewrite linearized_index() recursively as follows
// ground case
template <std::size_t>
constexpr std::size_t linearized_index (std::size_t idx0) const
{ return idx0; }
// recursive case
template <std::size_t, std::size_t... Is, typename... Idx>
constexpr std::size_t linearized_index (std::size_t idx0,
Idx ... idxs) const
{ return idx0 * meta::product<Is...>::value
+ linearized_index<Is...>(idxs...); }
If you prefer, the ground case can be written as follows
template <typename = void>
constexpr std::size_t linearized_index () const
{ return 0; }
Observe that you don't need meta::index_sequence, meta::make_index_sequence or meta::pack_element anymore.
The following is a full C++11 compiling example
#include <cstddef> // std::size_t
namespace meta
{
template <std::size_t...>
struct product;
template <std::size_t head, std::size_t... dim>
struct product<head, dim...>
{ static constexpr std::size_t const value
= head * product<dim...>::value; };
template <>
struct product<>
{ static constexpr std::size_t const value = 1; };
} // namespace meta
template <typename T, std::size_t... N>
class multi_array
{
private:
// ground case
template <std::size_t>
constexpr std::size_t linearized_index (std::size_t idx0) const
{ return idx0; }
// alternative ground case
//template <typename = void>
//constexpr std::size_t linearized_index () const
// { return 0; }
// recursive case
template <std::size_t, std::size_t... Is, typename... Idx>
constexpr std::size_t linearized_index (std::size_t idx0,
Idx ... idxs) const
{ return idx0 * meta::product<Is...>::value
+ linearized_index<Is...>(idxs...); }
// Storage
T m_data[meta::product<N...>::value];
public:
constexpr multi_array()
{ }
template <typename ... U>
constexpr multi_array(U ... data) : m_data{T(data)...}
{ }
template <typename... Idx>
constexpr T operator() (Idx... idx) const noexcept
{ return m_data[linearized_index<N...>(idx...)]; }
};
int main()
{
constexpr multi_array<double, 2, 2> const b = {0, 0, 0, 1};
static_assert( b(1, 1) == 1, "!" );
constexpr multi_array<double, 4, 3, 2> const c
{ 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 1};
static_assert( c(3, 2, 1) == 1, "!" );
static_assert( c(2, 1, 0) == 2, "!" );
}
Bonus suggestion: if you add the following constexpr functions (static methods inside multi_array?)
constexpr static std::size_t prod ()
{ return 1U; }
template <typename ... Args>
constexpr static std::size_t prod (std::size_t v, Args ... vs)
{ return v * prod(vs...); }
you can delete the struct product calling
// Storage
T m_data[prod(N...)];
and
// recursive case
template <std::size_t, std::size_t... Is, typename... Idx>
constexpr std::size_t linearized_index (std::size_t idx0,
Idx ... idxs) const
{ return idx0 * prod(Is...) + linearized_index<Is...>(idxs...); }
Straightforward approach avoiding arrays:
#include <tuple>
#include <type_traits>
#include <cstddef>
template
<
typename x_IndexTypesTuple
, ::std::size_t ... x_dimension
> class
t_MultiIndexImpl;
template
<
typename x_LeadingIndex
, typename ... x_Index
, ::std::size_t x_leadding_dimension
, ::std::size_t ... x_dimension
> class
t_MultiIndexImpl
<
::std::tuple<x_LeadingIndex, x_Index ...>, x_leadding_dimension, x_dimension ...
> final
{
static_assert
(
::std::is_same<::std::size_t, x_LeadingIndex>::value
, "index type must be ::std::size_t"
);
public: static constexpr auto
Op
(
::std::size_t const stride_size
, x_LeadingIndex const leading_index
, x_Index const ... index
) -> ::std::size_t
{
return stride_size * leading_index
+ t_MultiIndexImpl<::std::tuple<x_Index ...>, x_dimension ...>::Op
(
stride_size * x_leadding_dimension, index ...
);
}
};
template<> class
t_MultiIndexImpl<::std::tuple<>> final
{
public: static constexpr auto
Op(::std::size_t const /*stride_size*/) -> ::std::size_t
{
return ::std::size_t{0};
}
};
template<::std::size_t ... x_dimension, typename ... x_Index> inline constexpr auto
Caclculate_MultiIndex(x_Index const ... index) -> ::std::size_t
{
static_assert
(
sizeof...(x_dimension) == sizeof...(x_Index)
, "arguments count must match dimensions count"
);
return t_MultiIndexImpl<::std::tuple<x_Index ...>, x_dimension ...>::Op(::std::size_t{1}, index ...);
}
online compiler |
godbolt
I have managed to get a C++11 compatible solution by rewriting the function to evaluate recursively. This not only works but is also a lot nicer to read:
template <typename... Idx>
constexpr std::size_t linearized_index(std::size_t n, Idx... idx) const {
using unpack = std::size_t[];
return unpack{std::size_t(idx)...}[n] +
(n == 0 ? 0
: unpack{std::size_t(N)...}[n] *
linearized_index(n - 1, idx...));
}
I have found another solution using template specializations which avoids the recursive function call and replaces it with recursive instantiation.
namespace meta {
template <size_t n, size_t... N>
struct linearized_index {
template <typename... Idx>
constexpr std::size_t operator()(Idx... idx) const {
using unpack = std::size_t[];
return unpack{std::size_t(idx)...}[n] +
unpack{std::size_t(N)...}[n] *
linearized_index<n - 1, N...>{}(idx...);
}
};
template <size_t... N>
struct linearized_index<0, N...> {
template <typename... Idx>
constexpr std::size_t operator()(Idx... idx) const {
using unpack = std::size_t[];
return unpack{std::size_t(idx)...}[0];
}
};
} // namespace meta
and the multi_array call operator
template <typename... Idx>
constexpr T operator()(Idx... idx) const noexcept {
return m_data[meta::linearized_index<sizeof...(idx) - 1, N...>{}(
idx...)];
}
This produces perfect assembly: https://godbolt.org/g/8LPkBZ
Related
I am struggling with an implementation of the Cartesian product for
multiple indices with a given range 0,...,n-1.
The basic idea is to have a function:
cartesian_product<std::size_t range, std::size_t sets>()
with an output array that contains tuples that hold the different products
[(0,..,0), (0,...,1), (0,...,n-1),...., (n-1, ..., n-1)]
An simple example would be the following:
auto result = cartesian_product<3, 2>();
with the output type std::array<std::tuple<int, int>, (3^2)>:
[(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)]
My main problem is that my version of the Cartesian product is slow and creates a stack overflow if you choose to have more than 5 sets. I believe that my code has too many recursions and temporary variables.
My implementation (C++17) can be found here: cartesian_product
#include <stdio.h>
#include <iostream>
#include <tuple>
template<typename T, std::size_t ...is>
constexpr auto flatten_tuple_i(T tuple, std::index_sequence<is...>) {
return std::tuple_cat(std::get<is>(tuple)...);
}
template<typename T>
constexpr auto flatten_tuple(T tuple) {
return flatten_tuple_i(tuple, std::make_index_sequence<std::tuple_size<T>::value>{});
}
template<std::size_t depth, typename T>
constexpr auto recursive_flatten_tuple(T tuple){
if constexpr(depth <= 1){
return tuple;
}else{
return recursive_flatten_tuple<depth-1>(flatten_tuple(tuple));
}
}
template<std::size_t depth, typename T, std::size_t ...is>
constexpr auto wdh(T&& tuple, std::index_sequence<is...>){
if constexpr (depth == 0) {
return tuple;
}else{
//return (wdh<depth-1>(std::tuple_cat(tuple, std::make_tuple(is)),std::make_index_sequence<sizeof...(is)>{})...);
return std::make_tuple(wdh<depth-1>(std::tuple_cat(tuple, std::make_tuple(is)), std::make_index_sequence<sizeof...(is)>{})...);
}
}
template<std::size_t sets, typename T, std::size_t ...is>
constexpr auto to_array(T tuple, std::index_sequence<is...>){
if constexpr (sets == 0){
auto t = (std::make_tuple(std::get<is>(tuple)),...);
std::array<decltype(t), sizeof...(is)> arr = {std::make_tuple(std::get<is>(tuple))...};
//decltype(arr)::foo = 1;
return arr;
}else{
auto t = ((std::get<is>(tuple)),...);
std::array<decltype(t), sizeof...(is)> arr = {std::get<is>(tuple)...};
return arr;
}
}
template<std::size_t sets, std::size_t ...is>
constexpr auto ct_i(std::index_sequence<is...>){
if constexpr (sets == 0){
auto u = std::tuple_cat(wdh<sets>(std::make_tuple(is), std::make_index_sequence<sizeof...(is)>{})...);
auto arr = to_array<sets>(u, std::make_index_sequence<std::tuple_size<decltype(u)>::value>{});
return arr;
}else {
auto u = std::tuple_cat(wdh<sets>(std::make_tuple(is), std::make_index_sequence<sizeof...(is)>{})...);
auto r = recursive_flatten_tuple<sets>(u);
auto d = to_array<sets>(r, std::make_index_sequence<std::tuple_size<decltype(r)>::value>{});
return d;
}
}
template<std::size_t range, std::size_t sets>
constexpr auto cartesian_product(){
static_assert( (range > 0), "lowest input must be cartesian<1,1>" );
static_assert( (sets > 0), "lowest input must be cartesian<1,1>" );
return ct_i<sets-1>(std::make_index_sequence<range>{});
}
int main()
{
constexpr auto crt = cartesian_product<3, 2>();
for(auto&& ele : crt){
std::cout << std::get<0>(ele) << " " << std::get<1>(ele) << std::endl;
}
return 0;
}
Since I was also working on a solution I thought I post it aswell (although very similar to Artyer's answer). Same premise, we replace the tuple with an array and just iterate over the elements, incrementing them one by one.
Note that the power function is broken, so if you need power values <0 or non-integer types you have to fix it.
template <typename It, typename T>
constexpr void setAll(It begin, It end, T value)
{
for (; begin != end; ++begin)
*begin = value;
}
template <typename T, std::size_t I>
constexpr void increment(std::array<T, I>& counter, T max)
{
for (auto idx = I; idx > 0;)
{
--idx;
if (++counter[idx] >= max)
{
setAll(counter.begin() + idx, counter.end(), 0); // because std::fill is not constexpr yet
}
else
{
break;
}
}
}
// std::pow is not constexpr
constexpr auto power = [](auto base, auto power)
{
auto result = base;
while (--power)
result *= base;
return result;
};
template<std::size_t range, std::size_t sets>
constexpr auto cartesian_product()
{
std::array<std::array<int, sets>, power(range, sets)> products{};
std::array<int, sets> currentSet{};
for (auto& product : products)
{
product = currentSet;
increment(currentSet, static_cast<int>(range));
}
return products;
}
int main()
{
constexpr auto crt = cartesian_product<5, 3>();
for (auto&& ele : crt)
{
for (auto val : ele)
std::cout << val << " ";
std::cout << "\n";
}
return 0;
}
Example
With Boost.Mp11, this is... alright, it's not a one-liner, but it's still not so bad:
template <typename... Lists>
using list_product = mp_product<mp_list, Lists...>;
template <typename... Ts>
constexpr auto list_to_tuple(mp_list<Ts...>) {
return std::make_tuple(int(Ts::value)...);
}
template <typename... Ls>
constexpr auto list_to_array(mp_list<Ls...>) {
return std::array{list_to_tuple(Ls{})...};
}
template <size_t R, size_t N>
constexpr auto cartesian_product()
{
using L = mp_repeat_c<mp_list<mp_iota_c<R>>, N>;
return list_to_array(mp_apply<list_product, L>{});
}
With C++20, you can declare the two helper function templates as lambdas inside of cartesian_product, which makes this read nicer (top to bottom instead of bottom to top).
Explanation of what's going on, based on the OP example of cartesian_product<3, 2>:
mp_iota_c<R> gives us the list [0, 1, 2] (but as integral constant types)
mp_repeat_c<mp_list<mp_iota_c<R>>, N> gives us [[0, 1, 2], [0, 1, 2]]. We just repeat the list, but we want a list of lists (hence the extra mp_list in the middle).
mp_apply<list_product, L> does mp_product, which is a cartesian product of all the lists you pass in... sticking the result in an mp_list. This gives you [[0, 0], [0, 1], [0, 2], ..., [2, 2]], but as an mp_list of mp_list of integral constants.
At this point the hard part is over, we just have to convert the result back to an array of tuples. list_to_tuple takes an mp_list of integral constants and turns that into a tuple<int...> with the right values. And list_to_array takes an mp_list of mp_lists of integral constants and turns that into an std::array of tuples.
A slightly different approach using just the single helper function:
template <template <typename...> class L,
typename... Ts, typename F>
constexpr auto unpack(L<Ts...>, F f) {
return f(Ts{}...);
}
template <size_t R, size_t N>
constexpr auto cartesian_product()
{
using P = mp_apply_q<
mp_bind_front<mp_product_q, mp_quote<mp_list>>,
mp_repeat_c<mp_list<mp_iota_c<R>>, N>>;
return unpack(P{},
[](auto... lists){
return std::array{
unpack(lists, [](auto... values){
return std::make_tuple(int(values)...);
})...
};
});
}
This approach is harder to read though, but it's the same algorithm.
You can do this without recursion easily. Notice that each tuple is the digits of numbers from 0 to range ** sets in base range, so you could increment a counter (Or apply to a std::index_sequence) and calculate each value one after the other.
Here's an implementation (That returns a std::array of std::arrays, which works mostly the same as std::tuples as you can get<N>, tuple_size and tuple_element<N> on a std::array, though if you really wanted you can convert them to std::tuples):
#include <cstddef>
#include <array>
namespace detail {
constexpr std::size_t ipow(std::size_t base, std::size_t exponent) noexcept {
std::size_t p = 1;
while (exponent) {
if (exponent % 2 != 0) {
p *= base;
}
exponent /= 2;
base *= base;
}
return p;
}
}
template<std::size_t range, std::size_t sets>
constexpr std::array<std::array<std::size_t, sets>, detail::ipow(range, sets)>
cartesian_product() noexcept {
constexpr std::size_t size = detail::ipow(range, sets);
std::array<std::array<std::size_t, sets>, size> result{};
for (std::size_t i = 0; i < size; ++i) {
std::size_t place = size;
for (std::size_t j = 0; j < sets; ++j) {
place /= range;
result[i][j] = (i / place) % range;
}
}
return result;
}
Here's a test link: https://www.onlinegdb.com/By_X9wbrI
Note that (empty_set)^0 is defined as a set containing an empty set here, but that can be changed by making ipow(0, 0) == 0 instead of 1
I was trying it out just for fun and I ended with pretty much the same idea as #Timo, just with a different format/style.
#include <iostream>
#include <array>
using namespace std;
template<size_t range, size_t sets>
constexpr auto cartesian_product() {
// how many elements = range^sets
constexpr auto size = []() {
size_t x = range;
size_t n = sets;
while(--n != 0) x *= range;
return x;
}();
auto products = array<array<size_t, sets>, size>();
auto counter = array<size_t, sets>{}; // array of zeroes
for (auto &product : products) {
product = counter;
// counter increment and wrapping/carry over
counter.back()++;
for (size_t i = counter.size()-1; i != 0; i--) {
if (counter[i] == range) {
counter[i] = 0;
counter[i-1]++;
}
else break;
}
}
return products;
}
int main() {
auto prods = cartesian_product<3, 6>();
}
I basically have a counter array which I increment manually, like so:
// given cartesian_product<3, 4>
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 1, 0] // carry over
...
...
[2, 2, 2, 2]
Pretty much just how you would do it by hand.
Example
If you want it in compile-time, you should only employ compile-time evaluations over compile-time data structures. As #Barry pointed above, using Boost.Mp11 greatly facilitates it. Of course you can do reimplement the relevant fundamental functions in plain C++17 on your own:
#include <iostream>
template<class T> struct Box {
using type = T;
};
template<class... Types> struct List {};
template<class Car, class Cdr> struct Cons;
template<class Car, class Cdr> using ConsT = typename Cons<Car, Cdr>::type;
template<class Car, class... Cdr> struct Cons<Car, List<Cdr...>>: Box<List<Car, Cdr...>> {};
using Nil = List<>;
template<std::size_t i, class L> struct Nth;
template<std::size_t i, class L> using NthT = typename Nth<i, L>::type;
template<std::size_t i, class... Ts> struct Nth<i, List<Ts...>>: std::tuple_element<i, std::tuple<Ts...>> {};
template<class L> struct Head;
template<class L> using HeadT = typename Head<L>::type;
template<class Car, class... Cdr> struct Head<List<Car, Cdr...>>: Box<Car> {};
template<class L> struct Tail;
template<class L> using TailT = typename Tail<L>::type;
template<class Car, class... Cdr> struct Tail<List<Car, Cdr...>>: Box<List<Cdr...>> {};
template<class... Lists> struct Concat;
template<class... Lists> using ConcatT = typename Concat<Lists...>::type;
template<class T, class... Rest> struct Concat<T, Rest...>: Cons<T, ConcatT<Rest...>> {};
template<class Head, class... Tail, class... Rest> struct Concat<List<Head, Tail...>, Rest...>: Cons<Head, ConcatT<List<Tail...>, Rest...>> {};
template<class... Rest> struct Concat<Nil, Rest...>: Concat<Rest...> {};
template<> struct Concat<>: Box<Nil> {};
template<class T, class Subspace> struct Prepend;
template<class T, class Subspace> using PrependT = typename Prepend<T, Subspace>::type;
template<class T, class... Points> struct Prepend<T, List<Points...>>: Box<List<ConsT<T, Points>...>> {};
template<class T> struct Prepend<T, Nil>: Box<List<List<T>>> {};
template<class Range, class Subspace> struct Product;
template<class Range, class Subspace> using ProductT = typename Product<Range, Subspace>::type;
template<class Range, class Subspace> struct Product: Concat<PrependT<HeadT<Range>, Subspace>, ProductT<TailT<Range>, Subspace>> {};
template<class Subspace> struct Product<Nil, Subspace>: Box<Nil> {};
template<std::size_t i> using IntValue = std::integral_constant<std::size_t, i>;
template<class Seq> struct IntegerSequence;
template<class Seq> using IntegerSequenceT = typename IntegerSequence<Seq>::type;
template<std::size_t... is> struct IntegerSequence<std::index_sequence<is...>>: Box<List<IntValue<is>...>> {};
template<std::size_t n> using Range = IntegerSequenceT<std::make_index_sequence<n>>;
template<std::size_t dimensions, std::size_t range> struct CartesianCube;
template<std::size_t dimensions, std::size_t range> using CartesianCubeT = typename CartesianCube<dimensions, range>::type;
template<std::size_t dimensions, std::size_t range> struct CartesianCube: Product<Range<range>, CartesianCubeT<dimensions - 1, range>> {};
template<std::size_t range> struct CartesianCube<0, range>: Box<Nil> {};
template<std::size_t i> std::ostream &operator<<(std::ostream &s, IntValue<i>) {
return s << '<' << i << '>';
}
template<class... Ts> std::ostream &operator<<(std::ostream &s, List<Ts...>);
namespace detail_ {
template<class L, std::size_t... is> std::ostream &printList(std::ostream &s, L, std::index_sequence<is...>) {
return ((s << (is == 0? "" : ", ") << NthT<is, L>{}), ...), s;
}
}
template<class... Ts> std::ostream &operator<<(std::ostream &s, List<Ts...>) {
return detail_::printList(s << "List{", List<Ts...>{}, std::index_sequence_for<Ts...>{}) << '}';
}
int main() {
std::cout << CartesianCubeT<2, 3>{} << '\n';
}
Note that CartesianCubeT here is actually a List of Lists of integral_constants. Once you have those, converting them into run-time values is trivial. Note that cartesian_product does not even have to be a function, since the whole data set is evaluated at compile-time it can be a templated value.
Given this type
template<std::size_t N, int ...content>
struct list {
inline int reduce() {
int result = 0;
constexpr int arr[N] = { content... };
for(std::size_t k = 0; k < N; ++k) {
result += arr[k];
}
return result;
}
};
I'd like to implement a function add, which returns a new list containing the element-by-element addition of the two input lists.
In other words (pseudo-code):
add([a0, a1, a2], [b0, b1]) -> [a0 + b0, a1 + b2, a2]
Problem:
I don't even know how to declare the return type of such a function
and i don't know if it's possible
Here is how I would do it:
#include <iostream>
#include <utility>
template<std::size_t N, int ...content>
struct list {
inline int reduce() {
int result = 0;
constexpr int arr[N] = { content... };
for(std::size_t k = 0; k < N; ++k) {
result += arr[k];
}
return result;
}
};
template <std::size_t I, int ...A>
constexpr int list_at(list<sizeof...(A),A...>)
{
if constexpr (I < sizeof...(A))
{
constexpr int arr[] {A...};
return arr[I];
}
else
{
return 0;
}
}
template <int ...A, int ...B, std::size_t ...I>
constexpr auto list_sum_low(list<sizeof...(A),A...>,
list<sizeof...(B),B...>,
std::index_sequence<I...>)
{
return list<sizeof...(I), (list_at<I>(list<sizeof...(A),A...>{}) +
list_at<I>(list<sizeof...(B),B...>{}))...>{};
}
template <int ...A, int ...B>
constexpr auto list_sum(list<sizeof...(A),A...>, list<sizeof...(B),B...>)
{
constexpr int a = sizeof...(A), b = sizeof...(B);
return list_sum_low(list<a,A...>{}, list<b,B...>{},
std::make_index_sequence<(a > b ? a : b)>{});
}
template <int ...A>
void print_list(list<sizeof...(A),A...>)
{
(void(std::cout << ' ' << A) , ...);
}
int main()
{
constexpr auto x = list_sum(list<4, 1,2,3,4>{}, list<2, 10,20>{});
print_list(x);
}
Also, note that there is no need have size_t N template parameter for class list. Parameter packs know their own size.
Just another solution, based on good old partial specialization:
template <size_t N, int... E> struct list { };
template <typename, typename> struct list_cat;
template <size_t N1, int... E1, size_t N2, int... E2>
struct list_cat<list<N1, E1...>, list<N2, E2...>>
{ using type = list<N1 + N2, E1..., E2...>; };
template <typename, typename> struct list_add;
template <size_t N1, int E1H, int... E1T, size_t N2, int E2H, int... E2T>
struct list_add<list<N1, E1H, E1T...>, list<N2, E2H, E2T...>>
{
using type = typename list_cat<
list<1, E1H + E2H>,
typename list_add<list<N1 - 1, E1T...>, list<N2 - 1, E2T...>>::type
>::type;
};
template <size_t N2, int... E2>
struct list_add<list<0>, list<N2, E2...>> { using type = list<N2, E2...>; };
template <size_t N1, int... E1>
struct list_add<list<N1, E1...>, list<0>> { using type = list<N1, E1...>; };
template <>
struct list_add<list<0>, list<0>> { using type = list<0>; }
Which can be used as:
using L1 = list<3, -1, -2, -3>;
using L2 = list <2, 10, 20>;
using L = typename list_add<L1, L2>::type;
Live demo: https://wandbox.org/permlink/x8LYcoC3lWu51Gqo
Another solution std::integer_sequence based.
When the dimension of the two lists is the same, an add() function is trivially simple
template <std::size_t N, int ... Is1, int ... Is2>
constexpr auto add (list<N, Is1...>, list<N, Is2...>)
{ return list<N, Is1+Is2...>{}; }
The problem is when we have lists of different length.
A possible solution is extend the shorter list with zeros and apply the preceding function to the length-uniformed lists.
Given a extender as follows
template <std::size_t N1, std::size_t N0, int ... Is, std::size_t ... Js>
constexpr auto listExtend (list<N0, Is...>, std::index_sequence<Js...>)
{ return list<N1, Is..., ((void)Js, 0)...>{}; }
template <std::size_t N1, std::size_t N0, int ... Is,
std::enable_if_t<(N1 > N0), bool> = true>
constexpr auto listExtend (list<N0, Is...> l)
{ return listExtend<N1>(l, std::make_index_sequence<N1-N0>{}); }
we need only to add the following add() functions
template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2,
std::enable_if_t<(N1 > N2), bool> = true>
constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2)
{ return add(l1, listExtend<N1>(l2)); }
template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2,
std::enable_if_t<(N1 < N2), bool> = true>
constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2)
{ return add(listExtend<N2>(l1), l2); }
The following is a full compiling C++14 (unfortunately std::make_index_sequence/std::index_sequence require C++14) example
#include <utility>
#include <type_traits>
template <std::size_t, int ...>
struct list
{ };
template <std::size_t N1, std::size_t N0, int ... Is, std::size_t ... Js>
constexpr auto listExtend (list<N0, Is...>, std::index_sequence<Js...>)
{ return list<N1, Is..., ((void)Js, 0)...>{}; }
template <std::size_t N1, std::size_t N0, int ... Is,
std::enable_if_t<(N1 > N0), bool> = true>
constexpr auto listExtend (list<N0, Is...> l)
{ return listExtend<N1>(l, std::make_index_sequence<N1-N0>{}); }
template <std::size_t N, int ... Is1, int ... Is2>
constexpr auto add (list<N, Is1...>, list<N, Is2...>)
{ return list<N, Is1+Is2...>{}; }
template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2,
std::enable_if_t<(N1 > N2), bool> = true>
constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2)
{ return add(l1, listExtend<N1>(l2)); }
template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2,
std::enable_if_t<(N1 < N2), bool> = true>
constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2)
{ return add(listExtend<N2>(l1), l2); }
int main ()
{
list<3u, 1, 2, 3> l1;
list<2u, 10, 20> l2;
auto l3 = add(l1, l2);
static_assert( std::is_same<decltype(l3), list<3u, 11, 22, 3>>::value,
"!" );
}
I will use std::integer_sequence:
template <int ... Is>
using int_sequence = std::integer_sequence<int, Is...>;
When size match, it would be easy, so create method to increase size and fill with zero:
template <int... Is, int... Zeros>
int_sequence<Is..., (0 * Zeros)...>
fill_with_zero(int_sequence<Is...>, int_sequence<Zeros...>) { return {}; }
template <std::size_t N, int... Is>
auto fill_with_zero_to_reach(int_sequence<Is...> seq)
-> decltype(fill_with_zero(seq,
std::make_integer_sequence<int,
(sizeof...(Is) < N
? N - sizeof...(Is)
: 0)>{}))
{
return {};
}
Then the addition:
// simple case, sizes match:
template <int... Is1, int... Is2,
std::enable_if_t<sizeof...(Is1) == sizeof...(Is2), int> = 0>
int_sequence<(Is1 + Is2)...>
add(int_sequence<Is1...>, int_sequence<Is2...>) { return {}; }
// sizes mismatch:
template <int... Is1, int... Is2,
std::enable_if_t<sizeof...(Is1) != sizeof...(Is2), int> = 0>
auto
add(int_sequence<Is1...> seq1, int_sequence<Is2...> seq2)
-> decltype(add(fill_with_zero_to_reach<std::max(sizeof...(Is1), sizeof...(Is2))>(seq1),
fill_with_zero_to_reach<std::max(sizeof...(Is1), sizeof...(Is2))>(seq2)))
{ return {}; }
Demo
I am currently creating arithmetic operators libraries for high level synthesis.
For this, I am also creating a library to manipulate bits and bit vectors like it would be done in VHDL. To make my libraries synthesizable, nearly everything must be resolved at compile time.
However, I have an issue with loops.
Indeed, I would like to be able to write things like that:
const int N = 5;
for(int i = 0; i < N-2; i++) {
x.bit<i+2>() = x.bit<i>();
}
Of course, it does not compile since i is a variable and not a constant determined at compile time.
However, N being a constant, this code is strictly equivalent to:
x.bit<2>() = x.bit<0>();
x.bit<3>() = x.bit<1>();
x.bit<4>() = x.bit<2>();
which compiles and works perfectly.
Is there a way to make the compiler (gcc in my case) unroll the loop since N is constant? Or to define a macro or a constexpr which could do it with a clean syntax? This would be the equivalent of for generate in VHDL.
While constexpr has got much more powerful in C++14/17 it is not yet possible to mix this kind of compile time / template code with an ordinary loop. There is some talk of introducing a construct that might enable that in a future version of C++. For now you have a few choices, either recursive calls to a function with an integer template argument or probably simpler in this case a C++17 fold expression. You could also use C++11 variadic template expansion to get a similar result to fold expressions in this example, though fold expressions are more powerful.
Just saw your comment about being stuck with C++11, you're probably better off using the recursive function approach I think. I've added that approach to the example.
If you were able to use C++14 you might also want to consider moving entirely into constexpr function / type land so your bit<I>() function would not be templated but would be just a constexpr function bit(i). You could then use normal functions and loops. Given the C++11 restrictions on constexpr functions that is probably less useful in your case however. I've added an example using that approach.
#include <iostream>
#include <utility>
template <size_t N>
struct bits {
bool bs[N];
template <size_t I>
constexpr const bool& bit() const {
return bs[I];
}
template <size_t I>
constexpr bool& bit() {
return bs[I];
}
constexpr bool bit(int i) const { return bs[i]; }
constexpr void bit(int i, bool x) { bs[i] = x; }
};
// Using C++17 fold expressions
template <size_t N, size_t... Is>
constexpr bits<N> set_bits_helper(bits<N> x, std::index_sequence<Is...>) {
((x.bit<Is + 2>() = x.bit<Is>()), ...);
return x;
}
template <size_t N>
constexpr bits<N> set_bits(bits<N> x) {
return set_bits_helper(x, std::make_index_sequence<N - 2>{});
}
// Using recursive template function, should work on C++11
template <size_t I, size_t N>
constexpr bits<N> set_bits_recursive_helper(bits<N> x, std::integral_constant<size_t, I>) {
x.bit<N - I>() = x.bit<N - I - 2>();
return set_bits_recursive_helper(x, std::integral_constant<size_t, I - 1>{});
}
template <size_t N>
constexpr bits<N> set_bits_recursive_helper(bits<N> x, std::integral_constant<size_t, 0>) { return x; }
template <size_t N>
constexpr bits<N> set_bits_recursive(bits<N> x) {
return set_bits_recursive_helper(x, std::integral_constant<size_t, N - 2>{});
}
// Using non template constexpr functions
template <size_t N>
constexpr bits<N> set_bits_constexpr(bits<N> x) {
for (int i = 0; i < N - 2; ++i) {
x.bit(i + 2, x.bit(i));
}
return x;
}
// Test code to show usage
template <size_t N>
void print_bits(const bits<N>& x) {
for (auto b : x.bs) {
std::cout << b << ", ";
}
std::cout << '\n';
}
void test_set_bits() {
constexpr bits<8> x{ 1, 0 };
print_bits(x);
constexpr auto y = set_bits(x);
static_assert(y.bit<2>() == x.bit<0>());
print_bits(y);
}
void test_set_bits_recursive() {
constexpr bits<8> x{ 1, 0 };
print_bits(x);
constexpr auto y = set_bits_recursive(x);
static_assert(y.bit<2>() == x.bit<0>());
print_bits(y);
}
void test_set_bits_constexpr() {
constexpr bits<8> x{ 1, 0 };
print_bits(x);
constexpr auto y = set_bits_constexpr(x);
static_assert(y.bit<2>() == x.bit<0>());
print_bits(y);
}
int main() {
test_set_bits();
test_set_bits_recursive();
test_set_bits_constexpr();
}
Also without std::integer_sequence (but I suggest to implement a substitute and use it), in C++11 you can use template partial specialization.
I mean that you can implement something like
template <int I, int Sh, int N>
struct shiftVal
{
template <typename T>
static int func (T & t)
{ return t.template bit<I+Sh>() = t.template bit<I>(),
shiftVal<I+1, Sh, N>::func(t); }
};
template <int I, int Sh>
struct shiftVal<I, Sh, I>
{
template <typename T>
static int func (T &)
{ return 0; }
};
and your cycle become
shiftVal<0, 2, N-2>::func(x);
The following is a full working example
#include <array>
#include <iostream>
template <std::size_t N>
struct foo
{
std::array<int, N> arr;
template <int I>
int & bit ()
{ return arr[I]; }
};
template <int I, int Sh, int N>
struct shiftVal
{
template <typename T>
static int func (T & t)
{ return t.template bit<I+Sh>() = t.template bit<I>(),
shiftVal<I+1, Sh, N>::func(t); }
};
template <int I, int Sh>
struct shiftVal<I, Sh, I>
{
template <typename T>
static int func (T &)
{ return 0; }
};
int main ()
{
foo<10U> f { { { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 } } };
for ( auto const & i : f.arr )
std::cout << i << ' ';
std::cout << std::endl;
shiftVal<0, 2, 10-2>::func(f);
for ( auto const & i : f.arr )
std::cout << i << ' ';
std::cout << std::endl;
}
Nobody else produce an example based on a C++11 simulation of std::integer_sequence (as suggested by W.F., Passer By and Sopel and the simpler solution, IMHO) so I propose the following one (of std::index_sequence and std::make_index_sequence in reality: simulate std::integer_sequence is more complicated)
template <std::size_t ...>
struct indexSequence
{ };
template <std::size_t N, std::size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
{ };
template <std::size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
{ using type = indexSequence<Next ... >; };
template <std::size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;
So a function (with function helper) to reproduce the asked loop can be written as
template
void shiftValHelper (T & t, indexSequence<Is...> const &)
{
using unused = int[];
(void)unused { 0,
(t.template bit<Is+Sh>() = t.template bit<Is>(), 0)... };
}
template <std::size_t Sh, std::size_t N, typename T>
void shiftVal (T & t)
{ shiftValHelper<Sh>(t, makeIndexSequence<N>{}); }
and called ad follows
shiftVal<2, N-2>(x);
The following is a full working example
#include <array>
#include <iostream>
template <std::size_t ...>
struct indexSequence
{ };
template <std::size_t N, std::size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
{ };
template <std::size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
{ using type = indexSequence<Next ... >; };
template <std::size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;
template <std::size_t N>
struct foo
{
std::array<int, N> arr;
template <std::size_t I>
int & bit ()
{ return arr[I]; }
};
template <std::size_t Sh, typename T, std::size_t ... Is>
void shiftValHelper (T & t, indexSequence<Is...> const &)
{
using unused = int[];
(void)unused { 0,
(t.template bit<Is+Sh>() = t.template bit<Is>(), 0)... };
}
template <std::size_t Sh, std::size_t N, typename T>
void shiftVal (T & t)
{ shiftValHelper<Sh>(t, makeIndexSequence<N>{}); }
int main ()
{
foo<10U> f { { { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 } } };
for ( auto const & i : f.arr )
std::cout << i << ' ';
std::cout << std::endl;
shiftVal<2, 10-2>(f);
for ( auto const & i : f.arr )
std::cout << i << ' ';
std::cout << std::endl;
}
I have a class that defines a constexpr size_t dimensions. In this class, I implemented an EvaluateOver<Dimensions...>(Lambda F) which does something over the dimensions I specify. For example, say dimensions=4 and f is some lambda expression:
MyClass.EvaluateOver<0,2>(f);
will perform f with respect to 0 and 2 by doing the following expansion:
template<size_t... Dims, typename Lambda>
inline auto EvaluateOver(const Lambda& F) const
{
F(std::get<Dims>(_memberTupleDataContainer)...);
}
Now I want another member function that will evaluate over the unspecified dimensions. So EvaluateOverOthers<0,2>(f) will perform its operation on dimensions 1 and 3.
Ideally, I am thinking of the following:
template<size_t... Dims, typename Lambda>
inline auto EvaluateOverOthers(const Lambda& F) const
{
EvaluateOver<
// variadic parameter that does the mathematical complement of
// Dims... with a variadic expansion of dimensions
>(F);
}
Following might help:
namespace details
{
template <typename Seq1, typename Seq2, typename Res = std::index_sequence<>>
struct minus;
// Nothing more to remove
template <std::size_t ... Is1, std::size_t... IRes>
struct minus<std::index_sequence<Is1...>,
std::index_sequence<>,
std::index_sequence<IRes...>>
{
using type = std::index_sequence<IRes..., Is1...>;
};
// Remove front elements as they are equal.
template <std::size_t I, std::size_t ... Is1, std::size_t ... Is2, std::size_t... IRes>
struct minus<std::index_sequence<I, Is1...>,
std::index_sequence<I, Is2...>,
std::index_sequence<IRes...>>
{
using type = typename minus<std::index_sequence<Is1...>,
std::index_sequence<Is2...>,
std::index_sequence<IRes...>>::type;
};
// Add front element to result.
template <std::size_t I1, std::size_t I2,
std::size_t ... Is1, std::size_t ... Is2,
std::size_t... IRes>
struct minus<std::index_sequence<I1, Is1...>,
std::index_sequence<I2, Is2...>,
std::index_sequence<IRes...>>
{
using type = typename minus<std::index_sequence<Is1...>,
std::index_sequence<I2, Is2...>,
std::index_sequence<IRes..., I1>>::type;
};
}
template <std::size_t N, typename Seq>
using complement = details::minus<std::make_index_sequence<N>, Seq>;
template <std::size_t N, typename Seq>
using complement_t = typename complement<N, Seq>::type;
// Some test
static_assert(std::is_same<std::index_sequence<0, 3>,
complement_t<4, std::index_sequence<1, 2>>>::value, "!");
And then
template<size_t... Is, typename Lambda>
auto EvaluateOver(const Lambda& F, std::index_sequence<Is...>) const
{
return F(std::get<Is>(_memberTupleDataContainer)...);
}
template<size_t... Dims, typename Lambda>
auto EvaluateOver(const Lambda& F) const
{
return EvaluateOver(F, std::index_sequence<Is...>{});
}
template<size_t... Is, typename Lambda>
auto EvaluateOverOthers(const Lambda& F) const
{
return EvaluateOver(F, complement_t<_dimension, std::index_sequence<Is...>>{});
}
I played with a constexpr C++17 solution (online demo). I think the logic for complements could be factored out (if desired).
#include <iostream>
#include <iterator>
#include <utility>
#include "my_constexpr_array.hpp"
template<size_t Nd>
struct MyClass {
static constexpr auto dim_arr = Array(std::make_index_sequence<Nd>{});
template<size_t... excludes, class F>
auto eval_others(F f) const {
constexpr auto excl_arr = Array{excludes...};
constexpr auto incl_pred = [&] (size_t i) { return !excl_arr.contains(i); };
constexpr auto incl_excl_arr = dim_arr.partition(incl_pred);
constexpr auto incl_count = dim_arr.count_if(incl_pred);
return eval_helper(
f,
[&] { return incl_excl_arr; },// wrapped in lambda to preserve constexpr
std::make_index_sequence<incl_count>{}// indices for trimming incl_excl_arr
);
}
template<class F, class Dims, size_t... is>
auto eval_helper(F f, Dims dims, std::index_sequence<is...>) const {
return f(std::integral_constant<size_t, dims()[is]>{}...);
}
};
int main() {
MyClass<7> foo{};
foo.eval_others<2, 4>([&] (auto... is) { (std::cout << ... << is) << "\n"; });
return 0;
}
Where "my_constexpr_array.hpp" would behave like
template<class T, size_t size>
struct Array {
static_assert(size >= 1);
T data_[size];
constexpr Array() noexcept
: data_{} {}
template<class... Ts>
explicit constexpr Array(T v0, Ts... vs) noexcept
: data_{v0, vs...} {}
template<T... vs>
explicit constexpr Array(std::integer_sequence<T, vs...>) noexcept
: data_{vs...} {}
constexpr T* begin() { return data_; }
constexpr const T* begin() const { return data_; }
constexpr T* end() { return begin() + size; }
constexpr const T* end() const { return begin() + size; }
constexpr decltype(auto) operator[](size_t i) { return data_[i]; }
constexpr decltype(auto) operator[](size_t i) const { return data_[i]; }
constexpr bool contains(const T& v) const {
for(auto& x : *this) if(x == v) return true;
return false;
}
template<class Pred>
constexpr size_t count_if(Pred pred) const {
size_t result = 0;
for(auto& x : *this) result += size_t(pred(x) ? 1 : 0);
return result;
}
template<class Pred>
constexpr Array partition(Pred pred) const {
// return a sorted copy such that all `true`s precede all `false`s
Array result{};
T* true_false_dst[2] = {result.begin(), result.begin() + count_if(pred)};
// pair of output iterators; use first if predicate is true and vice versa
for(auto& x : *this) *true_false_dst[pred(x) ? 0 : 1]++ = x;
return result;
}
friend std::ostream& operator<<(std::ostream& os, const Array& self) {
for(auto& x : self) os << x << ", ";
return os;
}
};
template<class T, class... Ts>
Array(T, Ts...) -> Array<T, size_t(1) + sizeof...(Ts)>;
template<class T, T... vs>
Array(std::integer_sequence<T, vs...>) -> Array<T, sizeof...(vs)>;
Is there a better way more compact to implement the computation of nBits below at compile time? Please note that the question is not how to implement n_active_bits, which I know how to do.
constexpr int n_active_bits(int m) { /* count the bits */ }
template <uint8_t...Masks>
struct MaskPack
{
// is there a more concise way than to implement
//the auxiliary recursive function count_bits?
static constexpr uint8_t nBits = count_bits(Masks...);
private:
template <typename M, typename...Ms>
static constexpr uint8_t count_bits(M&& m, Ms&&...ms)
{
return n_active_bits(m) + count_bits(ms...);
}
static constexpr uint8_t count_bits()
{
return 0;
}
};
I tried with no luck to use the one-liner C++17 folds:
static constexpr uint8_t nBits = (n_active_bits(Masks) + ...);
With folds you can do it as follows:
template <uint8_t...Masks>
struct MaskPack {
static constexpr uint8_t nBits = (n_active_bits(Masks) + ...);
};
Live Demo
Note that n_active_bits must be constexpr:
constexpr int n_active_bits(unsigned int mask) {
return mask == 0 ? 0 : (mask & 0x01) + n_active_bits(mask >> 1);
}
It requires some boilerplate.
template<size_t I, class Indexes>
struct offset_indexes;
template<size_t I, class Indexes>
using offset_indexes_t = typename offset_indexes<I,Indexes>::type;
template<size_t I, size_t...Is>
struct offset_indexes<I, std::index_sequence<Is...>> {
using type=std::index_sequence<(I+Is)...>;
};
then a binary fold:
template<size_t...Is, class Tuple>
constexpr auto retuple( std::index_sequence<Is...>, Tuple&& tuple ) {
return std::forward_as_tuple( std::get<Is>(std::forward<Tuple>(tuple))... );
}
template<class F, class T>
constexpr T binary_fold( F&& f, std::tuple<T>&& tuple ) {
return std::get<0>(std::move(tuple));
}
template<class Tuple>
struct first_element {};
template<class Tuple>
using first_element_t=typename first_element<Tuple>::type;
template<template<class...>class Z, class T0, class...Ts>
struct first_element<Z<T0,Ts...>>{using type=T0;};
template<class F, class Tuple, class E=first_element_t<std::decay_t<Tuple>>>
constexpr std::decay_t<E> binary_fold( F&& f, Tuple&& tuple ) {
constexpr auto count = std::tuple_size<std::decay_t<Tuple>>{};
using first_half = std::make_index_sequence< count/2 >;
using second_half = offset_indexes_t<
count/2,
std::make_index_sequence< (count+1)/2 >
>;
return f(
binary_fold( f, retuple( first_half{}, std::forward<Tuple>(tuple) ) ),
binary_fold( f, retuple( second_half{}, std::forward<Tuple>(tuple) ) )
);
}
which should make a binary tree of recursive calls to binary_fold.
All of this makes count_bits easy:
template <class...Ms>
constexpr size_t count_bits(Ms&&...ms)
{
return binary_fold(
std::plus<>{},
std::make_tuple(n_active_bits(std::forward<Ms>(ms))...)
);
}
note, however, that there is lots of boilerplate.
I did a more complex binary fold, because left/right fold has a deep recursive depth.
live example.