user3786992
user3786992

Reputation: 167

Templated optional argument constructor with void parameter callback

I'm looking to write a class such than it can store a piece of data and pass it through a lambda. I'd also like to be able to have an option to not pass a value in the constructor and have the constructor generate with a lambda without a parameter. I'm OK, with still having a piece of data on the class.

I've tried using optionals and using a default template parameter of type void but couldn't get it to work. The closest thing I got was this.

template<class T = void>
class X {
   std::optional<T> d;       // data present or garbage data
   std::function<void(T)> f; // parameter = type of data or void if no arg provided
public:
   X(std::optional<T> a, std::function<void(T)> b) : d{a}, f{b} {}
   a() {
      if (d.has_value()) f(d);
      else f();
   }
}

Hoping the constructor work like

X(5, [&](int x) {}); // where the int type is implied from the constructor

OR

X([&](){}); // where the lack of parameter implies the function parameter is void

Thank you.

EDIT: provided an example of an attempt to generate the class myself EDIT 2: attempted to clarified the need to store and call the function at a later date.

Upvotes: 2

Views: 265

Answers (1)

NutCracker
NutCracker

Reputation: 12263

You can use something like this:

#include <iostream>
#include <functional>

void foo() {
    std::cout << "void foo()" << std::endl;
}

void bar(int x) {
    std::cout << "void foo(int x) with parameter x = " << x << std::endl;
}

class X {
public:
    template <typename F, typename ... Args>
    X(F&& f, Args&&... args) {
        std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
    }
};

int main() {
    X a(foo);
    X b(bar, 1);
    X c([&](int x, int y) {
        std::cout << "lambda with parameters x = " << x << " y = " << y << std::endl;
    }, 1, 2);

    return 0;
}

Check live

Basically, by using provided class X you can send whatever function you want (free function, lambda, etc.) and pass any number of parameters you want.

UPDATE

According to the OP's comment and wish to store the lambda and arguments to a class member and call it later, here is the updated code:

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

template <typename F, typename ... Args>
class X {
public:
    X(F&& f, Args&&... args)
        : f_(std::forward<F>(f)),
          args_(std::forward<Args>(args)...)
    {}

    void call() {
        std::apply(f_, args_);
    }

private:
    F f_;
    std::tuple<Args...> args_;
};

int main() {
    X c([&](int x, int y) {
        std::cout << "lambda with parameters x = " << x << " y = " << y << std::endl;
    }, 1, 2);

    c.call();

    return 0;
}

Check live

Upvotes: 3

Related Questions