0

How can I define a function object that can be passed to apply? Ultimately, I want a function F f that can take all the elements of the tuple and push them back t a vector.

template <class F, size_t... Is>
constexpr auto index_apply_impl(F f, index_sequence<Is...>) {
    return f(integral_constant<size_t, Is> {}...);
}

template <size_t N, class F>
constexpr auto index_apply(F f) {
    return index_apply_impl(f, make_index_sequence<N>{});
}

template <class Tuple, class F>
constexpr auto apply(Tuple t, F f) {
    return index_apply<tuple_size<Tuple>{}>(
        [&](auto... Is) { return f(get<Is>(t)...); });
}

Thanks :)

4
  • The idea is very nice, but it would be a vector of what? Commented Aug 18, 2017 at 2:05
  • Possible duplicate of iterate over tuple Commented Aug 18, 2017 at 2:07
  • Not a duplicate. This particular question has no answer, since the only kind of vector-like thing that could hold the result would be a tuple that is a copy of the input. Commented Aug 18, 2017 at 2:09
  • Thanks, Michaël. I need the underling bytes. The problem that I am trying to solve is to take a tuple like (12, "abc", 10, 'c', 1) and convert each element into its equivalent byte array, and store all those bytes in a final array. Commented Aug 18, 2017 at 2:22

1 Answer 1

1

You can use the expander trick. This needs C++14 though. I don't know what your byte array format looks like, so I just took a generic struct with a string member and convert everything to string in the constructor.

#include <iostream>
#include <string>
#include <tuple>
#include <vector>

template < typename T, typename F, size_t ... Is >
void for_each_impl(T&& t, F&& f, std::index_sequence<Is...>)
{
  using expand_type = int[];
  (void) expand_type { 0, ( (void) f(std::get<Is>(t)), 0) ... };
}

template < typename... Args, typename F >
void for_each(std::tuple<Args...> const& t, F&& f)
{
  for_each_impl(t, f, std::make_index_sequence<sizeof...(Args)>{});
}


struct Bytes {
  std::string str;
  Bytes(char const * c) : str(c) {};
  Bytes(int i) : str(std::to_string(i)) {};
  Bytes(char c) : str(1, c) {};
};


int main()
{
  auto t = std::make_tuple(12, "abc", 10, 'c', 1);
  std::vector<Bytes> v;
  for_each(t, [&v](auto&& x){ v.push_back(x); });

  for (auto const& e : v)
    std::cout << e.str << ' ';
  std::cout << '\n';
}

Live example


In C++17 it is much easier, thanks to variadic lambda, fold expression, and std::apply.

#include <iostream>
#include <string>
#include <tuple>
#include <vector>

struct Bytes {
  std::string str;
  Bytes(char const * c) : str(c) {};
  Bytes(int i) : str(std::to_string(i)) {};
  Bytes(char c) : str(1, c) {};
};


int main()
{
  auto t = std::make_tuple(12, "abc", 10, 'c', 1);
  std::vector<Bytes> v;
  std::apply([&v](auto&&... x){ (... , v.push_back(x)); }, t);

  for (auto const& e : v)
    std::cout << e.str << ' ';
  std::cout << '\n';
}

Live example


If you cannot use C++11 your life will be painful. You have to roll your own implementation of index_sequence and you have to define a helper struct with templated call operator to substitute the generic lambda.

#include <iostream>
#include <string>
#include <tuple>
#include <vector>

// https://stackoverflow.com/a/24481400/1944004
template <size_t ...I>
struct index_sequence {};

template <size_t N, size_t ...I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};

template <size_t ...I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};


// Call a function for each element in a tuple

template < typename T, typename F, size_t ... Is >
void for_each_impl(T&& t, F&& f, index_sequence<Is...>)
{
  using expand_type = int[];
  (void) expand_type { 0, ( (void) f(std::get<Is>(t)), 0) ... };
}

template < typename... Args, typename F >
void for_each(std::tuple<Args...> const& t, F&& f)
{
  for_each_impl(t, f, make_index_sequence<sizeof...(Args)>{});
}

// "Byte array" emulation

struct Bytes {
  std::string str;
  Bytes(char const * c) : str(c) {};
  Bytes(int i) : str(std::to_string(i)) {};
  Bytes(char c) : str(1, c) {};
};

// Surrogate template lambda

struct Visitor
{
  std::vector<Bytes>& v;
  Visitor(std::vector<Bytes>& vb) : v(vb) {};
  template < typename T >
  void operator() (T&& x) { v.push_back(x); }
};


int main()
{
  auto t = std::make_tuple(12, "abc", 10, 'c', 1);
  std::vector<Bytes> v;
  for_each(t, Visitor(v));

  for (auto const& e : v)
    std::cout << e.str << ' ';
  std::cout << '\n';
}

Live example

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Henri! That was brilliant! Is there any way to accomplish this in C++11?
@a03 yes, rewrite the lambda as an object with a template operator().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.