4

This question was closed as exact duplicate since I chose a misleading question title. It was not wrong but suggested an issue often discussed, e.g. in this question. Since the content is about a more specific topic never covered on Stackoverflow I would like the question to be reopened. This happened now, so here goes the question.

I have given a function expecting three integer values as parameters length(int x, int y, int z);. I cannot modify this function, e.g. to accept a struct or tuple of whatever as single parameter.

Is there a way in C++ to write another function which can be used as single argument to the function above, like length(arguments());?

Anyhow the return type of that function arguments(); seems to need to be int, int, int. But as far as far as I know I can't define and use functions like this in C++. I know that I could return a list, a tuple, a struct or a class by arguments(). The question was closed because some people thought I would have asked about this. But the difficult part is to pass the tuple, or struct, or whatever as the three given integer parameters.

Is this possible and if yes, how is that possible in C++? A solution making use of C++11 would be fine.

Community
  • 1
  • 1
danijar
  • 32,406
  • 45
  • 166
  • 297

6 Answers6

8

I don't think there is any direct way of doing what you want, but here is a C++11 technique that I use in several places of my code. The basic idea is to use a template function which I've called call_on_tuple to take a function argument f as well as a tuple of further arguments, expand the tuple and call the function on the expanded list of arguments:

template <typename Fun, typename... Args, unsigned... Is>
typename std::result_of<Fun(Args...)>::type
call_on_tuple(Fun&& f, std::tuple<Args...>&& tup, indices<Is...>)
{ return f(std::get<Is>(tup)...); }

So the idea is that instead of calling

length(arguments());

you would call

call_on_tuple(length,arguments());

This assumes that arguments() is changed so it returns a std::tuple<int,int,int> (this is basically the idea from the question you cited).

Now the difficult part is how to get the Is... argument pack, which is a pack of integers 0,1,2,... used to number the elements of the tuple.

If you are sure you'll always have three arguments, you could use 0,1,2 literally, but if the ambition is to make this work for any n-ary function, we need another trick, which has been described by other posts, for example in several answers to this post.

It's a trick to transform the number of arguments, i.e. sizeof...(Args) into a list of integers 0,1,...,sizeof...(Args):

I'll put this trick and the implementation of call_on_tuple in a namespace detail:

namespace detail {

  template <unsigned... Is>
  struct indices
  { };

  template <unsigned N, unsigned... Is>
  struct index_maker : index_maker<N-1,N-1,Is...>
  { };

  template <unsigned... Is>
  struct index_maker<0,Is...>
  { typedef indices<Is...> type; };


  template <typename Fun, typename... Args, unsigned... Is>
  typename std::enable_if<!std::is_void<typename std::result_of<Fun(Args...)>::type>::value,
              typename std::result_of<Fun(Args...)>::type>::type
  call_on_tuple(Fun&& f, std::tuple<Args...>&& tup, indices<Is...>)
  { return f(std::get<Is>(tup)...); }
}

Now the actual function call_on_tuple is defined in global namespace like this:

template <typename Fun, typename... Args>
typename std::enable_if<!std::is_void<typename std::result_of<Fun(Args...)>::type>::value,
            typename std::result_of<Fun(Args...)>::type>::type
call_on_tuple(Fun&& f, std::tuple<Args...>&& tup)
{
  using std::tuple;
  using std::forward;
  using detail::index_maker;

  return detail::call_on_tuple
    (forward<Fun>(f),forward<tuple<Args...>>(tup),typename index_maker<sizeof...(Args)>::type());
}

It basically calls detail::index_maker to generate the list of increasing integers and then calls detail::call_on_tuple with that.

As a result, you can do this:

int length(int x, int y, int z)
{ return x + y + z; }

std::tuple<int,int,int> arguments()
{ return std::tuple<int,int,int> { 1 , 2 , 3 }; }

int main()
{
  std::cout << call_on_tuple(length,arguments()) << std::endl;
  return 0;
}

which is hopefully close enough to what you needed.

Note. I have also added an enable_if to ensure this is only used with functions f that actually return a value. You can readily make another implementation for functions that return void.

Sorry again for closing your question prematurely.

PS. You'll need to add the following include statements to test this:

#include <tuple>
#include <type_traits>
#include <iostream>
Community
  • 1
  • 1
jogojapan
  • 68,383
  • 11
  • 101
  • 131
  • Thanks for sharing your idea and code. I can even use a modified version of your approach to simplify some other logic of my implementation. – danijar Dec 30 '12 at 10:42
4

It is not possible, C++ does not allow to provide 3 return values natively that can be used as 3 separate input arguments for another function.

But there are 'tricks' to return multiple values. Although none of these provide a perfect solution for your question, as they are not able to be used as a single argument to length() without modifying length().

Use a container object, like a struct, tuple or class

typedef struct { int a,b,c; } myContainer;

myContainer arguments(int x, int y, int z) {
  myContainer result;
  result.a = 1;
  // etc
  return result;
}

myContainer c = arguments(x, y, z);
length(c.a, c.b, c.c);

The trick is to overload the length() function, so it looks like you can use it with a single argument:

void length(myContainer c) {
  length(c.a, c.b, c.c);
}

length(arguments());

Of course you could optimize it further, by using inline, macros, and what not.

I know it is still not exactly what you want, but I think this is the closest approach.

Veger
  • 37,240
  • 11
  • 105
  • 116
  • I am sorry that my formulation was unclear, but I cannot modify the `length` function. I already updated my question to make that clear. – danijar Dec 30 '12 at 09:45
  • 2
    @sharethis I have updated my answer again. Using function overloading you can *fake* the single argument length function... – Veger Dec 30 '12 at 10:26
  • Overloading is a nice and simple (!) approach to this question, too. I gave you an upvote since jogojapan's answer helped me more. But this is case specific I guess. – danijar Dec 30 '12 at 10:44
1

You need to declare a struct { int a, b, c; } or something similar (a class would work too) - I take it you have been programming python or php or some such.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
1

Most programming languages would do this through some form of adapter function. That is a function that will take as argument the function to call (here length) and the arguments to call it with. You can probably build something similar in C++ with templates. Look at the functional header to get inspiration.

A language that natively provides what you are looking for is Perl. You can write:

sub arguments {
    return 1, 2, 3;
}
sub length {
    my ($p1, $p2, $p3) = @_;
    # … Work with $p1, $p2 and $p3
}
length(arguments());
kmkaplan
  • 18,655
  • 4
  • 51
  • 65
-1

Pass in the arguments by reference so you can change them without returning or return a struct. You can only return a single value from a function.

hari
  • 9,439
  • 27
  • 76
  • 110
-2

we can only return one value. but in case you want to return multiple value you can use an array or define a object or a structure

    int* arguments() {
        int x[1,4,6] 
        return x;
    };

    void length(int i[]);

    length(arguments());
Sar009
  • 2,166
  • 5
  • 29
  • 48