How can I put packed parameter types into a tuple?

Viewed 111
template<typename... Args, typename Action>
    void execute(const Action& _action) {
       using all_components = std::tuple<Args...>;
       auto tuple = std::tie();
       for (int s = 0; s < all_components.size(); s++) {                     
          tuple = std::tuple_cat(tuple, *reinterpret_cast<get<s>(all_components)*>(data[s]);
        }
       std::apply(_action, tuple);
    }

I want to access the Parameter types, so I can cast my data that is saved in vector<char> type. In the type the function action needs it to have. But I'm not sure if how to access them. This was my idea, but it doesn't work, as the get function returns error: syntax error: unexpected token 'identifier', expected 'type specifier', I get what the error means, no explanation needed, but I need an alternative solution.

1 Answers

Couple of things:

  1. all_components is a type, it does not have size() method. You can use sizeof...(Args) to obtain the length of the pack.
  2. std::get requires a tuple object. Since it is a cast, thus unevaluated context, you can use std::declval<all_components>() to obtain the tuple.
  3. Because std::get returns a value, you have to convert it to a type for it to be casted to.
#include <tuple>
#include <functional>

template<typename... Args, typename Action>
void execute(const Action& _action) {
    using all_components = std::tuple<Args...>;
    auto tuple = std::tie();
    for (int s = 0; s < sizeof...(Args); s++) {                     
        tuple = std::tuple_cat(tuple, *reinterpret_cast<decltype(std::get<s>(std::declval<all_components>()))*>(data[s]);
    }
    std::apply(_action, tuple);
}

But

  • you are still missing data.
  • Because s is used in a template, it has to be constexpr, so the ordinary loop won't compile.
  • Is sizeof of every Arg 1? If not, the deserialization is wrong.

My wild guess is that the data is tightly packed inside the vector. In that case you need something like this:

#include <tuple>
#include <functional>

template<typename...Args>
constexpr auto data_indices(){
    std::array is{std::size_t(0), sizeof(Args)...};
    for(std::size_t i=1;i<is.size();++i){
        is[i]+=is[i-1];
    }
    return is;
}

template<typename...Args,std::size_t...Is>
auto get_tuple(std::index_sequence<Is...>, const char* data){
    auto indices = data_indices<Args...>();
    return std::tuple{*reinterpret_cast<const Args*>(data+indices[Is])...};
}

template<typename... Args, typename Action>
void execute(const Action& _action, const char* data) {
    std::apply(_action, get_tuple<Args...>(std::make_index_sequence<sizeof...(Args)>{},data));
}

#include <cstring>
#include <iostream>

void foo(int x, double y){
    std::cout<<x<<' '<<y<<'\n';
}
int main(){
    
    std::array<char,sizeof(int)+sizeof(double)> arr;
    int x = 12;
    double y = 24.0;
    std::memcpy(arr.data(),&x,sizeof(x));
    std::memcpy(arr.data()+sizeof(x), &y,sizeof(y));

    execute<int,double>(foo,arr.data());
}

Unless you are using placement new to put the elements into the array, you have to explicitly copy them to the tuple, otherwise you are breaking the strict aliasing rule. So:

template<typename...Args,std::size_t...Is>
auto get_tuple(std::index_sequence<Is...>, const char* data){
    auto indices = data_indices<Args...>();
    std::tuple<Args...> tuple;

    (std::memcpy(&std::get<Is>(tuple), data+indices[Is], sizeof(Args)), ...);
    return tuple;
}
Related