Boost :: Tuples vs Structs for return values

I'm trying to tie up tuples (thanks @litb), and the general suggestion for using them is for functions that return a value> 1.

This is what I usually used for the structure, and I cannot understand the advantages of tuples in this case - it seems to be an error-prone approach to terminally lazy.

Borrowing an example , I would use this

struct divide_result { int quotient; int remainder; }; 

Using a tuple, you will have

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

But, without reading the code of the function you are calling (or the comments, if you are dumb enough to trust them), you don't know which int is private and vice versa. It looks like...

 struct divide_result { int results[2]; // 0 is quotient, 1 is remainder, I think }; 

... which would not fill me with confidence.

So what are the advantages of tuples over structures that compensate for ambiguity?

+43
c ++ tuples boost-tuples
Jan 03 '09 at 20:57
source share
9 answers

tuples

I think I agree with you that the problem with which position corresponds to the variable that may introduce confusion. But I think there are two sides. One of them is the call-side, and the other is the call side:

 int remainder; int quotient; tie(quotient, remainder) = div(10, 3); 

I think itโ€™s clear what we got, but it can become confusing if you need to return more values โ€‹โ€‹at once. As soon as the callerโ€™s programmer has looked through the div documentation, he will know which position is and can write the effective code. As a rule, I would say not to return more than 4 values โ€‹โ€‹at once. For anything, prefer structure.

output parameters

Of course, you can use the output parameters:

 int remainder; int quotient; div(10, 3, &quotient, &remainder); 

Now I think this illustrates how tuples are better than output parameters. We mixed the input of the div with its output, but did not get any benefits. Even worse, we leave the reader of this code with a doubt about what the actual return value of the div be might be. There are great examples when output options are useful. In my opinion, you should use them only when you have no other way, because the return value has already been completed and cannot be changed to either a tuple or a structure. operator>> is a good example of where you use the output parameters, because the return value is already reserved for the stream, so you can bind operator>> calls. If you are not associated with operators and the context is not crystal clear, I recommend that you use pointers to signal on the call side that the object is actually used as an output parameter, in addition to comments where necessary.

returns struct

The third option is to use struct:

 div_result d = div(10, 3); 

I think that definitely wins the reward for clarity . But note that you still need to access the result inside this structure, and the result is not "exposed" in the table, as was the case with the output parameters and the tuple used with tie .

I think that today it is important to make everything as general as possible. So let's say you have a function that can print tuples. You can just do

 cout << div(10, 3); 

And display your result. I think that tuples, on the other hand, clearly win for their universal nature. When doing this with div_result, you need to overload operator </ or display each element separately.

+24
Jan 03 '09 at 21:53
source share

Another option is to use a Boost Fusion card (code not verified):

 struct quotient; struct remainder; using boost::fusion::map; using boost::fusion::pair; typedef map< pair< quotient, int >, pair< remainder, int > > div_result; 

You can access the results relatively intuitively:

 using boost::fusion::at_key; res = div(x, y); int q = at_key<quotient>(res); int r = at_key<remainder>(res); 

There are other benefits, such as the ability to repeat field fields, etc. etc. For more information see doco .

+10
Jan 03 '09 at 22:48
source share

With tuples, you can use tie , which is sometimes very useful: std::tr1::tie (quotient, remainder) = do_division (); . It is not so simple with structs. Secondly, when using the template code, it is sometimes easier to rely on pairs than to add another typedef for the structure type.

And if the types are different, then the pair / tuple is really no worse than the structure. Think, for example, pair<int, bool> readFromFile() , where int is the number of bytes read, and bool is whether eof hit. Adding structure in this case seems redundant to me, especially since there is no ambiguity.

+5
Jan 03 '09 at 21:05
source share

Tuples are very useful in languages โ€‹โ€‹like ML or Haskell.

In C ++, their syntax makes them less elegant, but can be useful in the following situations:

  • you have a function that should return more than one argument, but the result is "local" to the caller and the callee; you don't want to define a structure just for that

  • you can use the bind function to make a very limited form of matching a ร  la ML pattern that is more elegant than using a structure for the same purpose.

  • they come with predefined <operators, which can be time-saving.

+4
Jan 03 '09 at 21:46
source share

I usually use tuples in combination with typedefs to at least partially eliminate the "nameless tuple" problem. For example, if I had a grid structure, then:

 //row is element 0 column is element 1 typedef boost::tuple<int,int> grid_index; 

Then I use the named type as:

 grid_index find(const grid& g, int value); 

This is a somewhat contrived example, but I think that most of the time it falls into a happy environment between readability, clarity, and ease of use.

Or in your example:

 //quotient is element 0 remainder is element 1 typedef boost:tuple<int,int> div_result; div_result div(int dividend,int divisor); 
+3
Jan 03 '09 at 22:27
source share

One of the features of tuples that you don't have with structs is in their initialization. Consider the following:

 struct A { int a; int b; }; 

If you are not writing an equivalent or the make_tuple constructor, then to use this structure as an input parameter, you first need to create a temporary object:

 void foo (A const & a) { // ... } void bar () { A dummy = { 1, 2 }; foo (dummy); } 

Not too bad, however, note that the service adds a new member to our structure for any reason:

 struct A { int a; int b; int c; }; 

Aggregate initialization rules actually mean that our code will continue to compile unchanged. Therefore, we must look for all the applications of this structure and update them without any help from the compiler.

Contrast this with a tuple:

 typedef boost::tuple<int, int, int> Tuple; enum { A , B , C }; void foo (Tuple const & p) { } void bar () { foo (boost::make_tuple (1, 2)); // Compile error } 

The compiler cannot initialize "Tuple" with the result of make_tuple , and therefore generates an error that allows you to specify the correct values โ€‹โ€‹for the third parameter.

Finally, another advantage of tuples is that they allow you to write code that iterates over each value. This is simply not possible using struct.

 void incrementValues (boost::tuples::null_type) {} template <typename Tuple_> void incrementValues (Tuple_ & tuple) { // ... ++tuple.get_head (); incrementValues (tuple.get_tail ()); } 
+3
Jun 25 '09 at 10:04
source share

Prevents code truncation by many structure definitions. This is easier for the person writing the code, and for others, using it when you simply document what each element in the tuple is, instead of writing your own structures / forcing people to look for a definition of the structure.

+2
Jan 03 '09 at 21:08
source share

Tuples are easier to write - no need to create a new structure for each function that returns something. The documentation of where it goes goes to the functional documentation, which will be necessary in any case. To use this function, in any case, you will need to read the documentation of the functions, and there the tuple will be explained.

+2
Jan 03 '09 at 21:19
source share

I agree with you 100% Roddy.

To return multiple values โ€‹โ€‹from a method, you have several parameters other than tuples, which best depends on your case:

  • Create a new structure. This is good when the multiple values โ€‹โ€‹you return are related and it needs to create a new abstraction. For example, I think that "divide_result" is a good general abstraction, and passing this object around makes your code much clearer than just skipping the nameless tuple. Then you can create methods that work on this new type, convert it to other numeric types, etc.

  • Using the "Out" options. Pass several parameters by reference and return several values, assigning each parameter to out. This is useful if your method returns several unrelated pieces of information. Creating a new structure would be superfluous in this case, and with the Out parameters you underline this point, plus each element gets the name that it deserves.

Tuples are evil.

-one
Jan 03 '09 at 21:24
source share



All Articles