6

I want to be able to return a function from a class, so that I do not need to if-else through a return type.

I have a class that returns multiple strings. Instead, I want to return multiple functions.

#include <iostream>

class Handler
{
private:

public:
    int handleMessage(int code)
    {
        return code+1;
    }

};

void func1();
void func2();
void func3();

int main (int argc, char *argv[])
{
    Handler handle;
    int code = handle.handleMessage(0);
    if(code == 1)
    {
        func1();
    }
    return 0;
}

void func1(){ std::cout << "1" << std::endl;}
void func2(){ std::cout << "2" << std::endl;}
void func3(){ std::cout << "3" << std::endl;}

What I want is: That the function handleMessage in the class Handler returns something so that in my main application I do not have to use if-else.

So the main looks like this:

function = handle.handleMessage(0); 

And the application will choose which function it will run. for example:

function = handle.handleMessage(0);  //will run func1 
function = handle.handleMessage(1);  //will run func2
Stack Danny
  • 7,754
  • 2
  • 26
  • 55
  • 1
    Use an array of [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function), and let `handleMessage` return an index into that array? And why not put the logic of *calling* the right function inside the `handleMessage` function itself? – Some programmer dude Apr 09 '19 at 08:39
  • I guess you should return a pointer to a function to use it, see the following post https://stackoverflow.com/questions/997821/how-to-make-a-function-return-a-pointer-to-a-function-c there is explained how to, hope that helps – J. Knabenschuh Apr 09 '19 at 08:41
  • @Someprogrammerdude Will look into std::function, the reason why it is not handled in handlemessage itself is because the actual program where I want to use this, can't do that. – JeanClarity Apr 09 '19 at 08:41
  • Your `handleMessage` can still return `int`. Just use a lookup table instead of `if-else` – Michał Walenciak Apr 09 '19 at 08:43
  • I guess higher order functions are not best practice in c++. Anywhere in your code you have to dispatch (If really needed) maybe dataflow like or by control structures. If you dont want it to be placed within your main place it in a seperated layer or function. – J. Knabenschuh Apr 09 '19 at 08:47

7 Answers7

6

There are several ways to do so, the simplest one, you can use an std::function. In this example we returning a lambda function for each case. You can replace it with the functions you just wrote.

class Handler {
public:
    std::function<void()> handleMessage(int code) {
        code = code + 1; // ++code or whatever
        if (code == X) {
            return []() { std::cout << "Cool! I'am x!" << std::endl; };
        } else if (code == Y) {
            return []() { std::cout << "Cool! I'am x!" << std::endl; };
        } else if (...) {
            ...
        } else {
            ....
        }
    }
};

Then your main function becomes:

int main (int argc, char *argv[]) {
    Handler handle;
    const auto func = handle.handleMessage(0);
    func();
    return 0;
}

You can replace the swith/if case statement by an array storing the different functions, like they mentioned in the comments.

If you dont want to pay the extra virtual function call regarding the usage of an std::function, you can use an alias like the answer below or just the auto keyword:

class Handler {
public:
    constexpr auto handleMessage(int code) {
        code = code + 1; // ++code or whatever
        if (code == X) {
            return &func1;
        } else if (code == Y) {
            return &func2;
        } else if (...) {
            ...
        } else {
            ....
        }
    }
};
mohabouje
  • 3,867
  • 2
  • 14
  • 28
  • 5
    Beware that `std::function` comes at a cost of one extra virtual call. – rustyx Apr 09 '19 at 08:46
  • 1
    @rustyx: and generate a class instance, maybe with allocation overhead, maybe with copy itself by passing it, ... – Klaus Apr 09 '19 at 08:58
6

You can modify the member function such that it returns a function pointer, e.g.

using fptr = void (*)();

struct Handler
{
    fptr handleMessage (int code)
    {
       if (code == 0)
          return &func1;
       else if (code == 1)
          return &func2;
       else
          return &func3;
    }

};

This can be invoked as follows

 Handler handle;
 auto f = handle.handleMessage(0);
 f();

Note that the above if-else if-else dispatch isn't ideal. Prefer a data member that stores the function pointers and associates them with a code, e.g. using a std::unordered_map.

Note that when you need to return stateful function objects in the future, this approach will fail. Then, you need to embrace std::function which is able to wrap lambdas with closures or custom types with an operator() overload.

lubgr
  • 37,368
  • 3
  • 66
  • 117
1

std::function is a powerful tool. The tiny brother is a simple function pointer. I transformed MCVE respectively to return a function pointer:

#include <iostream>

typedef void (*FuncPtr)();

void func1();
void func2();
void func3();
void funcError();

class Handler
{
private:

public:
    FuncPtr handleMessage(int code)
    {
      switch (code + 1) {
        case 1: return &func1;
        case 2: return &func2;
        case 3: return &func3;
        default: return &funcError;
      }
    }

};

int main (int argc, char *argv[])
{
    Handler handle;
    FuncPtr pFunc = handle.handleMessage(0);
    pFunc();
    return 0;
}

void func1(){ std::cout << "1" << std::endl;}
void func2(){ std::cout << "2" << std::endl;}
void func3(){ std::cout << "3" << std::endl;}
void funcError(){ std::cout << "ERROR!" << std::endl;}

Output:

1

Live Demo on coliru

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
1

You can return a function with return_type(*function_name)(argument_type1, argument_type2...) so a function that looks like:

double f(int a, int b);

has the name double(*f)(int, int).

Worth mentioning is C++11's std::function which requires the <functional> header. It has a more intuitive usage: std::function<double(int, int)> but also adds a bit of overhead.

I would also like to suggest the usage of C++17's std::optional as for the case when the variable code goes out of bounds. This implementation requires the <optional> header.

std::optional<void(*)()> handleMessage(int code){
    switch (code) {
    case 0: return std::optional(func1);
    case 1: return std::optional(func2);
    case 2: return std::optional(func3);
    }
    return std::nullopt; //empty
}

usage in main looks like the following:

Handler handle;
auto func = handle.handleMessage(0);
if (func.has_value()) {
    func.value()();
}

as this allows to check if func.has_value() which is quite convenient.

Stack Danny
  • 7,754
  • 2
  • 26
  • 55
1

Use an array of functions.

void func1(){  std::cout << "1" << std::endl; } 
void func2(){  std::cout << "2" << std::endl; } 
void func3(){  std::cout << "3" << std::endl; } 

typedef void (* func ) () ;

class Handler { 
  public:
      func handleMessage(int code)const{  
            static const func  F[] = { func1, func2, func3 };
            return F[ code ];
       }
};

int main() 
{
     Handler handler;
     func f = handler.handleMessage(0); // returns func1
     f();
}

live example

0

you can map the ints to a function or lambda, but read befor what at() does and what happens if the key is not found!!

void function1()
{
    std::cout << "q1" << std::endl;
}
void function2()
{
    std::cout << "q2" << std::endl;
}

int main(int argc, char* argv[])
{
    std::map<int, std::function<void(void)>> map;
    map.insert(std::make_pair(1, function1));
    map.insert(std::make_pair(1, function2));
    map.at(1)();
ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
0

I would like to offer solution without any if-else block. You just need to templatize your Handler::handleMessage function. Something like this:

// Class declaration
class Handler
{
private:

public:
  template<int code>
  void handleMessage();
};

and specialize the function template for particular codes:

// Function template specializations.
template<>
void Handler::handleMessage<1>()
{
  std::cout << "1" << std::endl;
}
template<>
void Handler::handleMessage<2>()
{
  std::cout << "2" << std::endl;;
}
template<>
void Handler::handleMessage<3>()
{
  std::cout << "3" << std::endl;;
}

// All cases, except 1, 2 and 3
template<int code>
void Handler::handleMessage()
{
  std::cout << "Anything else" << std::endl;;
}

The usage may look like:

Handler h;
h.handleMessage<1>();   // Prints 1
h.handleMessage<2>();   // Prints 2
h.handleMessage<3>();   // Prints 3
h.handleMessage<323>(); // Prints 'Anything else'
vahancho
  • 20,808
  • 3
  • 47
  • 55