tuples in C++11

Discussion in 'C++' started by Single Stage to Orbit, Aug 14, 2012.

  1. 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!
    --
    Tactical Nuclear Kittens
    Single Stage to Orbit, Aug 14, 2012
    #1
    1. Advertising

  2. On 8/14/2012 2:17 PM, Single Stage to Orbit wrote:
    > 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
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Aug 14, 2012
    #2
    1. Advertising

  3. On 14.08.2012 20:17, Single Stage to Orbit wrote:
    > 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
    Marcel Müller, Aug 14, 2012
    #3
  4. On Tue, 2012-08-14 at 14:33 -0400, Victor Bazarov wrote:
    > On 8/14/2012 2:17 PM, Single Stage to Orbit wrote:
    > > Suppose I have the following:


    [ 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.
    --
    Tactical Nuclear Kittens
    Single Stage to Orbit, Aug 14, 2012
    #4
  5. 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.
    --
    Tactical Nuclear Kittens
    Single Stage to Orbit, Aug 14, 2012
    #5
  6. On 8/14/2012 3:15 PM, Single Stage to Orbit wrote:
    > 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
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Aug 14, 2012
    #6
  7. 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!
    >
    > --
    >
    > Tactical Nuclear Kittens




    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!
    >
    > --
    >
    > Tactical Nuclear Kittens


    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.
    88888 Dihedral, Aug 14, 2012
    #7
  8. On Tue, 2012-08-14 at 13:43 -0700, 88888 Dihedral wrote:
    > 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.
    --
    Tactical Nuclear Kittens
    Single Stage to Orbit, Aug 14, 2012
    #8
  9. On Tue, 2012-08-14 at 16:39 -0400, Victor Bazarov wrote:
    > > 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).


    Indeed no, I realise that. Perhaps a template specialization will solve
    that particular problem for me.
    --
    Tactical Nuclear Kittens
    Single Stage to Orbit, Aug 14, 2012
    #9
  10. Single Stage to Orbit

    Marc Guest

    Marcel Müller wrote:

    > 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<int,3> please, since the context is C++11...

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


    Indeed, doesn't compile.
    Marc, Aug 14, 2012
    #10
  11. Single Stage to Orbit

    Casey Carter Guest

    On 2012-08-14 13:17, Single Stage to Orbit wrote:
    > 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;
    }
    Casey Carter, Aug 14, 2012
    #11
  12. On Tue, 2012-08-14 at 17:01 -0500, Sam wrote:
    > > Bit of a hack but yes you're quite right. Thanks.

    >
    > 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.
    --
    Tactical Nuclear Kittens
    Single Stage to Orbit, Aug 15, 2012
    #12
  13. Single Stage to Orbit

    SG Guest

    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) {
    cout << get<i>(t) << endl;
    }

    > 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
    SG, Aug 15, 2012
    #13
  14. Single Stage to Orbit <> wrote:
    > 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.
    Juha Nieminen, Aug 15, 2012
    #14
  15. Single Stage to Orbit

    Jeff Flinn Guest

    On 8/14/2012 2:17 PM, Single Stage to Orbit wrote:
    > 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;
    }
    Jeff Flinn, Aug 15, 2012
    #15
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jeff Kish
    Replies:
    11
    Views:
    797
    Jeff Kish
    Oct 21, 2004
  2. Replies:
    5
    Views:
    541
    Thomas J. Gritzan
    Oct 6, 2006
  3. tuples within tuples

    , Oct 26, 2007, in forum: Python
    Replies:
    12
    Views:
    551
    Dennis Lee Bieber
    Oct 27, 2007
  4. xera121
    Replies:
    8
    Views:
    707
    lolmc
    Sep 30, 2009
  5. Jon Reyes
    Replies:
    18
    Views:
    220
    Mitya Sirenef
    Feb 19, 2013
Loading...

Share This Page