为什么我不能std::申请成员函数

问题描述 投票:0回答:3

我正在尝试开发一个包装器来帮助人们在调用任何成员函数时使用

pthread

template <typename>
struct signature;

template <typename C, typename R, typename... Args>
struct signature<R (C::*)(Args...)> {
    using return_type        = R;
    using pure_argument_type = std::tuple<C*, std::tuple<Args...>>; // obj, {args...}
    using argument_type =
        std::tuple<R (C::*)(Args...), pure_argument_type>; // obj.mem_fn, {obj, args...}
};
    
struct Job {
public:
    Job()  = default;
    ~Job() = default;

    void join() { pthread_join(btd, nullptr); }

    template <typename C, typename F, typename... Args>
    int run(F C::*f, C* c, Args&&... args) {
        typename signature<decltype(f)>::pure_argument_type pureParams =
            std::make_tuple(c, std::forward_as_tuple(args...));
        typename signature<decltype(f)>::argument_type params = std::make_tuple(f, pureParams);

        return pthread_create(
            &btd, nullptr,
            [](void* p) {
                auto param = static_cast<typename signature<decltype(f)>::argument_type*>(p);
                std::apply(std::get<0>(*param), std::get<1>(*param)); ////// ERROR!
                return (void*)nullptr;
            },
            &params);
    }

private:
    pthread_t btd;
};

但是,当我尝试执行以下操作时,

class Test {
public:
    void func(int& a, double& d) { a = 2; d = 2.0; }

    void dojob() {
        int a = 0;
        double d = 0.0;
        Job job;
        job.run(&Test::func, this, std::ref(a), std::ref(d));
        job.join();
        std::cout << "a:" << a << " d:" << d << std::endl;
    }
};

Test t;
t.dojob();

我收到错误:

In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_map.h:63,
                 from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/map:61,
                 from <source>:2:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple: In instantiation of 'constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = void (Test::*&)(int&, double&); _Tuple = std::tuple<Test*, std::tuple<int&, double&> >&; long unsigned int ..._Idx = {0, 1}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1>]':
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:1854:31:   required from 'constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = void (Test::*&)(int&, double&); _Tuple = std::tuple<Test*, std::tuple<int&, double&> >&]'
<source>:98:27:   required from 'int Job::run(F C::*, C*, Args&& ...) [with C = Test; F = void(int&, double&); Args = {std::reference_wrapper<int>, std::reference_wrapper<double>}]'
<source>:118:16:   required from here
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:1843:27: error: no matching function for call to '__invoke(void (Test::*&)(int&, double&), Test*&, std::__tuple_element_t<1, std::tuple<Test*, std::tuple<int&, double&> > >&)'
 1843 |       return std::__invoke(std::forward<_Fn>(__f),
      |              ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
 1844 |                            std::get<_Idx>(std::forward<_Tuple>(__t))...);
      |

您可以在这里获取我的代码:https://godbolt.org/z/sMjcxvP33

c++ templates c++17 apply template-meta-programming
3个回答
3
投票

std::get<1>(*param)
的返回类型是
std::tuple<C*, std::tuple<Args...>>

由于这个

tuple
的第二个元素的类型也是
tuple
,所以需要使用
std::apply
再次展开它,像这样

std::apply(
  [f = std::get<0>(*param)](auto* obj, auto&& args) {
    std::apply([&](auto&&... args) { 
      (obj->*f)(std::forward<decltype(args)>(args)...); 
    }, std::forward<decltype(args)>(args));
  },
  std::get<1>(*param));

演示

另一种选择是将嵌套的

tuple
展平为
std::tuple<C*, Args...>
,然后将其与
std::apply
一起传递给
f

std::apply(
  std::get<0>(*param),
  std::tuple_cat(
    std::make_tuple(std::get<0>(std::get<1>(*param))),
                    std::get<1>(std::get<1>(*param))));

演示


2
投票

std::apply
需要一个包含所有参数的单个元组,而不是彼此嵌套的元组。

你有一个竞争条件,如果你的 lambda 没有将参数复制到

std::apply
的参数中,那么当
run
返回时,你将取消引用一个悬空指针。

template <typename C, typename R, typename... Args>
struct signature<R (C::*)(Args...)> {
    using return_type        = R;
    using pure_argument_type = std::tuple<C*, Args...>; // obj, args...
    struct argument_type {
        R (C::*fun)(Args...);
        pure_argument_type args;
    };
};
    
struct Job {
public:
    Job()  = default;
    ~Job() = default;

    void join() { pthread_join(btd, nullptr); }

    template <typename C, typename R, typename... Args>
    int run(R (C::*f)(Args...), C* c, Args&&... args) {
        using params_t = typename signature<R (C::*)(Args...)>::argument_type;

        auto * params = new params_t{f, std::tuple_cat(std::make_tuple(c), std::forward_as_tuple(args...))};

        return pthread_create(
            &btd, nullptr,
            [](void* p) {
                auto param = static_cast<params_t*>(p);
                std::apply(param->fun, param->args);
                delete param;
                return (void*)nullptr;
            },
            params);
    }

private:
    pthread_t btd;
};

0
投票

这个例子向您展示如何在 2023 年正确使用 apply 命令。

template<typename ... TypyParametru>
decltype(auto) vypustky(TypyParametru...arg)
{
    // Create std::tuple z předaných hodnot
    std::tuple<TypyParametru...> hodnoty(arg...);

   // print all the values from std::tuple
    std::experimental::fundamentals_v1::apply([](const auto&... args)
    {
        ((std::cout << args << " "), ...);
    }, hodnoty);

    std::cout << std::endl;
}

此命令捕获了重要部分:

std::experimental::fundamentals_v1::apply

该命令嵌套在experimental::fundamentals_v1中。

© www.soinside.com 2019 - 2024. All rights reserved.