tuples in C++11

  • Thread starter Single Stage to Orbit
  • Start date
S

Single Stage to Orbit

Hello!

Suppose I have the following:

int main()
{
typedef boost::tuple<int, int, int> tuple3;
std::vector<tuple3> tuples;

tuples.push_back(tuple3(1, 2, 3));
tuples.push_back(tuple3(7, 8, 9));
tuples.push_back(tuple3(4, 5, 6));

for (auto& i : tuples)
std::cout << i.get<0>() << " " << i.get<1>() << " " <<
i.get<2>() << '\n';

return 0;
}

Is it even possible to have something like this:

for (auto& i : tuples)
{
for (unsigned j = 0; j < 3; ++j)
{
std::cout << i.get<j>();
if (j < 3)
std::cout << " ";
}

std::cout << '\n';
}

When I try it, GCC 4.6.3 says it's illegal to have an non constant
expression as in 'i.get<j>'. Are there any workarounds for this one?

Thanks!
 
V

Victor Bazarov

Suppose I have the following:

int main()
{
typedef boost::tuple<int, int, int> tuple3;
std::vector<tuple3> tuples;

tuples.push_back(tuple3(1, 2, 3));
tuples.push_back(tuple3(7, 8, 9));
tuples.push_back(tuple3(4, 5, 6));

for (auto& i : tuples)
std::cout << i.get<0>() << " " << i.get<1>() << " " <<
i.get<2>() << '\n';

return 0;
}

Is it even possible to have something like this:

for (auto& i : tuples)
{
for (unsigned j = 0; j < 3; ++j)
{
std::cout << i.get<j>();
if (j < 3)
std::cout << " ";
}

std::cout << '\n';
}

When I try it, GCC 4.6.3 says it's illegal to have an non constant
expression as in 'i.get<j>'. Are there any workarounds for this one?

No, there are no work-arounds. But this really have nothing to do with
tuples or C++ 11.

You will get the same/similar error in this code:

#include <iostream>

template<int i> int foo() { return i*42; }

int main() {
for (int j = 0; j < 3; ++j)
std::cout << foo<j>() << std::endl;
}

The template argument for 'foo' can only be a const expression. A
run-time expression ('j') cannot be used.

You *could* write an adapter function, something like

template<class Tup> int Tget(Tup const& tup, int j)
{
switch (j)
{
case 0: return tup.get<0>();
case 1: return tup.get<1>();
....
}

and then use it

std::cout << Tget(i, j) << ...

but that kind of defeats the purpose, doesn't it?

V
 
M

Marcel Müller

Is it even possible to have something like this:

for (auto& i : tuples)
{
for (unsigned j = 0; j< 3; ++j)
{
std::cout<< i.get<j>();
if (j< 3)
std::cout<< " ";
}

std::cout<< '\n';
}

When I try it, GCC 4.6.3 says it's illegal to have an non constant
expression as in 'i.get<j>'. Are there any workarounds for this one?

No, this is not possible. The compiler cannot know, which function to
call, because at the compile time j does not have a value. In fact you
want to call /another/ function at each loop iteration.
Furthermore i.get<0> and i.get<1> do not have the same type in general.
So the compiler cannot know the type of the expression get<j>.

Your code requires run time polymorphism rather than compile time
polymorphism.


If you want to iterate over the components of your tuple and if all
components have the same type then tuple<> is not the solution. This is
a vector (in the mathematical sense). The C++ equivalent of a vector
with a constant length is simply int[3]. I.e.:

typedef int tuple3[3];
....
tuples.push_back(tuple3({1, 2, 3}));
....
std::cout<< i[j];

(untested)


tuple<> is more something like an anonymous structure.


Marcel
 
S

Single Stage to Orbit

[ snip ]
The template argument for 'foo' can only be a const expression. A
run-time expression ('j') cannot be used.

OK, not possible.
You *could* write an adapter function, something like

template<class Tup> int Tget(Tup const& tup, int j)
{
switch (j)
{
case 0: return tup.get<0>();
case 1: return tup.get<1>();
....
}

[ snip ]
but that kind of defeats the purpose, doesn't it?

Bit of a hack but yes you're quite right. Thanks.
 
S

Single Stage to Orbit

On Tue, 2012-08-14 at 20:43 +0200, Marcel Müller wrote:

[ snip ]
No, this is not possible. The compiler cannot know, which function to
call, because at the compile time j does not have a value. In fact you
want to call /another/ function at each loop iteration.
Furthermore i.get<0> and i.get<1> do not have the same type in general.
So the compiler cannot know the type of the expression get<j>.

Your code requires run time polymorphism rather than compile time
polymorphism.

Right, I see.
If you want to iterate over the components of your tuple and if all
components have the same type then tuple<> is not the solution. This is
a vector (in the mathematical sense). The C++ equivalent of a vector
with a constant length is simply int[3]. I.e.:

They won't all be the same types eventually - it was just an experiment
to see what I can do with tuples using the index into the tuple to
retrieve things.

I could use constants to get at the fields in the tuple, I suppose.
 
V

Victor Bazarov

On Tue, 2012-08-14 at 20:43 +0200, Marcel Müller wrote:

[ snip ]
No, this is not possible. The compiler cannot know, which function to
call, because at the compile time j does not have a value. In fact you
want to call /another/ function at each loop iteration.
Furthermore i.get<0> and i.get<1> do not have the same type in general.
So the compiler cannot know the type of the expression get<j>.

Your code requires run time polymorphism rather than compile time
polymorphism.

Right, I see.
If you want to iterate over the components of your tuple and if all
components have the same type then tuple<> is not the solution. This is
a vector (in the mathematical sense). The C++ equivalent of a vector
with a constant length is simply int[3]. I.e.:

They won't all be the same types eventually - it was just an experiment
to see what I can do with tuples using the index into the tuple to
retrieve things.

If they are going to be different types, a function like I suggested
won't work (they need to be of the same type - the return value type of
that function, of course).

V
 
8

88888 Dihedral

Single Stage to Orbitæ–¼ 2012å¹´8月15日星期三UTC+8上åˆ2時17分26秒寫é“:
Hello!



Suppose I have the following:



int main()

{

typedef boost::tuple<int, int, int> tuple3;

std::vector<tuple3> tuples;



tuples.push_back(tuple3(1, 2, 3));

tuples.push_back(tuple3(7, 8, 9));

tuples.push_back(tuple3(4, 5, 6));



for (auto& i : tuples)

std::cout << i.get<0>() << " " << i.get<1>() << " " <<

i.get<2>() << '\n';



return 0;

}



Is it even possible to have something like this:



for (auto& i : tuples)

{

for (unsigned j = 0; j < 3; ++j)

{

std::cout << i.get<j>();

if (j < 3)

std::cout << " ";

}



std::cout << '\n';

}



When I try it, GCC 4.6.3 says it's illegal to have an non constant

expression as in 'i.get<j>'. Are there any workarounds for this one?



Thanks!



Single Stage to Orbitæ–¼ 2012å¹´8月15日星期三UTC+8上åˆ2時17分26秒寫é“:
Hello!



Suppose I have the following:



int main()

{

typedef boost::tuple<int, int, int> tuple3;

std::vector<tuple3> tuples;



tuples.push_back(tuple3(1, 2, 3));

tuples.push_back(tuple3(7, 8, 9));

tuples.push_back(tuple3(4, 5, 6));



for (auto& i : tuples)

std::cout << i.get<0>() << " " << i.get<1>() << " " <<

i.get<2>() << '\n';



return 0;

}



Is it even possible to have something like this:



for (auto& i : tuples)

{

for (unsigned j = 0; j < 3; ++j)

{

std::cout << i.get<j>();

if (j < 3)

std::cout << " ";

}



std::cout << '\n';

}



When I try it, GCC 4.6.3 says it's illegal to have an non constant

expression as in 'i.get<j>'. Are there any workarounds for this one?



Thanks!

Do you plan to compress imutable objects into a serializable frozen file in
the hard disk or your own heap manager?

If you are not planning to do that then I don't think immutable tuples
are very helpful.
 
S

Single Stage to Orbit

Do you plan to compress imutable objects into a serializable frozen
file in the hard disk or your own heap manager?

If you are not planning to do that then I don't think immutable tuples
are very helpful.

That's a bit advanced. Right now I'm just playing with the various
features of the C++11 language. Some I really like, like the new 'for
(auto& item : items)' syntax.
 
S

Single Stage to Orbit

If they are going to be different types, a function like I suggested
won't work (they need to be of the same type - the return value type
of that function, of course).

Indeed no, I realise that. Perhaps a template specialization will solve
that particular problem for me.
 
M

Marc

Marcel said:
If you want to iterate over the components of your tuple and if all
components have the same type then tuple<> is not the solution. This is
a vector (in the mathematical sense). The C++ equivalent of a vector
with a constant length is simply int[3]. I.e.:

std::array said:
typedef int tuple3[3];
...
tuples.push_back(tuple3({1, 2, 3}));
...
std::cout<< i[j];

(untested)

Indeed, doesn't compile.
 
C

Casey Carter

Is it even possible to have something like this:

for (auto& i : tuples)
{
for (unsigned j = 0; j < 3; ++j)
{
std::cout << i.get<j>();
if (j < 3)
std::cout << " ";
}

std::cout << '\n';
}

It's possible if you convert the iteration into recursion, and implement
the recursion with some nasty template meta-programming:

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

template <std::size_t Index, std::size_t Max, typename Tuple>
struct dump_helper
{
static void dump(std::eek:stream& os, const Tuple& t)
{
if (Index > 0)
os << ' ';
os << std::get<Index>(t);
dump_helper<Index+1,Max,Tuple>::dump(os, t);
}
};

template <std::size_t Max, typename Tuple>
struct dump_helper<Max,Max,Tuple>
{
static void dump(std::eek:stream&, const Tuple&)
{}
};

template <typename... Args>
inline void dump(std::eek:stream& os, const std::tuple<Args...>& t)
{
typedef std::tuple<Args...> Tuple;
dump_helper<0,sizeof...(Args),Tuple>::dump(os, t);
}

int main()
{
typedef std::tuple<int, int, int> tuple3;
std::vector<tuple3> tuples;

tuples.push_back(tuple3(1, 2, 3));
tuples.push_back(tuple3(7, 8, 9));
tuples.push_back(tuple3(4, 5, 6));

for (const auto& i : tuples)
{
dump(std::cout, i);
std::cout << '\n';
}

return 0;
}
 
S

Single Stage to Orbit

Actually, it is possible. But it's not that easy.

If you Google around, you'll find examples of a templates that convert
tuples to parameter packs.

You take that, and feed it to a variadic recursive template function
that walks its parameters.

Ergo, use this template with an arbitrarily-sized tuple.

Put some more elbow grease on it, and you can wind up with a template
function that takes a tuple, a variable int, and returns the i-ths
tuple value.

There's been an excellent example posted.
But this will only work if all tuple values are the same type (or, are
convertible to the same type, at least).

If all the types were the same, I'd use a std::array<type> instead. I
suppose I could write templates for each of the types used in the tuple.
Or use constants to index into the fields of the tuple.
 
S

SG

Am Dienstag, 14. August 2012 20:17:26 UTC+2 schrieb Single Stage to Orbit:
typedef boost::tuple<int, int, int> tuple3;

tuple3 t (1,2,3);
for (int i=0; i<3; ++i) {
Is it even possible to have something like this

No. <i> after get is supposed to be a template parameter and therefore their values have to be known at compile-time. If you need to decide which element you want to access at run-time and the fixed-size collection is _homogenious_ like in your case (you only have ints), you might want to use boost::array<int,3> instead. This type allows you to get an element by a run-timedependent index.

Cheers!
SG
 
J

Juha Nieminen

Single Stage to Orbit said:
typedef boost::tuple<int, int, int> tuple3;

You seem to be using tuple when you should really be using std::array.
With the latter, runtime indexing will work.
 
J

Jeff Flinn

Hello!

Suppose I have the following:

int main()
{
typedef boost::tuple<int, int, int> tuple3;
std::vector<tuple3> tuples;

tuples.push_back(tuple3(1, 2, 3));
tuples.push_back(tuple3(7, 8, 9));
tuples.push_back(tuple3(4, 5, 6));

for (auto& i : tuples)
std::cout << i.get<0>() << " " << i.get<1>() << " " <<
i.get<2>() << '\n';

return 0;
}

Is it even possible to have something like this:

for (auto& i : tuples)
{
for (unsigned j = 0; j < 3; ++j)
{
std::cout << i.get<j>();
if (j < 3)
std::cout << " ";
}

std::cout << '\n';
}

When I try it, GCC 4.6.3 says it's illegal to have an non constant
expression as in 'i.get<j>'. Are there any workarounds for this one?

While I realize that you're just using this to investigate working with
tuple types in general, certainly boost tuple already handles IO. You
can do for example:

#include "boost/tuple/tuple_io.hpp"

int main() // untested
{
typedef boost::tuple<int, int, int> tuple3;
std::vector<tuple3> tuples;

tuples.push_back(tuple3(1, 2, 3));
tuples.push_back(tuple3(7, 8, 9));
tuples.push_back(tuple3(4, 5, 6));

for (auto& i : tuples)
{
using namespace boost::tuples;

std::cout << set_delimiter(' ') << set_close('\n') << i;
}
return 0;
}

As others have stated, you are mixing compiletime and runtime
polymorphism. This is boost fusion's realm.

#include <boost/fusion/adapted/boost_tuple.hpp>
#include <iostream>

struct print //some polymorphic callable function type
{
typedef void result_type;

std::eek:stream& mos;

print(std::eek:stream& os) : mos(os) {}

template<typename T> void operator()(const T& t) const
{
mos << t << ' ';
}
};

int main() // untested
{
typedef boost::tuple<int, float, std::string> tuple3;
std::vector<tuple3> tuples;

tuples.push_back(tuple3(1, 2.1f, '3'));
tuples.push_back(tuple3(7, 8.1f, '9'));
tuples.push_back(tuple3(4, 5.1f, '6'));

for (auto& i : tuples)
{
boost::fusion::for_each(i, print(std::cout));

std::cout << '\n';
}
return 0;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top