Humam Helfawi
Humam Helfawi

Reputation: 20264

Get set of arguments as result of a function

I do not know the name of this pattern or even if it is exist. I need a function that return a set of arguments that would be used in another function. For example, I have this function:

void foo(int a, float b, some_class c);

I want to write a function like this:

//Theoretical code:
arguments get_arguments(){
    return arguments(0,0.0f, some_class());
}

Then call foo like this:

foo(get_arguments());

Is this possible? if yes,How?

Upvotes: 6

Views: 247

Answers (7)

Patrik H
Patrik H

Reputation: 459

std::tuple allows you to pack together the arguments you need for your call to foo. As others already pointed out, there are several ways you can then call your function and some of these might require a more recent standard.

In C++11, you already have std::bind which should suit your needs just fine in your case. Here's an example of how this could be achieved:

#include <iostream>
#include <functional>
#include <tuple>

class MyClass {};

std::tuple<int, float, MyClass> get_arguments()
{
    int a = 0;
    float b = 1.0f;
    MyClass c;
    // ... calculate parameters;
    return std::make_tuple(a, b, c);
}

void foo(int a, float b, MyClass c)
{
    std::cout << "a: " << a << ", b: " << b << "\n";
}

int main(int argc, char* argv[])
{
    // get tuple holding arguments
    auto arguments = get_arguments();

    // Binding arguments and calling directly
    std::bind(
            foo, 
            std::get<0>(arguments), 
            std::get<1>(arguments), 
            std::get<2>(arguments))();

    return 0;
}

You can put the std::bind call into a wrapper, especially when you're using it frequently.

If you end up passing around packs of parameters back and forth, it might make sense to encapsulate them into their own data type or even a function object. This method does not need std::tuple nor std::bind and can thus be used even when you don't have access to C++11.

#include <iostream>

class MyClass {};

void foo(int a, float b, MyClass c)
{
    std::cout << "a: " << a << ", b: " << b << "\n";
}

class FooCaller
{
public:
    void operator()(void) const
    {
        foo(a, b, c);
    }

    int a;
    float b;
    MyClass c;
};

void get_arguments(FooCaller& fooCaller)
{
    // ... calculate parameters;
    fooCaller.a = 0.0f;
    fooCaller.b = 1.0f;
}


int main(int argc, char* argv[])
{
    // Create Instance and calculate/retrieve arguments
    FooCaller fooCaller;
    get_arguments(fooCaller);

    // call 'foo'
    fooCaller();
    return 0;
}

This could be made more generic but this might require some template metaprogramming features introduced in C++11 and later.

Upvotes: 2

max66
max66

Reputation: 66200

Not exactly what you ask... but... just for fun... using std::bind and std::ref, you can call foo() using a binded bar(), in this way

#include <tuple>
#include <complex>
#include <iostream>
#include <functional>

void foo(int a, float b, std::complex<double> c)
 { std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl; }

std::tuple<int, float, std::complex<double>> getArgs1 ()
 { return std::make_tuple(1, 22.22f, std::complex<double>{333.333, 4444.4444}); }

std::tuple<int, float, std::complex<double>> getArgs2 ()
 { return std::make_tuple(4444, 333.333f, std::complex<double>{22.22, 1.1}); }

int main()
 {

   std::tuple<int, float, std::complex<double>>  t;

   auto bar = std::bind(foo,
                        std::ref(std::get<0>(t)),
                        std::ref(std::get<1>(t)),
                        std::ref(std::get<2>(t)));

   t = getArgs1();

   bar();

   t = getArgs2();

   bar();

   return 0;
 }

The output is

a = 1, b = 22.22, c = (333.333,4444.44)
a = 4444, b = 333.333, c = (22.22,1.1)

I repeat: just for fun.

Upvotes: 2

marcinj
marcinj

Reputation: 49986

Sample code using tuple, as I see now it bases on other ideas from this question:

[live]

#include <iostream>
#include <tuple>

struct some_class{};

void foo(int a, float b, some_class c) {
    std::cout << a << " " << b << "\n";
}

decltype(auto) get_arguments() {
   return std::make_tuple(0.1f, 0.2f, some_class{});
}

template<typename T>
void callFoo(const T& args)
{
  foo(std::get<0>(args), std::get<1>(args), std::get<2>(args));
}

int main()
{
    callFoo(get_arguments());
}

Upvotes: 2

Daksh Gupta
Daksh Gupta

Reputation: 7804

Multiple ways to achieve the same things

  1. Plain old C++ way : Return a structure containing multiple variables
  2. C++11 Way : use std::tuples
  3. Boost Way : Use boost::fusion

The best way to declare return type of function as "auto"

Upvotes: 1

TartanLlama
TartanLlama

Reputation: 65620

get_arguments can be implemented with std::make_tuple:

auto get_arguments(){
    return std::make_tuple(0,0.0f, some_class());
}

This will return a std::tuple<int,float,some_class>.

You can call foo with the arguments using std::experimental::apply from C++17:

std::experimental::apply(foo, get_arguments());

Live Demo

There is an implementation of std::experimental::apply here if you need one.


To clean up the call, you could add a forwarding function:

template <typename Tuple>
void foo(Tuple&& t) {
    return std::experimental::apply(
        static_cast<void(*)(int,float,some_class)>(&foo),
        std::forward<Tuple>(t));
}

Then you can use the syntax you desire:

foo(get_arguments());

Live Demo

Upvotes: 6

Revolver_Ocelot
Revolver_Ocelot

Reputation: 8785

If your compiler supports future C++17 addition, you can do this by modifying get_arguments() to return std::tuple and using std::apply:

std::apply(foo, get_arguments())

Upvotes: 7

Some programmer dude
Some programmer dude

Reputation: 409176

It could probably be done by using std::tuple and overloading the foo function to take a tuple and expand it to call the actual foo function.

Upvotes: 9

Related Questions