简体   繁体   English

将可变参数函数参数转发到 std::function 对象

[英]Forwarding variadic function parameters to a std::function object

so I am trying to create a queue that stores functions to be called later.所以我试图创建一个队列来存储稍后调用的函数。 In order to do this I must create a std::function object to place into the queue from any function passed.为了做到这一点,我必须创建一个 std::function 对象,以便将任何传递的函数放入队列中。 The issue is coming where I create this object and it can be called, but it seems the passed parameters arent being stored properly (how I want them to be)问题出现在我创建这个对象并且可以调用它的地方,但似乎传递的参数没有正确存储(我希望它们如何)

The main issue is all inside the member template function push(Ret (&)(...), &&...) I tried inserting a call to the temp created function above the push(temp) and everything worked as expected.主要问题都在成员模板函数push(Ret (&)(...), &&...)我尝试在push(temp)上方插入对临时创建的函数的调用,一切都按预期工作。 But when I try and access this function off of the queue it seems my parameters forwarded have been overriden.但是当我尝试从队列中访问此函数时,似乎我转发的参数已被覆盖。

class thread_queue {

  public:
    thread_queue() = default;
    void push(std::function<void()> func) { thread_q_.push(func); } 

    template <typename Ret, typename ... Params>
    void push(Ret (&func)(Params...), Params&&... params) {
      std::function<void()> temp = [&]() { // TO DO : FIX HERE
        func(std::forward<Params>(params)...);
      };
      push(temp);
    }

    void pop() { thread_q_.pop(); }

    std::function<void()> front() { return thread_q_.front(); }

  private:
    std::queue<std::function<void()>> thread_q_; 

};

void func1() {
  std::cout << "Inside of func1" << std::endl;
}

void func2(int a) {
  std::cout << "Inside of func2 " << a << std::endl;
}

int func3() {
  std::cout << "Inside of func3" << std::endl;
  return 5;
}

int func4(int a, int b) {
  std::cout << "Inside of func4 " << a + b << std::endl;
  return a + b;
}

int main() {

  thread_queue test;
  test.push(func1);
  test.push(func2, 10);
  test.push(func3);
  test.push(func4, 1, 8);

  test.front()();
  test.pop();
  test.front()();
  test.pop();
  test.front()();
  test.pop();
  test.front()();
  test.pop();


  return 0;
}

So with this i want to get所以有了这个我想得到

Inside of func1 Inside of func2 10 Inside of func3 Inside of func4 9 but instead I am getting Inside of func1 Inside of func2 8 Inside of func3 Inside of func4 9 Inside of func1 Inside of func2 10 Inside of func3 Inside of func2 10 Inside of func3 Inside of func4 9 Inside of func3 Inside of func4 9但我得到的Inside of func1 Inside of func2 8 Inside of func3 Inside of func2 8 Inside of func3 Inside of func4 9 Inside of func3 Inside of func4 9

Some further notes : I would like to try and forward the passed params so if i decide to pass some large object there will be less time wasted than copying it.一些进一步的说明:我想尝试转发传递的参数,所以如果我决定传递一些大对象,那么浪费的时间会比复制它少。 I have also considered instead somehow using shared_ptr or unique_ptr on the parameters, but havent tested this as I would like to avoid this if possible.我还考虑以某种方式在参数上使用 shared_ptr 或 unique_ptr,但尚未对此进行测试,因为我想尽可能避免这种情况。 Thank you.谢谢你。

Edit: It seems my issue might have something to do with rvalue references being passed, because when I tried making 10, 1, and 8 into lvalues (by setting them as variables in main) it worked as expected.编辑:看来我的问题可能与传递的右值引用有关,因为当我尝试将 10、1 和 8 转换为左值(通过将它们设置为 main 中的变量)时,它按预期工作。 Looking more into this now现在更多地研究这个

Your queue is holding references to the arguments so the argument must still be in scope when the function is called.您的队列保存对参数的引用,因此在调用函数时参数必须仍在范围内。 eg例如

{
   int value = 1;
   test.push(func2, value);
}
test.front()(); //Invalid, value is out of scope

int value2 = 2;
test.push(func2, value2);
test.front()(); //Ok, value2 is still in scope

test.push(func2, 3);
test.front()(); //Invalid, the temporary that was holding 3 is out of scope

If you want the function to always be valid you will need to store the arguments by value.如果您希望函数始终有效,则需要按值存储参数。 Capturing parameter packs by value in a lambda isn't straight forward, however, we can use std::bind instead of a lambda.在 lambda 中按值捕获参数包并不简单,但是,我们可以使用 std::bind 代替 lambda。

#include <functional>
#include <queue>
#include <iostream>

class thread_queue {

  public:
    thread_queue() = default;
    void push(std::function<void()> func) { thread_q_.push(func); } 

    template <typename Ret, typename ... Params>
    void push(Ret (&func)(Params...), Params&&... params) {
      std::function<void()> temp = std::bind(func, std::forward<Params>(params)...);
      push(std::move(temp));
    }

    void pop() { thread_q_.pop(); }

    std::function<void()> front() { return thread_q_.front(); } //could avoid a copy 
      //by returning a reference. Would be more consistent with other containers.

  private:
    std::queue<std::function<void()>> thread_q_; 

};

void func1() {
  std::cout << "Inside of func1" << std::endl;
}

void func2(int a) {
  std::cout << "Inside of func2 " << a << std::endl;
}

int func3() {
  std::cout << "Inside of func3" << std::endl;
  return 5;
}

int func4(int a, int b) {
  std::cout << "Inside of func4 " << a + b << std::endl;
  return a + b;
}

int main() {

  thread_queue test;
  test.push(func1);
  test.push(func2, 10);
  test.push(func3);
  test.push(func4, 1, 8);

  test.front()();
  test.pop();
  test.front()();
  test.pop();
  test.front()();
  test.pop();
  test.front()();
  test.pop();


  return 0;
}

UPDATE: If you have move only parameters std::bind will not work as the object it returns can be called multiple times and thus can't move the stored parameters.更新:如果您只移动参数 std::bind 将不起作用,因为它返回的对象可以被多次调用,因此无法移动存储的参数。 Another problem with move only parameters is that std::function requires the function object passed to it to be copyable.仅移动参数的另一个问题是 std::function 要求传递给它的函数对象是可复制的。 One why of solving these problems is to store a std::shared_ptr in the std::function eg解决这些问题的一个原因是在 std::function 中存储一个 std::shared_ptr 例如

#include <functional>
#include <queue>
#include <iostream>
#include <tuple>
#include <memory>

class thread_queue {
    template <typename Ret, typename... Params>
    struct callable {
        Ret (&func)(Params...);
        std::tuple<Params...> params;

        template<typename... Params2>
        callable(Ret (&func1)(Params...), Params2&&... params) :
            func(func1),
            params{std::forward<Params2>(params)...}
        {}

        void operator()() {
            std::apply(func, std::move(params));
        }
    };
  public:
    thread_queue() = default;
    void push(std::function<void()> func) { thread_q_.push(std::move(func)); } 

    template <typename Ret, typename... Params>
    void push(Ret (&func)(Params...), Params&&... params) {
         auto data = std::make_shared<callable<Ret, Params...>>(func, std::forward<Params>(params)...);
      thread_q_.push(std::function<void()>{
                [data = std::move(data)]() {
                    (*data)();
                }
            });
    }

    void pop() { thread_q_.pop(); }

    std::function<void()>& front() { return thread_q_.front(); }
  private:
    std::queue<std::function<void()>> thread_q_; 

};

struct MoveOnly {
    MoveOnly() {}
    MoveOnly(MoveOnly&&) {}
};

void func5(MoveOnly m) {
    std::cout << "func5\n";
}

int main() {
    thread_queue test;
    test.push(func5, MoveOnly{});
    test.front()();
    test.pop();

    return 0;
}

Another likely faster solution is to write your own version of std::function.另一个可能更快的解决方案是编写您自己的 std::function 版本。 The following is a minimal example of this and doesn't include small buffer optimization.下面是一个最小的例子,不包括小缓冲区优化。

#include <functional>
#include <queue>
#include <iostream>
#include <tuple>
#include <memory>

template<class T>
class move_only_function;

template<class R, class... Args>
class move_only_function<R(Args...)>
{
    struct base_callable
    {
        virtual R operator()(Args... args) = 0;
        virtual ~base_callable() = default;
    };

    template<class F>
    struct callable : public base_callable
    {
        F func;
        callable(const F& f) : func(f) {}
        callable(F&& f) : func(std::move(f)) {}

        virtual R operator()(Args... args) override
        {
            return static_cast<R>(func(args...));
        }
    };

    std::unique_ptr<base_callable> func;
public:
    move_only_function(move_only_function&& other) : func(std::move(other.func)) {}

    template<class F>
    move_only_function(F&& f) : func(std::make_unique<callable<F>>(std::forward<F>(f))) {}

    template<class... Args2>
    R operator()(Args2&&... args)
    {
        return (*func)(std::forward<Args2>(args)...);
    }
};

class thread_queue {
  public:
    thread_queue() = default;
    void push(move_only_function<void()> func) { thread_q_.push(std::move(func)); } 

    template <typename Ret, typename ... Params>
    void push(Ret (&func)(Params...), Params&&... params) {
      thread_q_.push(move_only_function<void()>{
            [func, tup=std::make_tuple(std::forward<Params>(params)...)]() mutable {
                return std::apply(func, std::move(tup));
            }});
    }

    void pop() { thread_q_.pop(); }

    move_only_function<void()>& front() { return thread_q_.front(); }
  private:
    std::queue<move_only_function<void()>> thread_q_; 

};

struct MoveOnly {
    MoveOnly() {}
    MoveOnly(MoveOnly&&) {}
};

void func5(MoveOnly m) {
    std::cout << "func5\n";
}

int main() {
    thread_queue test;
    test.push(func5, MoveOnly{});
    test.front()();
    test.pop();

    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM