0xbadf00d
0xbadf00d

Reputation: 18178

Passing a value caputring lambda to a function which expects a function pointer

I've got a class foo to which I want to pass an object of type T together with a "deleter-like" function with signature void (T). Currently I'm storing this function as an object m_bar of type typedef void (*bar_t)(T); inside foo.

This works without a problem as long as the function I want to use precisely matches the aforementioned signature. However, if the desired function has an additional parameter and I need to pass a specific non-constant value to it, I've tried to pass a lambda function which captures this value by value; but now I'm receiving the compiler error cannot convert from 'initializer list' to 'foo<int>' in the example code below: `

template<typename T>
struct foo
{
    typedef void (*bar_t)(T);

    foo(T t, bar_t bar)
        : t(t),
          bar(bar)
    {}

    ~foo() { bar(t); }

    T t;
    bar_t bar;
};

void bar1(int x) {}
void bar2(int* x) {}
void bar3(int, int* x) {}

template<typename T, class Bar>
foo<T> make_foo(T t, Bar bar) { return { t, bar }; }

int main()
{
    int x = 47, y = 0;
    
    foo f1(x, bar1);
    foo f2(x, [](int x) { bar2(&x); });
    auto f3a = make_foo(x, [y](int x) { bar3(0, &x); }); // ok
    auto f3b = make_foo(x, [y](int x) { bar3(y, &x); }); // compiler error

}

Can we solve this issue without storing a std::function inside in foo?

Upvotes: 0

Views: 58

Answers (2)

Picaud Vincent
Picaud Vincent

Reputation: 10982

In the very restricted case where you know y value at compile-time this works:

template <typename T>
struct foo
{
  typedef void (*bar_t)(T);

  foo(T t, bar_t bar) : t(t), bar(bar) {}

  ~foo() { bar(t); }

  T t;
  bar_t bar;
};

template <typename T, typename Y, Y y>
auto create_lambda()
{
  return [](T x) { std::cout << x + y << "\n"; };
}

int main()
{
  foo<double> f1(1.5, create_lambda<double, int, 1>());
  foo<double> f2(1.5, create_lambda<double, int, 2>());
}

and prints

3.5
2.5

Upvotes: 0

Jarod42
Jarod42

Reputation: 217085

You basically have 2 choices:

  • Add extra template parameter:

    template<typename T, typename DeleterLike>
    struct foo
    {
        using bar_t = DeleterLike;
    
        foo(T t, bar_t bar)
            : t(t),
              bar(bar)
        {}
    
        ~foo() { bar(t); }
    
        T t;
        bar_t bar;
    };
    
  • use some type erasure on the type (as std::function)

    template<typename T>
    struct foo
    {
        using bar_t = std::function<void(const T&)>;
    
        foo(T t, bar_t bar)
            : t(t),
              bar(bar)
        {}
    
        ~foo() { bar(t); }
    
        T t;
        bar_t bar;
    };
    

Upvotes: 3

Related Questions