How to statically apply a function to individual elements of a non-type template template and summarize the results?

Is there a better way to more compactly implement the nBits calculation below at compile time? 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 not to use single-line C ++ 17 folds:

static constexpr uint8_t nBits = (n_active_bits(Masks) + ...);
+4
source share
2 answers

This requires some template.

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 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 binary_fold.

All this simplifies count_bits:

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 are many patterns.

, / .

.

+2

:

template <uint8_t...Masks>
struct MaskPack {
   static constexpr uint8_t nBits = (n_active_bits(Masks) + ...);
};

Live Demo

, n_active_bits constexpr:

constexpr int n_active_bits(unsigned int mask) {
  return mask == 0 ? 0 : (mask & 0x01) + n_active_bits(mask >> 1);
}
+4

Source: https://habr.com/ru/post/1624813/


All Articles