6

The question is pretty straight, how could I generate :

std::tuple<float, int, double>

If I know the type :

struct Foo { float a; int b; double c; };

And how could I retrieve the data, in both convertion?

Mathieu Van Nevel
  • 1,428
  • 1
  • 10
  • 26
  • 1
    There are no features in the C++ language itself to effect this kind of a type conversion. You will have to do it yourself. – Sam Varshavchik Feb 24 '18 at 15:13
  • The normal answer is that you cannot. C++ doesn't have the reflection required to do this. Of course you can add your own preprocessor script that parses the declaration and generates the tuple. There is also some UB-based boost thing that is not supposed to work but does anyways. – nwp Feb 24 '18 at 15:14
  • 5
    You'd need reflection. You can do something like that using [magic_get](https://github.com/apolukhin/magic_get), but it will only work with pod types. – Guillaume Racicot Feb 24 '18 at 15:14
  • @GuillaumeRacicot: Thanks for the link to **magic_get**. I was looking for that. Here is the [talk at CppCon](https://www.youtube.com/watch?v=abdeAew3gmQ) that explains it in great details. – Nawaz Feb 24 '18 at 15:18
  • We need clarification on "And how could I retrieve the data, in both convertion [sic]?" What is your desired _convention_ of retrieving data for tuple? Is it `std::get<0>(some_foo)`? – Wyck Feb 24 '18 at 15:41
  • @Wyck: That is how data is retrieved from a tuple, yes. – Lightness Races in Orbit Feb 24 '18 at 15:59
  • 1
    All users: The comments are for ***on-topic*** clarification of posts only. They are not for the kind of bickering I just cleaned up. Do not restart that discussion. – elixenide Feb 24 '18 at 20:05

6 Answers6

9

You cannot do this in C++, as it would require a language feature known as reflection.

Instead, "manually" build the tuple or just begin with a tuple in the first place.

Alternatively, you could build a script in Python (or similar) to preprocess your code and auto-generate the resulting conversion.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I was hoping some improvement with C++17 or even C++20. Well that's a shame I guess :) – Mathieu Van Nevel Feb 24 '18 at 15:25
  • Upvoted for the last sentence. Sometime I would prefer Python instead of ugly metaprogramming... – llllllllll Feb 24 '18 at 15:27
  • 1
    Indeed. There's nothing wrong with a nice pre-build step. I have a list of "message types" and their logical fields, in a text file, that Python will "compile" into C++ classes for me resplendent with all the ugly boilerplate they require. It's lovely. – Lightness Races in Orbit Feb 24 '18 at 15:35
3

You can write a conversion operator.

struct Test {
    int a;
    float b;
    double c;

    explicit operator std::tuple<int, float, double>() const {
       return {a, b, c};
    }
};

Then, use it like that:

int main() {
    Test t{12, 3.2f, 4.5};
    std::tuple tt = t; 
}

Live example

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
3

As other answer said, there is no way to do this in a generic way in C++14.

However, there is a trick in C++17 that uses structured bindings:

template<typename T>
auto as_tuple_ref( T & t )
{
   auto &[a,b,c] = t;
   return std::tie(a,b,c);
}

struct Foo { float a; int b; double c; };

Foo bar;
int & b_ref = std::get<1>( as_tuple_ref(bar) );

This version only works for structures with 3 members, but I believe that with some time and effort (and some SFINAE) it is possible to write a fully generic solution (and it will probably involve a lot of copy-paste).

sbabbi
  • 11,070
  • 2
  • 29
  • 57
1

You can do it manually. For example:

#include <iostream>
#include <tuple>

using std::tuple;
using std::cout;
using std::endl;
using std::get;

struct Foo { float a; int b; double c; };

int main()
{
  auto tuple_foo = tuple<decltype(Foo::a), decltype(Foo::b), decltype(Foo::c)>{1.1f, 10, 100.001};
  cout << "<0>: " << get<0>(tuple_foo) << endl;
  cout << "<1>: " << get<1>(tuple_foo) << endl;
  cout << "<2>: " << get<2>(tuple_foo) << endl;
}
Eljay
  • 4,648
  • 3
  • 16
  • 27
0

Based on those member names, i.e.: the 1st member is called a, the 2nd b, and the 3rd c. You can define the following struct template, member_type<>, to pick up the type of each individual member:

template<typename T>
struct member_type : T {
    using a_t = decltype(T::a);   
    using b_t = decltype(T::b);   
    using c_t = decltype(T::c);   
};

With this alias template, tuple_splitter<>, you can define a tuple from a struct with such members (named a, b and c):

template<typename T>
using tuple_splitter = std::tuple
                 <
                    typename member_type<T>::a_t,
                    typename member_type<T>::b_t,
                    typename member_type<T>::c_t
                 >;

Then, for your Foo:

tuple_splitter<Foo> foo_tuple;

foo_tuple will have the type std::tuple<float, int, double>.

If you define now a new struct, Bar, as:

struct Bar { int a; char b; float c; };

Then:

tuple_splitter<Bar> bar_tuple;

bar_tuple will be of type std::tuple<int, char, float>.

JFMR
  • 23,265
  • 4
  • 52
  • 76
0

Using std::tie even makes it easier:

struct foo
{
    int value1;
    int value2;
    string str;
};

void main()
{
    std::tuple<int, int, string> a{ 1, 1, "Hello " };
    foo b;
    std::tie(b.value1, b.value2, b.str) = a;
}
seccpur
  • 4,996
  • 2
  • 13
  • 21