STD ::螺纹存储variadic参数如何通过其构造函数传递?

发布于 2025-02-09 06:52:02 字数 836 浏览 3 评论 0原文

假设我声明了一个带有以下代码的线程:

#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();
}

参数如何打印“啤酒”,以及在线程中存储的900000000?

我知道他们正在使用一个变异模板,在该模板中您首先传递函数,然后通过参数包。我对它们如何转发所有这些模板参数感到困惑,然后以某种方式调用加入或分离时,以某种方式将输入的函数调用。

std ::函数具有类似的功能,当您调用std ::绑定时,它将存储一个函数及其参数在对象中,然后当您调用std ::函数对象时,它将仅使用其参数执行绑定函数。

我基本上是在尝试实现自己的std ::函数,以供我自己的教育。我很好奇,在C ++中,您将如何使用一堆任意参数在对象中存储一个函数,然后使用一种将函数与参数中传递的函数调用的方法。

我已经看过线程和std ::函数类,并且似乎都以某种方式使用元组来存储他们的参数。在声明元组的声明中,您必须指定其中存储在其中的类型:

std::tuple<int, std::string> tup;

std ::功能和线程如何通过将其变异参数存储在元组中来解决这个问题?此外,他们如何检索功能并使用所有参数称呼它?

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?

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.

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.

I am basically trying to implement my own version of std::function, for my own edification. 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.

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. 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? Furthermore, how do they retrieve the function and call it with all of the arguments?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

鹤舞 2025-02-16 06:52:02

我基本上是在尝试实现自己的std ::函数,以供我自己的教育。我很好奇您在C ++中如何使用一堆任意参数在对象中存储一个函数,然后使用一种将通过参数中传递的函数调用函数的方法。

std :: function是一类的野兽,因此我不会假装这与完整的地方相近。 std :: function使用 type Erasure 和小对象优化,但我将使用多态性并将基类指针存储到函数包装器的半匿名实现以显示如何显示可以做到。我说 semi - 匿名,因为它实际上具有一个名称,但是它在实例化的函数内定义了。存储指针(或空状态)将在std :: unique_ptr&lt; funcbase&gt;中完成。

我理解的是,目标是创建一个基本接口的类:

template <class R, class... Args>
class fn_with_args<R(Args...)> {
public:
    template <class F> fn_with_args(F&& f, Args&&... args);
    R operator()();
};

也就是说,我们需要fn_with_args&lt; r(args ...)&gt;的实例才能存储使用存储的参数调用时,函数指针 /函子返回r < / code>。

#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;
};

示例用法:

#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
}

demo

I am basically trying to implement my own version of std::function, for my own edification. 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.

std::function is a beast of a class so I won't pretend that this is anywhere close to as complete. 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. 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>.

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.

#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
}

Demo

尛丟丟 2025-02-16 06:52:02

您应该将参数存储在std :: tuple中,然后使用 std ::应用

#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]();
}

在Compiler Explorer中

you should store the params in std::tuple and invoke them using 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]();
}

Here in Compiler Explorer

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文