【问题标题】:Is it possible for C++ to implement function pointers point to different parameter lists?C++ 是否可以实现指向不同参数列表的函数指针?
【发布时间】:2019-01-14 03:19:53
【问题描述】:

我最近写了关于类成员函数回调的功能。我需要保存回调对象和函数指针,然后调用函数指针并在需要回调的地方填写适当的参数。

我一开始是typedef void (AAA::*Function)(int a, int b);的一种形式,但是当我需要支持成员函数的不同参数列表时,我显然需要一种动态的方式来实现它。

class AAA
{
public:
    int add(int a, int b)
    {
        return (a + b);
    }
};

class BBB
{
public:

    void setValue(std::string value)
    {
        this->value = value;
    }

private:
    std::string value;
};

class CCC
{
public:

    void bind(??? p)    // Binding objects and callback functions.
    {
        this->p = p;
    }

    template <class... Args>
    auto callback(Args&&... args)   // Autofill parameter list.
    {
        return this->p(std::forward<Args>(args)...);
    }

private:
    ??? p;  // How is this function pointer implemented?
};

int main()
{
    AAA aaa;
    BBB bbb;

    CCC ccc;
    ccc.bind(???(aaa, &AAA::add));
    int number = ccc.callback(5, 6);

    ccc.bind(???(bbb, &BBB::setValue));
    ccc.callback("Hello");

    system("pause");
    return 0;
}

不知道怎么实现函数指针“???”。

【问题讨论】:

标签: c++ c++11 visual-c++ c++14 c++17


【解决方案1】:

您基本上是要求具有完全动态类型和检查的函数调用。

要进行完全动态的函数调用,基本上必须抛弃 C++ 函数调用系统。

这是个坏主意,但我会告诉你怎么做。

一个可动态调用的对象大致如下:

using dynamic_function = std::function< std::any( std::vector<std::any> ) >

在哪里使用

struct nothing_t {};

当我们想返回void时。

然后你编写机器,接受一个对象和一个特定的签名,然后把它包装起来。

template<class R, class...Args, class F>
struct dynamic_function_maker {
  template<std::size_t...Is>
  dynamic_function operator()(std::index_sequence<Is...>, F&& f)const {
    return [f=std::forward<F>(f)](std::vector<std::any> args)->std::any {
      if (sizeof...(Is) != args.size())
        throw std::invalid_argument("Wrong number of arguments");
      if constexpr( std::is_same< std::invoke_result_t<F const&, Args... >, void >{} )
      {
        f( std::any_cast<Args>(args[Is])... );
        return nothing_t{};
      }
      else
      {
        return f( std::any_cast<Args>(args[Is])... );
      }
    };
  }
  dynamic_function operator()(F&& f)const {
    return (*this)(std::make_index_sequence<sizeof...(Args)>{}, std::forward<F>(f));
  }
};
template<class R, class...Args, class F>
dynamic_function make_dynamic_function(F f){
  return dynamic_function_maker<R,Args...,F>{}(std::forward<F>(f));
}

接下来,您将要推断函数指针等的签名:

template<class R, class...Args>
dynamic_function make_dynamic_function(R(*f)(Args...)){
  return dynamic_function_maker<R,Args...,F>{}(std::forward<F>(f));
}
template<class Tclass R, class...Args>
dynamic_function make_dynamic_function(T* t, R(T::*f)(Args...)){
  return dynamic_function_maker<R,Args...,F>{}(
    [t,f](auto&&...args)->decltype(auto){return (t->*f)(decltype(args)(args)...);}
  );
}

那么在修正了上面的错别字之后,你应该能够解决你原来的问题。

再次,作为一个真正可以编写和理解上述代码的人,我强烈建议您不要使用它。它脆弱而危险。

几乎没有充分的理由将回调存储在您不知道要使用什么参数调用它的地方。

对于您要调用它的每组参数,应该有一个不同的类型和CCC 实例。当人们问这个问题时,有 99/100 次他们问错了问题。

【讨论】:

  • 其实我只是想做一个通用的指针变量,指向回调函数。但是当我将它定义为成员变量时,我无法为它写一个固定的格式。所以,我不知道如何做得更好。
【解决方案2】:

C++ 是一种类型安全的语言。这意味着您无法完全按照您在问题中概述的内容进行操作。指向带有特定参数的函数的指针与指向带有不同参数的函数的指针是不同的类型。这是 C++ 的基础。

std::bind 可用于将不同类型擦除为同一类型,但最后会得到一个类型,只能使用匹配的参数集(如果有)调用。不可能调用带有实际参数的“底层”绑定函数。那是因为std::bind 的全部目的是让它们消失,并且无法访问。这就是std::bind 的用途。

在遵守 C++ 类型安全的界限和约束的同时,您只有有限的一组选项来完成这项工作。

  1. 以某种方式使用void *。实际上,不要。不要那样做。这只会导致更多的问题和头痛。

  2. 有一个单独的回调列表和类,每个回调集都有一个列表,它们采用一组特定的参数。在调用回调时,您必须知道要传递哪些参数。因此,只需从适当的列表中获取您的回调即可。

  3. 使用std::variant。类型安全的 std::variant 仅适用于 C++17(但 boost 有一个类似的模板,大部分是等效的,并且可用于较旧的 C++ 修订版)。您的所有回调都采用单个 std::variant 参数,每个可能的参数集的变体(指定为其中的 std::tuple,或某个类/结构实例)。如果收到包含错误参数值的std::variant,每个回调都必须决定如何处理。

或者,std::variant 可以是不同 std::function 类型的变体,从而将类型检查的责任转移给调用者,而不是每个回调。

归根结底,C++ 本质上是一种类型安全的语言;这正是人们选择使用 C++ 而不是不具有相同类型安全性的不同语言的原因之一。

但是作为一种类型安全的语言,这意味着在将不同类型混合在一起时会有一定的限制。具体来说:你不能。 C++ 中的所有内容始终且必须是单一类型。

【讨论】:

  • 我很确定 std::bind 会进行零类型擦除。
  • 定义“std::bind”类型的成员变量需要模板参数,但我需要一个动态定义的方法。
  • Java 也是一种类型安全的语言,但是这个功能可以通过reflection 来实现。我的观点是 c++ 是类型安全的可能不是一个很好的论据,为什么在 c++ 中不能(或不应该)这样做。
猜你喜欢
  • 1970-01-01
  • 2013-05-22
  • 1970-01-01
  • 2011-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-26
  • 1970-01-01
相关资源
最近更新 更多