Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

extract an array from another array at compile time using c++

Not sure if it's possible for more later version of c++. (I can't figure out using traditional c++ to achieve the following behaviofgr.)

For example,

If I have an array defined like this:

In the header file

struct Def {
  static const int N = 5;
  static const double data[N];
};

In its cpp

const double Def::data[Def::N] = {0,1,2,3,4};

Is it possible to have a template get_subarray such that

get_subarray<Def,2,0>::data will be an array of content {0,2,4}

get_subarray<Def,2,1>::data will be an array of content {1,3}

where

template<typename T, int M, int m>
struct get_phase {
    // some code for the array variable data which will 
    // extract element from T::data for every M sample offset by index m
};
like image 439
Carson Pun Avatar asked Jul 24 '16 20:07

Carson Pun


2 Answers

As mentioned in the comments, the OP is interested also in C++14 based solutions.
Here is one of them:

#include<functional>
#include<cstddef>
#include<utility>
#include<array>

template<std::size_t O, typename T, std::size_t N, std::size_t... I>
constexpr std::array<T, sizeof...(I)>
f(const std::array<T, N> &arr, std::index_sequence<I...>) {
    return { std::get<I+O>(arr)... };
}

template<std::size_t B, std::size_t E, typename T, std::size_t N>
constexpr auto f(const std::array<T, N> &arr) {
    return f<B>(arr, std::make_index_sequence<E-B>());
}

int main() {
    constexpr std::array<int, 3> a1 = { 0, 1, 2 };
    constexpr auto a2 = f<1, 2>(a1);
    static_assert(a1[1] == a2[0], "!");
}

In this case, a2 is equal to { 1 }.
It's worth it checking B and E so as to verify that E is greater than B, but the example should give an idea of what's the way to do it.

To port it to C++11:

  • Do not use auto as return type, but explicitly specify std::array (easy)

  • Search on the web one of the available C++11 implementations of integer_sequence and make_index_sequence and use it

If it's fine to be explicit about the indexes and not use a range, here is a naïve snippet that should work in C++11:

#include<cstddef>
#include<utility>
#include<array>

template<std::size_t... I, typename T, std::size_t N>
constexpr std::array<T, sizeof...(I)>
f(const std::array<T, N> &arr) {
    return { std::get<I>(arr)... };
}

int main() {
    constexpr std::array<int, 3> a1 = { 0, 1, 2 };
    constexpr auto a2 = f<1>(a1);
    static_assert(a1[1] == a2[0], "!");
}

As in the previous example, a2 is { 1 }.

like image 136
skypjack Avatar answered Sep 17 '22 20:09

skypjack


I like the skypjack's solution but it doesn't extract the requested values. skypjack's version has two parameters, "begin" and "end". The OP requested "stride" or "frequency" and "begin".

I've modified it to match the OP's requested "frequency" and "start" arguments, giving the template non-type parameters more self-explaining names, and rewriting a couple of index calculations:

#include<utility>
#include<iostream>
#include<array>

template <std::size_t Freq, std::size_t Start, typename T, std::size_t Dim,
          std::size_t... I>
constexpr std::array<T, sizeof...(I)>
extractHelper (const std::array<T, Dim> & arr,
               std::integer_sequence<std::size_t, I...>)
 { return { { std::get<Freq*I+Start>(arr)... } }; }

template <std::size_t Freq, std::size_t Start, typename T, std::size_t Dim>
constexpr auto extractSamples (const std::array<T, Dim> & arr)
 { return extractHelper<Freq, Start>
      (arr, std::make_index_sequence<(Dim+Freq-1-Start)/Freq>()); }

Here is some test code:

int main()
 {
   constexpr std::array<int, 8> a1 = { { 0, 1, 2, 3, 4, 5, 6, 7 } };

   constexpr auto e1 = extractSamples<2, 0>(a1);
   constexpr auto e2 = extractSamples<2, 1>(a1);
   constexpr auto e3 = extractSamples<3, 0>(a1);
   constexpr auto e4 = extractSamples<3, 1>(a1);
   constexpr auto e5 = extractSamples<3, 2>(a1);

   std::cout << "samples<2, 0>: ";

   for ( auto const & i : e1 )
      std::cout << ' ' << i;

   std::cout << "\nsamples<2, 1>: ";

   for ( auto const & i : e2 )
      std::cout << ' ' << i;

   std::cout << "\nsamples<3, 0>: ";

   for ( auto const & i : e3 )
      std::cout << ' ' << i;

   std::cout << "\nsamples<3, 1>: ";

   for ( auto const & i : e4 )
      std::cout << ' ' << i;

   std::cout << "\nsamples<3, 2>: ";

   for ( auto const & i : e5 )
      std::cout << ' ' << i;

   std::cout << std::endl;

   return 0;
 }

The output is:

samples<2, 0>:  0 2 4 6
samples<2, 1>:  1 3 5 7
samples<3, 0>:  0 3 6
samples<3, 1>:  1 4 7
samples<3, 2>:  2 5

which matches the OP's requests

like image 39
max66 Avatar answered Sep 16 '22 20:09

max66