0

How can I create a variable of generalised std::function type that can take any callable objects? I can't use variadic templates because it'll create a family of different types, while I need a single type, so that I can take different [&] lambdas to a same array.

I did it with functors (example below) that produce std::function<void()>, but for this I have to create a functor for every set of arguments that my functions use. Now I want to use lambdas to bind arguments, but their type is tricky and I can't get different lambdas to the same array.

I understand that this will be very unsafe.

#include <iostream>
#include <vector>
#include <string>

using ChoiceArray = std::vector<std::function<void()>>;
int GetInt(int restriction);

class MenuFunctor {
private:
    std::string output;
    ChoiceArray arrayOfFunctions;
public:
    MenuFunctor(std::string n_output, ChoiceArray n_arrayOfFunctions)
        : output(n_output), arrayOfFunctions(n_arrayOfFunctions)
    { }
    void operator()() const {
        int selection;
        std::cout << output;
        selection = GetInt(int(arrayOfFunctions.size()));
        arrayOfFunctions[selection]();
    }
};

class OperationFunctor {
private:
    std::function<void(std::vector<std::string>*)> func;
    std::vector<std::string>* container;
public:
    OperationFunctor(std::function<void(std::vector<std::string>*)> n_func, std::vector<std::string>* n_container)
        : func(n_func), container(n_container)
    { }
    void operator()() const {
        func(container);
    }
};

void Func1(std::vector<std::string>* container);
void Func2(std::vector<std::string>* container);
void Func3(std::vector<std::string>* container);

int main() {
    std::vector<std::string> container;
    std::vector<std::string>* containerAddress = &container;
    OperationFunctor f1(Func1, containerAddress);
    OperationFunctor f2(Func2, containerAddress);
    OperationFunctor f3(Func3, containerAddress);
    ChoiceArray Array = {f1, f2, f3};
    MenuFunctor Start("input 0-2\n", Array);
    Start(); // Works
    return 0;
}

Also, I tried to take std::vector<std::string> container for Func1-Func3 by reference, but it didn't work, so I went with pointers. It has something to do with perfect forwarding?

Martian
  • 227
  • 1
  • 15
  • How do you intend to call one of these functions? Related: https://stackoverflow.com/questions/45715219/store-functions-with-different-signatures-in-a-map/45718187#45718187 – Caleth May 15 '20 at 08:18
  • @Caleth I will bind all arguments and will call it as `arrayOfFunctions[i]()`. – Martian May 15 '20 at 08:20
  • 1
    Not sure what you want, but `ChoiceArray Array = {[&](){Func1(&container);}, [&](){Func2(&container);}, [&](){Func3(&container);}};` would get rid of `OperationFunctor`. – Jarod42 May 15 '20 at 08:24
  • @Jarod42 no, `ChoiceArray` consists of `std::function` objects, and lambdas are different objects, so it won't work. – Martian May 15 '20 at 08:29
  • 2
    Work as expected [here](https://coliru.stacked-crooked.com/a/7bd59b3dac6963ad). Lambdas are indeed not `std::function`, but are convertible to. – Jarod42 May 15 '20 at 08:33
  • @Jarod42 oh, it works even with `&`. Somehow I thought that `[&]` lambda is some struct of `std::function` + bound arguments. Thank you! – Martian May 15 '20 at 08:39
  • `std::function` can contain any callable of the right signature. A lambda is an instance of a unique `struct /*un-nameable*/ { auto operator()(/*args*/) const { /*body*/ } /*captures as members*/ };` – Caleth May 15 '20 at 08:42

1 Answers1

1

Just stuff lambdas into std::function<void()>.

using OperationFunctor = std::function<void()>;
using ChoiceArray = std::vector<OperationFunctor>;

int GetInt(int restriction);

class MenuFunctor {
private:
    std::string output;
    ChoiceArray arrayOfFunctions;
public:
    MenuFunctor(std::string n_output, ChoiceArray n_arrayOfFunctions)
        : output(n_output), arrayOfFunctions(n_arrayOfFunctions)
    { }
    void operator()() const {
        int selection;
        std::cout << output;
        selection = GetInt(int(arrayOfFunctions.size()));
        arrayOfFunctions[selection]();
    }
};

void Func1(std::vector<std::string>* container);
void Func2(std::vector<std::string>* container);
void Func3(std::vector<std::string>* container);

int main() {
    std::vector<std::string> container;
    std::vector<std::string>* containerAddress = &container;

    OperationFunctor f1([containerAddress]{ Func1(containerAddress) });
    OperationFunctor f2([containerAddress]{ Func2(containerAddress) });
    OperationFunctor f3([containerAddress]{ Func3(containerAddress) });
    ChoiceArray Array = {f1, f2, f3};
    MenuFunctor Start("input 0-2\n", Array);
    Start(); // Works
    return 0;
}
Caleth
  • 52,200
  • 2
  • 44
  • 75