如何在 C 中实现通用回调

发布于 2024-08-26 02:17:39 字数 376 浏览 8 评论 0原文

请原谅我在问这个基本问题时的无知,但我已经习惯了使用 Python,因为这类事情是微不足道的,以至于我完全忘记了如何在 C++ 中尝试这样做。

我希望能够将回调传递给在后台执行缓慢进程的函数,并在进程完成后稍后调用它。该回调可以是自由函数、静态函数或成员函数。我还希望能够在其中注入一些任意参数以获取上下文。 (即,在某种程度上实现一个非常穷的人的协程。)最重要的是,这个函数将始终采用 std::string,它是进程的输出。我不介意这个参数在最终回调参数列表中的位置是否固定。

我感觉答案将涉及 boost::bind 和 boost::function 但我无法计算出创建任意可调用对象所需的精确调用(同时将它们柯里化为仅采用单个字符串),将它们存储在后台进程中,并使用字符串参数正确调用可调用对象。

Forgive my ignorance in asking this basic question but I've become so used to using Python where this sort of thing is trivial that I've completely forgotten how I would attempt this in C++.

I want to be able to pass a callback to a function that performs a slow process in the background, and have it called later when the process is complete. This callback could be a free function, a static function, or a member function. I'd also like to be able to inject some arbitrary arguments in there for context. (ie. Implementing a very poor man's coroutine, in a way.) On top of that, this function will always take a std::string, which is the output of the process. I don't mind if the position of this argument in the final callback parameter list is fixed.

I get the feeling that the answer will involve boost::bind and boost::function but I can't work out the precise invocations that would be necessary in order to create arbitrary callables (while currying them to just take a single string), store them in the background process, and invoke the callable correctly with the string parameter.

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

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

发布评论

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

评论(3

慈悲佛祖 2024-09-02 02:17:39

回调应存储为 boost::function。然后,您可以使用 boost::bind 通过绑定其他参数来将任何其他函数签名“转换”为此类对象。

示例

我没有尝试编译这个,但无论如何它应该显示总体思路

void DoLongOperation(boost::function<void, const std::string&> callback)
{
  std::string result = DoSomeLengthyStuff();
  callback(result);
}


void CompleteRoutine1(const std::string&);
void CompleteRoutine2(int param, const std::string&);

// Calling examples
DoLongOperation(&CompleteRoutine1); // Matches directly
DoLongOperation(boost::bind(&CompleteRoutine2, 7, _1)); // int parameter is bound to constant.

// This one is thanks to David Rodríguez comment below, but reformatted here:
struct S 
{ 
  void f( std::string const & );
};

int main() 
{ 
  S s;
  DoLongOperation( boost::bind( &S::f, &s, _1 ) ); 
}

The callback should be stored as a boost::function<void, std::string>. Then you can use boost::bind to "convert" any other function signature to such an object, by binding the other parameters.

Example

I've not tried to compile this, but it should show the general idea anyways

void DoLongOperation(boost::function<void, const std::string&> callback)
{
  std::string result = DoSomeLengthyStuff();
  callback(result);
}


void CompleteRoutine1(const std::string&);
void CompleteRoutine2(int param, const std::string&);

// Calling examples
DoLongOperation(&CompleteRoutine1); // Matches directly
DoLongOperation(boost::bind(&CompleteRoutine2, 7, _1)); // int parameter is bound to constant.

// This one is thanks to David Rodríguez comment below, but reformatted here:
struct S 
{ 
  void f( std::string const & );
};

int main() 
{ 
  S s;
  DoLongOperation( boost::bind( &S::f, &s, _1 ) ); 
}
眉黛浅 2024-09-02 02:17:39

听起来您想使用观察者模式

Sounds like you want to use the Observer pattern.

哭泣的笑容 2024-09-02 02:17:39

最简单的方法:

class Callback
{
public:
  virtual ~Callback() {}
  virtual Callback* clone() const = 0;

  // Better to wrap the call (logging, try/catch, etc)
  void execute(const std::string& result) { this->executeImpl(result); }

protected:
  // Don't make sense to have them public
  Callback() {}
  Callback(const Callback&) {}
  Callback& operator=(const Callback&) { return *this; }

private:
  virtual void executeImpl(const std::string& result) = 0;
};

// Example
class Example: public Callback
{
public:
  Example(int a, int b): Callback(), mA(a), mB(b) {}
  virtual Example* clone() const { return new Example(*this); }

private:
  virtual void executeImpl(const std::string& result) {}

  int mA;
  int mB;
};

然后,您可以将回调类(通过指针/引用)传递给进程。该类根据需要有一个状态,并且可以在必要时进行复制(如果没有,则删除克隆)。

The easiest way:

class Callback
{
public:
  virtual ~Callback() {}
  virtual Callback* clone() const = 0;

  // Better to wrap the call (logging, try/catch, etc)
  void execute(const std::string& result) { this->executeImpl(result); }

protected:
  // Don't make sense to have them public
  Callback() {}
  Callback(const Callback&) {}
  Callback& operator=(const Callback&) { return *this; }

private:
  virtual void executeImpl(const std::string& result) = 0;
};

// Example
class Example: public Callback
{
public:
  Example(int a, int b): Callback(), mA(a), mB(b) {}
  virtual Example* clone() const { return new Example(*this); }

private:
  virtual void executeImpl(const std::string& result) {}

  int mA;
  int mB;
};

And then, you can pass the callback class (by pointer / reference) to the process. The class has a state, as required, and may be copied if necessary (if not, drop the clone).

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