[英]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.