tuple

The problem with multiple return values


C++ is a C based language and keeps it’s limitation of returning a single value from a function. you can’t even return an array of objects from a function. However real life application at times need to return multiple data from a function. Generally this is how we return multiple values from a function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct MultipleReturnValues
{
    double d;
    int i;
    char c;
};

MultipleReturnValues MultiReturnValueFunction()
{
    //  really complicated logic
    //  ...
    MultipleReturnValues retval;
    return retval;
}

int MultiReturnValueFunction2(double* d, char* c)
{
    return 0;
}

int main()
{
    MultipleReturnValues retVal = MultiReturnValueFunction();
    double d;
    char c;
    int i = MultiReturnValueFunction2(&d, &c);
    
    return 0;
}

Which implies we define a new structure every time we have to return more than one value or increase the number of parameters to the function. Both of these are painful in long run, creating and maintaining a struct is at times tedious and with so many variables to a function it is very difficult to understand what each parameter does.

std::pair


standard library has a utility to group two values together also knows as std::pair, it is a template that takes in two values. It is used extensively with standard containers to return a pair of values from functions. Lets see how we can use it to return multiple values:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
#include  <utility>

typedef std::pair<int, int> TwoInts;

TwoInts TwoIntsFunction()
{
    return TwoInts();
}

void TwoIntsFunction_Demo()
{
    TwoInts twoInts = TwoIntsFunction();
    //  accessing values
    int firstInt = twoInts.first;
    int secondInt = twoInts.second;
}

//  what if you want to send in different types
//  pair can be very useful since it is templated
//  you dont need to define a new struct everytime
//
typedef std::pair<double, double> TwoDoubles;

TwoDoubles TwoDoublesFunction()
{
    return TwoDoubles();
}

void TwoDoublesFunction_Demo()
{
    TwoDoubles twoDoubles = TwoDoublesFunction();
    //  accessing values
    double firstDouble = twoDoubles.first;
    double secondDouble = twoDoubles.second;
}

//  what if three doubles??
//  pair is really convinient for two values
//  but is manageable with three
//
typedef std::pair<double, std::pair<double, double>> ThreeDoubles;

ThreeDoubles ThreeDoublesFunction()
{
    return ThreeDoubles();
}

void ThreeDoublesFunction_Demo()
{
    ThreeDoubles threeDoubles = ThreeDoublesFunction();
    //  accessing values
    //  little ugly but manageable
    //
    double firstDouble = threeDoubles.first;
    double secondDouble = threeDoubles.second.first;
    double thirdDouble = threeDoubles.second.second;
}

//  real life is not so cool, that you want to return just
//  2-3 things, at times you need to return 5 values
//  lets see how the pair looks for 5 arguments
//
class C
{};

//  pretty cool huh, I feel like double right shifted!
//
typedef std::pair<double, std::pair<int,
std::pair< char, std::pair<C,float>>>> FiveRetVals;


FiveRetVals FiveRetValsFunction()
{
    return FiveRetVals();
}

void FiveRetValsFunction_Demo()
{
    FiveRetVals fiveRetVals = FiveRetValsFunction();
    //  accessing values
    //
    //  good
    double first = fiveRetVals.first;
    //  Ok
    int second = fiveRetVals.second.first;
    //  huh??
    char third = fiveRetVals.second.second.first;
    //  what??
    C fourth = fiveRetVals.second.second.second.first;
    //  forget it!
    float fifth = fiveRetVals.second.second.second.second;
}

int main()
{
    TwoIntsFunction_Demo();
    ThreeDoublesFunction_Demo();
    FiveRetValsFunction_Demo();
    return 0;
}

As it is evident pair is not a really good solution if you want to group more that 2 values together. What is really required is an extension of pair that can take in n number of values. BTW if you observe closely you will notice that pair is actually a specialization of tuple.

using tuple


In previous example: ThreeDoubles –> tuple with 3 elements FiveRetVals –> tuple with 5 elements in all of the above cases what we really want is a tuple with varying number of elements such a construct was added to TR1 named tuple

Above code with tuples instead of pairs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <tuple>
//
//
//  for three doubles??
//  pair is really convinient for two values
//  tuple for any number of values
//
typedef std::tr1::tuple<double, double, double> ThreeDoubles;
//
ThreeDoubles ThreeDoublesFunction()
{
    return ThreeDoubles();
}
//
void ThreeDoublesFunction_Demo()
{
    ThreeDoubles threeDoubles = ThreeDoublesFunction();
    //  accessing values
    //  lot better than nested pairs
    //
    double firstDouble = std::tr1::get<0>(threeDoubles);
    double secondDouble = std::tr1::get<1>(threeDoubles);
    double thirdDouble = std::tr1::get<2>(threeDoubles);
}
//
//  real life is not so cool, that you want to return just
//  2-3 things, at times you need to return 5 values
//  lets see how the tuple looks for 5 arguments
//
class C
{};
//  pretty cool huh, I don’t feel like double right shifted!
//
typedef std::tr1::tuple<double, int, char, C, float> FiveRetVals;
//
//
FiveRetVals FiveRetValsFunction()
{
    return FiveRetVals();
}
//
void FiveRetValsFunction_Demo()
{
    FiveRetVals fiveRetVals = FiveRetValsFunction();
    //  accessing values
    //
    //  ok
    double first = std::tr1::get<0>(fiveRetVals);
    //  nice
    int second = std::tr1::get<1>(fiveRetVals);
    //  good
    char third = std::tr1::get<2>(fiveRetVals);
    //  better
    C fourth = std::tr1::get<3>(fiveRetVals);
    //  wow
    float fifth = std::tr1::get<4>(fiveRetVals);;
}
//
//
//
int main()
{
    ThreeDoublesFunction_Demo();
    FiveRetValsFunction_Demo();
    return 0;
}

tuple 101


Primarily tuple started off to address multiple return values, however it can be used to keep grouped data together, as an alternative to locally created structures. At the end we will see an example, although contrived, how to use tuples.

tuple was initially introduced by Jakko Jarvi in boost library ~2001. Later it was selected as one of the components to C++ TR1. As part of C++ TR1 tuple has been placed in std::tr1 namespace. However with C++11 it has been moved to std namespace. The code in this post is written in VS 2010 SP1. Same code can be compiled using GCC 4.6, but you will have to replace std::tr1 with std.

Basic usage of tuple:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// include tuple for tuple related functionality
//
#include <tuple>
#include <iostream>
//
//
// class that is convertible to-and-from double
// to demonstrate the tuple assignment and comparison
//
class Double
{
    double d;
public:
    Double(double d_) : d(d_)
    {}
    //
    Double& operator=(const Double& other)
    {
        if( this != &other )
        {
            d = other.d;
        }
        return *this;
    }
    //
    Double& operator=(double dd)
    {
        this->d = dd;
        return *this;
    }
    //
    operator double() const
    {
        return d;
    }
};
//
//
//
int main()
{
    // tuple of two ints, assigned by combining two values
    // make_tuple is equivalent of std::make_pair
    //
    std::tr1::tuple<int,int> twoInts1 = std::tr1::make_tuple(4,5);
    //
    //
    // direct initialization
    //
    std::tr1::tuple<int,int> twoInts2(4,5);
    //
    //
    // default construction
    std::tr1::tuple<int,int> twoInts3;
    //
    //
    // now twoInts2 is twoInts3
    twoInts2 = twoInts3;
    //
    //
    std::tr1::tuple<double, double> twoDoubles1 =
    std::tr1::make_tuple(4.0,5.0);
    //
    //
    // int to double conversion is allowed
    // if you have conversion defined, tuples of different
    // types can be assigned
    //
    twoDoubles1 = twoInts3;
    //
    //
    // Double class defines conversion to-and-from double
    Double dbl(10.0);
    double d = dbl;
    //
    //
    // due to conversion operator such assignments are feasible
    std::tr1::tuple<Double,Double> doubles =
    std::tr1::make_tuple(9.0,8.0);
    twoDoubles1 = doubles;
    //
    //
    // querying the elements of a tuple:
    // get the first element of
    double d = std::tr1::get<0>(doubles);
    //
    // get the second element
    d = std::tr1::get<1>(doubles);
    //
    //
    // fails to compile as there is no 3rd element for this tuple
    // declaration
    // lots of ugly error messages
    d = std::tr1::get<3>(doubles);
    //
    //
    // checking for equality is supported
    // it's like checking dbl == d
    // if one of the types in tuple does not provide == operator
    // the code fails to compile
    if( doubles == twoDoubles1 )
    {
        std::cout << "values are same" << std::endl;
    }
    //
    //
    if( !(doubles != twoDoubles1) )
    {
        std::cout << "values are same" << std::endl;
    }
    //
    //
    // operator <, >, <=, >= are supported if the types specified
    // in tuple support these
    bool boolean = doubles < twoDoubles1 || doubles > twoDoubles1;
    boolean = doubles <= twoDoubles1 || twoDoubles1 >= doubles;
    //
    //
    // streaming operators are not supported with tuples
    std::cout << twoDoubles1 << std::endl;
    //
    //
    // tie is a utility function can help you get the values out
    // of tuple in local variables easily
    // so its a painless way of getting results from a
    // tuple_returning method.
    int a = 0;
    int b = 0;
    std::tr1::tie(a,b) = twoInts1;
    //
    //
    return 0;
}

contrived implementation using tuple (events-delegate)


As promised here is the contrived implementation of event-delegate mechanism in C++ using tuples.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// implementing a simple event/delegate mechanism
// note this is contrived code to demonstrate the usage of tuple
//
#include <tuple>
#include <vector>
#include <iostream>
//
//
// base class of all delegates
// derive to implement custom notification functionality
class Delegate
{
protected:
    //
    // listener to notification
    virtual int NotifyCore() = 0;
    //
    // second type of listener to notification
    virtual int Notify2Core() = 0;
    //
public:
    Delegate()
    {}
    virtual ~Delegate()
    {}
    //
    int Notify()
    {
        return NotifyCore();
    }
    //
    int Notify2()
    {
        return Notify2Core();
    }
};
//
//
// implementes the both types of listeners to notification
//
class MyDelegate : public Delegate
{
public:
    MyDelegate() : Delegate()
    {}
    virtual ~MyDelegate()
    {}
    //
protected:
    //
    int NotifyCore()
    {
        std::cout << "Calling from MyDelegate::NotifyCore"
        << std::endl;
        return 0;
    }
    //
    int Notify2Core()
    {
        std::cout << "Calling from MyDelegate::Notify2Core"
        << std::endl;
        return 0;
    }
};
//
// class to fire notification to all listeners when some-thing
// interesting happens
//
class Event
{
    // notification function
    // type of function on Delegate that will be called to notify
    // certain event has taken place
    //
    typedef int(Delegate::*NotificationFunc)(void);
    //
    // object on which the notification function will be invoked
    //
    typedef std::tr1::tuple<Delegate*, NotificationFunc>
    CallbackType;
    //
    // n numbers of listeners can be registerd with an event
    //
    std::vector<CallbackType> delegates;
    //
public:
    Event()
    {}
    //
    void RegisterDelegate(Delegate* delg, NotificationFunc f )
    {
        this->delegates.push_back(std::tr1::make_tuple(delg, f));
    }
    //
    void DeRegisterDelegate(Delegate* delg)
    {
        // some code to un-register the delegate
    }
    //
    void Fire()
    {
        // loop over all the delegates and start notifying each of
        // the events
        //
        for(auto iter = this->delegates.begin(),
           end = this->delegates.end();
           iter != end; ++iter )
        {
           // really unreadable, good if you want to show off                //
          (std::tr1::get<0>(*iter)->*(std::tr1::get<1>(*iter)))();
          // here is the equivalent version:
          //
          // Delegate* delg = std::tr1::get<0>(*iter);
          // DelegateFunc f= std::tr1::get<1>(*iter);
          // we have a pointer to member function
          //
          // (delg->*f)();
          //
       }
    }
};

int main()
{
    MyDelegate* delg = new MyDelegate();
    Event evt;
    evt.RegisterDelegate(delg, &Delegate::Notify);
    evt.RegisterDelegate(delg, &Delegate::Notify2);
    evt.Fire();
    delete delg;
    
    return 0;
}

Summary


tuple were introduced to address the maintainability issues with pair they can be used to return multiple values from function can be used to group local data together rather than defining a local struct I hope this will get you started with tuples! happy tuppleing :)