Chromium Mojo C++ Bindings API(中文)

本文档详细介绍了Chromium中Mojo C++ Bindings API的使用,包括接口的基本用法、Remote和PendingReceiver、接口创建、类型转换、错误处理等方面,旨在为Chromium开发者提供便利。
摘要由CSDN通过智能技术生成

概述

本文档为binding API的用法提供详细的指引,并附有示例代码段。详细的API文献请参考//mojo/public/cpp/bindings的头文件。
针对 Chromium开发者的简化指引,请看此链接

起航

当Mojom IDL文件被bindings生成器处理时,c++代码产出了根据.mojom文件命名的一系列.h和.cc文件。假设我们在//services/db/public/mojom/db.mojom创建了以下Mojom文件:

module db.mojom;

interface Table {
   
  AddRow(int32 key, string data);
};

interface Database {
   
  CreateTable(Table& table);
};

并在==//services/db/public/mojom/BUILD.gn==有一个生成bindings的GN:

import("//mojo/public/tools/bindings/mojom.gni")

mojom("mojom") {
   
  sources = [
    "db.mojom",
  ]
}

确定需要本接口依赖的目标,比如这行代码:

   deps += [ '//services/db/public/mojom' ]

如果我们随后构建这个目标:

ninja -C out/r services/db/public/mojom

这将会生成几个源文件,一些与C++ bindings有关。其中的两个:

out/gen/services/db/public/mojom/db.mojom.cc
out/gen/services/db/public/mojom/db.mojom.h

你能将以上生成的头文件放入你的项目中,去运用它包含的定义:

#include "services/business/public/mojom/factory.mojom.h"

class TableImpl : public db::mojom::Table {
   
  // ...
};

本文档为C++开发覆盖了由Mojom IDL生成的各种不同的定义,并且他们如何有效的用于消息管道的通信。

接口

Mojom IDL接口被翻译成相应的C++(纯虚)类接口。在内部也有为消息的序列化和反序列化而生成的代码,不过详情是对bindings的消费者隐藏的。

基本用法

让我们来看一个新的//sample/logger.mojom,它定义了client用于log简单字符串消息的一个简单logging接口:

module sample.mojom;

interface Logger {
   
  Log(string message);
};

bindings生成器将会生产一个有如下定义的logger.mojom.h(隐藏了不重要的细节):

namespace sample {
   
namespace mojom {
   

class Logger {
   
  virtual ~Logger() {
   }

  virtual void Log(const std::string& message) = 0;
};

}  // namespace mojom
}  // namespace sample

Remote and PendingReceiver

在Mojo bindings库中,这些是有效的强类型消息管道端点。如果一个Remote< T>绑定了消息管道的一个端点,那么它能够通过解引用调用T的接口。这些调用立即序列化它们的参数(通过生成的代码),并将相关消息写入管道中。
PendingReceiver< T>一个持有Remote< T>管道另一端的类型容器–也就是接收端–它将被路由到一些绑定它的实现中去。PendingReceiver< T>除了持有管道的一端和一些有效的编译时类型信息外,不会做任何事情。
在这里插入图片描述
所以我们怎么创建一个强类型的消息管道呢?

创建接口管道

一个创建接口管道的办法就是通过手动地创建一个管道,然后将每一端用一个强类型对象封装:

#include "sample/logger.mojom.h"

mojo::MessagePipe pipe;
mojo::Remote<sample::mojom::Logger> logger(
    mojo::PendingRemote<sample::mojom::Logger>(std::move(pipe.handle0), 0));
mojo::PendingReceiver<sample::mojom::Logger> receiver(std::move(pipe.handle1));

这种做法很繁琐,C++ Bindings库提供了一个更方便的办法来完成这件事。remote.h定义了一个BindNewPipeAndPassReceive方法:

mojo::Remote<sample::mojom::Logger> logger;
auto receiver = logger.BindNewPipeAndPassReceiver();

第二个代码段和第一个是完全相同的。

注意:在第一个例子中你可能注意到mojo::PendingRemote< Logger>的使用。它和PendingReceiver< T>是一样的,都只持有一个管道handle,并且不在管道做实际的读写。这个类型和PendingReceiver< T>一样是安全的,当从一个序列移动到另一个序列时。一个绑定的Remote< T>被绑定于一个单独序列。
Remote< T>可能通过Unbind()方法来解除绑定,这个方法还返回一个新的PendingRemote< T>。反之,Remote< T>也可能绑定(并拥有所有权)PendingRemote< T>,这样就能在管道中进行接口调用。
==Remote< T>序列-绑定的特性,对于安全地分发消息响应和连接错误提醒是非常有必要的。

一旦PendingRemote< T>被绑定,我们可以立即开始调用Logger接口方法,它将会立刻将消息写入管道。这些消息将会在管道接收端排好序,直到接收端被绑定并且开始读消息。

logger->Log("Hello!");

这实际上写了一则Log消息给管道。
在这里插入图片描述
如上所述,PendingReceiver实际没有做任何事情,所以消息将会一直保留在管道中。我们需要一个办法来将消息从管道的另一端读出来并且分发它们。我们必须绑定pending receiver

绑定一个PendingReceiver

在bindings库中有非常多不同的helper类用来绑定消息管道的接收端。它们中最原始的是mojo::Receiver< T>mojo::Receiver< T>T的实现和一个单一绑定消息管道端点连接起来(通过PendingRemote< T>),它将持续监控管道的可读状态。
任何时候只要被绑定的管道变成可读状态,Receiver将会调度一个task来读、反序列化(运用生成的代码)、将所有可用的消息分发给绑定的T的实现。以下是Logger接口的一个简单实现。需要注意的是,实现自身拥有mojo::Receiver。这是一个通用的模板,因为一个绑定的实现生命必须比绑定在它上面的mojo::Receiver长。

#include "base/logging.h"
#include "sample/logger.mojom.h"

class LoggerImpl : public sample::mojom::Logger {
   
 public:
  // NOTE: A common pattern for interface implementations which have one
  // instance per client is to take a PendingReceiver in the constructor.

  explicit LoggerImpl(mojo::PendingReceiver<sample::mojom::Logger> receiver)
      : receiver_(this, std::move(receiver)) {
   }
  Logger(const Logger&) = delete;
  Logger& operator=(const Logger&) = delete;
  ~Logger() override {
   }

  // sample::mojom::Logger:
  void Log(const std::string& message) override {
   
    LOG(ERROR) << "[Logger] " << message;
  }

 private:
  mojo::Receiver<sample::mojom::Logger> receiver_;
};

现在我们通过我们的LoggerImpl构造一个LoggerImpl,之前被排列好的Log消息将会被立即分发给LoggerImpl的序列:

LoggerImpl impl(std::move(receiver));

以下的示意图说明了事件的顺序,都是由上述代码行启动的:

  1. LoggerImpl构造器被调用,将PendingReceiver< Logger>传递给Receiver
  2. Receiver拥有PendingReceiver< Logger>管道的端点,开始监控它是否可读。当管道是可读的,立即调度一个task来读管道中挂起的Log消息。
  3. Log消息被读取并且反序列化,造成Receiver触发它绑定的LoggerImplLogger::Log方法。
    在这里插入图片描述
    我们的implementation通过LOG(ERROR)最终log了client的"Hello!"消息。

注意:管道的消息被读取和分发,只在绑定它的对象(也就是以上的例子中的mojo::Receiver)活着的时候进行。

接收响应

一些Mojom 接口方法期望得到一个响应。假设我们修改Logger接口,最后一行log就可以这样获得:

module sample.mojom;

interface Logger {
   
  Log(string message);
  GetTail() => (string message);
};

生成的C++接口像这样:

namespace sample {
   
namespace mojom {
   

class Logger {
   
 public:
  virtual ~Logger() {
   }

  virtual void Log(const std::string& message) = 0;

  using GetTailCallback = base::OnceCallback<void(const std::string& message)>;

  virtual void GetTail(GetTailCallback callback) = 0;
}

}  // namespace mojom
}  // namespace sample

所有client和接口的implementation都使用GetTail方法相同的签名:implementation用callback参数来响应请求,clients传递callback参数来异步接收响应。传递给GetTailGetTailCallback参数是有序的。它必须在GetTail被调用的相同序列中触发。client的callback也在GetTail被调用的相同序列中运行(logger被绑定的序列)。以下是更新之后的实现:

class LoggerImpl : public sample::mojom::Logger {
   
 public:
  // NOTE: A common pattern for interface implementations which have one
  // instance per client is to take a PendingReceiver in the constructor.

  explicit LoggerImpl(mojo::PendingReceiver<sample::mojom::Logge
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值