[英]How does std::thread store variadic arguments passed through its constructor?
Let's say I declare a thread with the following code:假设我使用以下代码声明了一个线程:
#include <thread>
#include <iostream>
void printStuff(const char* c, long x) {
std::cout << x << " bottles of " << c << " on the wall\n";
}
int main()
{
std::thread t(printStuff, "beer", 900000000);
t.join();
}
How are the arguments printStuff, "beer," and 900000000 stored in the thread?参数 printStuff、“beer”和 900000000 是如何存储在线程中的?
I know they are using a variadic template, where you first pass in a function and then a parameter pack of arguments.我知道他们正在使用可变参数模板,您首先传入一个函数,然后传入一个参数包。 I am confused on how they forward all these template arguments, and then somehow call the inputted function with all the arguments when join or detach is called.
我对它们如何转发所有这些模板参数感到困惑,然后在调用 join 或 detach 时以某种方式调用带有所有参数的输入函数。
std::function has similar functionality where when you call std::bind it will store a function and its arguments inside the object, and then when you call the std::function object it will just execute the bound function with its arguments. std::function 具有类似的功能,当您调用 std::bind 时,它将在对象中存储一个函数及其参数,然后当您调用 std::function 对象时,它只会执行带有参数的绑定函数。
I am basically trying to implement my own version of std::function, for my own edification.我基本上是在尝试实现我自己的 std::function 版本,以供我自己启迪。 I am curious how in C++ you would go about storing a function with a bunch of arbitrary parameters inside an object, and then having a method that would call the function with the passed in arguments.
我很好奇在 C++ 中你将如何在一个对象中存储一个带有一堆任意参数的函数,然后有一个方法可以使用传入的参数调用该函数。
I have looked at both the thread and std::function class, and both seem to be using tuples in some way to store their arguments.我查看了线程和 std::function 类,两者似乎都以某种方式使用元组来存储它们的参数。 In a declaration of a tuple you have to specify what types you are storing in it:
在元组的声明中,您必须指定要存储在其中的类型:
std::tuple<int, std::string> tup;
How do std::function and thread get around this by storing their variadic arguments in tuples? std::function 和 thread 如何通过将可变参数存储在元组中来解决这个问题? Furthermore, how do they retrieve the function and call it with all of the arguments?
此外,他们如何检索函数并使用所有参数调用它?
I am basically trying to implement my own version of std::function, for my own edification.
我基本上是在尝试实现我自己的 std::function 版本,以供我自己启迪。 I am curious how in C++ you would go about storing a function with a bunch of arbitrary parameters inside an object, and then having a method that would call the function with the passed in arguments.
我很好奇在 C++ 中你将如何在一个对象中存储一个带有一堆任意参数的函数,然后有一个方法可以使用传入的参数调用该函数。
std::function
is a beast of a class so I won't pretend that this is anywhere close to as complete. std::function
是一流的野兽,所以我不会假装这已经接近完整。 std::function
uses type erasure and small object optimization but I'll use polymorphism and store a base class pointer to a semi-anonymous implementation of a function wrapper to show how it can be done. std::function
使用类型擦除和小对象优化,但我将使用多态性并将基类指针存储到函数包装器的半匿名实现中,以展示它是如何完成的。 I say semi -anonymous because it actually has a name, but it's defined locally inside the function that instantiates it.我说半匿名是因为它实际上有一个名称,但它是在实例化它的函数内部本地定义的。 Storing the pointer (or the empty state) will be done in a
std::unique_ptr<funcbase>
.存储指针(或空状态)将在
std::unique_ptr<funcbase>
中完成。
The goal, as I've understood it, is to create a class with this basic interface:据我了解,目标是创建一个具有此基本接口的类:
template <class R, class... Args>
class fn_with_args<R(Args...)> {
public:
template <class F> fn_with_args(F&& f, Args&&... args);
R operator()();
};
That is, we need instances of fn_with_args<R(Args...)>
to be able to store function pointers / functors that when invoked with the stored arguments returns R
.也就是说,我们需要
fn_with_args<R(Args...)>
的实例来存储函数指针/函子,当使用存储的参数调用时返回R
。
#include <functional>
#include <memory>
#include <tuple>
template <class> class fn_with_args; // not implemented
template <class R, class... Args>
class fn_with_args<R(Args...)> {
// an abstract base for cloneable function objects with an operator()() to call
struct funcbase {
virtual ~funcbase() = default;
virtual std::unique_ptr<funcbase> clone() const = 0;
virtual R operator()() = 0;
};
public:
// create empty "fn_with_args":
fn_with_args() noexcept = default;
fn_with_args(std::nullptr_t) noexcept {};
// copy ctor - if store contains a pointer to a funcbase,
// let it clone itself
fn_with_args(const fn_with_args& other) :
store(other.store ? other.store->clone() : nullptr) {}
// copy assignment
fn_with_args& operator=(const fn_with_args& other) {
if(this != &other) *this = fn_with_args(other); // copy+move
return *this;
}
// moving can be done by default:
fn_with_args(fn_with_args&& other) noexcept = default;
fn_with_args& operator=(fn_with_args&& other) noexcept = default;
// constructing and storing arguments
template <class F>
fn_with_args(F&& f, Args&&... args) {
// the semi-anonymous implementation that inherits from funcbase
// and stores both the function and the arguments:
struct funcimpl : funcbase {
funcimpl(F&& f, Args&&... a)
: func{std::forward<F>(f)}, args{std::forward<Args>(a)...} {}
// cloning via a base class pointer:
std::unique_ptr<funcbase> clone() const override {
return std::make_unique<funcimpl>(*this);
}
// the operator that will call `func` with the stored arguments:
R operator()() override { return std::apply(func, args); }
F func; // the actual function/functor
std::tuple<Args...> args; // and the stored arguments
};
// create and store an instance of the above semi-anonymous class:
store = std::make_unique<funcimpl>(std::forward<F>(f),
std::forward<Args>(args)...);
}
// The call interface. It'll dereference `store` and then call it which
// will call the overridden operator()() in the semi-anonymous `funcimpl`:
R operator()() {
if(store) return (*store)();
throw std::bad_function_call();
}
private:
std::unique_ptr<funcbase> store;
};
Example usage:示例用法:
#include <iostream>
double foo(int x) {
return x * 3.14159;
}
int main() {
fn_with_args<int(double)> f1([](double d) -> int { return d; }, 3.14159);
std::cout << f1() << '\n';
fn_with_args<void()> f2; // create empty
//f2(); // would throw "bad_function_call" since it is "empty"
// populate it
f2 = fn_with_args<void()>([]{ std::cout << "void\n"; });
f2();
// call regular function:
fn_with_args<double(int)> f3(foo, 2);
std::cout << f3() << '\n';
// example with capture:
int v = 123;
f1 = fn_with_args<int(double)>([v](double d) -> int { return v * d; }, 3.14159);
std::cout << f1() << '\n';
// copying:
auto f11 = f1;
std::cout << f11() << '\n'; // calling the copy
}
you should store the params in std::tuple
and invoke them using std::apply您应该将参数存储在
std::tuple
并使用std::apply调用它们
#include <functional>
#include <tuple>
#include <vector>
template <class R>
class Function_Wrapper {
public:
template <typename Callable, typename... Args>
Function_Wrapper(Callable&& callable, Args&&... args)
: fn_([=, args = std::make_tuple(std::forward<Args>(args)...)]() {
return std::apply(callable, args);
}) {}
decltype(auto) run() {
// call our callable with the passed in arguments
return fn_();
}
decltype(auto) operator()() { return run(); }
private:
std::function<R()> fn_;
};
int add(int a, int b) { return a + b; }
int main() {
std::vector<Function_Wrapper<int>> f{{&add, 9, 30}, {&add, 1, 2}};
return f[0].run() + f[1]();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.