支持自定义 C++ 类型
本文档仍为 experimental,详细信息可能会随着我们的迭代而发生变化。 欢迎在此页面分享你对 工作组内部讨论 的反馈。
英This documentation is still experimental and details are subject to changes as we iterate. Feel free to share your feedback on the discussion inside the working group for this page.
而且,它还包含几个 手动步骤。 请注意,一旦新架构稳定,这将不代表最终的开发者体验。 我们正在开发工具、模板和库,以帮助你快速开始使用新架构,而无需完成整个设置。
英Moreover, it contains several manual steps. Please note that this won't be representative of the final developer experience once the New Architecture is stable. We're working on tools, templates and libraries to help you get started fast on the New Architecture, without having to go through the whole setup.
默认情况下,C++ Turbo Native 模块支持大多数 std::
标准类型的 桥接功能。
英By default C++ Turbo Native Modules support bridging functionality for most std::
standard types.
如果你想在你的应用/库中添加对新/自定义类型的支持,你只需提供必要的 bridging
头文件。
英If you want to add support for new / custom types in your app / library, you only need to provide the necessary bridging
header file.
本指南继续前面的 C++ Turbo 原生模块 部分。
英This guide continues the previous C++ Turbo Native Modules section.
示例:Int64
C++ Turbo Native 模块尚不支持 int64_t
数字 - 因为 JavaScript 不支持大于 2^53
的数字。
英C++ Turbo Native Modules don't support int64_t
numbers yet - because JavaScript doesn't support numbers greater 2^53
.
我们不能将数字 > 2^53
表示为 JavaScript number
,但我们可以将它们表示为 JavaScript string
,并通过在 tm
文件夹中创建一个名为 Int64.h
的自定义桥接头文件自动将它们转换(又名 bridge
)为 C++ int64_t
:
英We can't represent numbers > 2^53
as JavaScript number
's - but we can represent them as JavaScript string
's and automatically convert (aka. bridge
) them to C++ int64_t
's by creating a custom Bridging header file called Int64.h
in the tm
folder:
#pragma once
#include <react/bridging/Bridging.h>
namespace facebook::react {
template <>
struct Bridging<int64_t> {
static int64_t fromJs(jsi::Runtime &rt, const jsi::String &value) {
try {
size_t pos;
auto str = value.utf8(rt);
auto num = std::stoll(str, &pos);
if (pos != str.size()) {
throw std::invalid_argument("Invalid number"); // don't support alphanumeric strings
}
return num;
} catch (const std::logic_error &e) {
throw jsi::JSError(rt, e.what());
}
}
static jsi::String toJs(jsi::Runtime &rt, int64_t value) {
return bridging::toJs(rt, std::to_string(value));
}
};
} // namespace facebook::react
自定义 bridging
标头的关键组件是:
英The key components for your custom bridging
header are:
- 自定义类型的
Bridging
结构的显式特化,在本例中为int64_t
- 用于从
jsi::
类型转换为自定义类型的fromJs
函数 - 用于从自定义类型转换为
jsi:
类型的toJS
函数
省略 fromJS
或 toJS
将使 bridging
标头成为只读或只写。
英Omitting either fromJS
or toJS
would make you bridging
header either readonly or writeonly.
现在你可以将以下函数添加到你的 JavaScript 规范中:
英Now you can add the following function to your JavaScript spec:
- TypeScript
- Flow
// ...
readonly cubicRoot: (input: string) => number;
// ..
// ...
+cubicRoot: (input: string) => number;
// ..
在 NativeSampleModule.h
文件中声明它并包含 Int64.h
头文件:
英Declare it in your NativeSampleModule.h
file and include the Int64.h
header file:
//...
#include "Int64.h"
//...
int32_t cubicRoot(jsi::Runtime& rt, int64_t input);
并在 NativeSampleModule.cpp
中实现:
英And implement it in NativeSampleModule.cpp
:
//...
#include <cmath>
//...
int32_t NativeSampleModule::cubicRoot(jsi::Runtime& rt, int64_t input) {
return std::cbrt(input);
}
在你的应用中,你可以通过以下方式调用这个新的原生函数:
英In your app you can call this new native function via:
<Section title="Cxx TurboModule">
NativeSampleModule.cubicRoot(...) ={' '}
{JSON.stringify(
NativeSampleModule.cubicRoot('9223372036854775807'),
)}
</Section>
应该返回 2097152
。
英which should return 2097152
.
任何自定义类型
与上面的示例类似,你现在可以为要公开给 React-Native 的任何自定义 C++ 类型编写自定义 bridging
功能。 例如,你可以添加对 folly::StringPiece
、QString
、boost::filesystem::path
、absl::optional
或你需要在 C++ Turbo Native 模块中支持的任何其他类型的支持。
英Similar to the example above you can now write custom bridging
functionality for any custom C++ type you want to expose to react-native. E.g., you can add support for folly::StringPiece
, QString
, boost::filesystem::path
, absl::optional
or any other type you need to support in your C++ Turbo Native Modules.
#pragma once
#include <react/bridging/Bridging.h>
#include <boost/filesystem.hpp>
namespace facebook::react {
template<>
struct Bridging<boost::filesystem::path> {
static boost::filesystem::path fromJs(jsi::Runtime& rt, const std::string& value) { // auto-bridge from jsi::String to std::string
return boost::filesystem::path(value);
}
static jsi::String toJs(jsi::Runtime& rt, boost::filesystem::path value) {
return bridging::toJs(rt, value.string());
}
};
} // namespace facebook::react
自定义结构
你可以对 JavaScript 中的自定义类型使用相同的方法,例如:
英You can use the same approach for you custom types in JavaScript such as this one:
export type CustomType = {
key: string,
enabled: boolean,
time?: number,
};
可以通过以下方式将其暴露给你的 C++ Turbo Native 模块
英which can be exposed to your C++ Turbo Native Module via
- TypeScript
- Flow
// ...
readonly passCustomType: (input: CustomType) => CustomType;
// ..
// ...
+passCustomType: (input: CustomType) => CustomType;
// ..
手动输入
要在 C++ 中使用此自定义类型,你需要定义自定义 Struct 和 bridging
函数,例如直接在 NativeSampleModule.h
中:
英To use this custom type in C++, you need to define your custom Struct and bridging
function e.g., directly in NativeSampleModule.h
:
struct CustomType {
std::string key;
bool enabled;
std::optional<int32_t> time;
};
template <>
struct Bridging<CustomType> {
static CustomType fromJs(
jsi::Runtime &rt,
const jsi::Object &value,
const std::shared_ptr<CallInvoker> &jsInvoker) {
return CustomType{
bridging::fromJs<std::string>(
rt, value.getProperty(rt, "key"), jsInvoker),
bridging::fromJs<bool>(
rt, value.getProperty(rt, "enabled"), jsInvoker),
bridging::fromJs<std::optional<int32_t>>(
rt, value.getProperty(rt, "time"), jsInvoker)};
}
static jsi::Object toJs(jsi::Runtime &rt, const CustomType &value) {
auto result = facebook::jsi::Object(rt);
result.setProperty(rt, "key", bridging::toJs(rt, value.key));
result.setProperty(rt, "enabled", bridging::toJs(rt, value.enabled));
if (value.time) {
result.setProperty(rt, "time", bridging::toJs(rt, value.time.value()));
}
return result;
}
};
在 NativeSampleModule.h
文件中声明它:
英Declare it in your NativeSampleModule.h
file:
CustomType passCustomType(jsi::Runtime& rt, CustomType input);
在 NativeSampleModule.cpp
中实现:
英Implement it in NativeSampleModule.cpp
:
CustomType NativeSampleModule::passCustomType(jsi::Runtime& rt, CustomType input) {
input.key = "1909";
input.enabled = !input.enabled;
input.time = 42;
return input;
}
在你的应用中,你可以通过以下方式调用这个新的原生函数:
英In your app you can call this new native function via:
<Section title="Cxx TurboModule">
NativeSampleModule.passCustomType(...) ={' '}
{JSON.stringify(
NativeSampleModule.passCustomType({
key: '123',
enabled: true,
time: undefined,
}),
)}
</Section>
应该返回 {"key":"1909","enabled":false","time":42}
。
英which should return {"key":"1909","enabled":false","time":42}
.
这可行 - 但相当复杂。
英This works - but is quite complex.
结构生成器
C++ Turbo Native Modules 的 代码生成器 确实支持结构生成器,因此你可以将 NativeSampleModule.h
中的上述代码简化为:
英Codegen for C++ Turbo Native Modules does support struct generators, so you can simplify the code above in NativeSampleModule.h
to:
using CustomType = NativeSampleModuleBaseCustomType<std::string, bool, std::optional<int32_t>>;
template <>
struct Bridging<CustomType>
: NativeSampleModuleBaseCustomTypeBridging<std::string, bool, std::optional<int32_t>> {};
使用 using CustomType
,你可以为具体结构声明一个名称。
英With using CustomType
you declare a name for your concrete struct.
成员类型
使用 std::string, bool, std::optional<int32_t>
,你可以按照 JavaScript 规范中定义结构成员的顺序定义结构成员的属性类型。 订单事宜. 第一个模板参数指的是结构体的第一个数据类型,依此类推。
英With std::string, bool, std::optional<int32_t>
you define the property types of the struct members in the order they were defined in your JavaScript spec. The order matters. The 1st template argument refers to the 1st data type of the struct, and so forth.
没有任何自定义转换函数:
英Without any custom conversion functions:
基类
NativeSampleModuleBaseCustomType
是 AppSpecsJSI.h
中自动生成的模板,其名称是通过以下方式生成的:
英NativeSampleModuleBaseCustomType
is an auto-generated template in your AppSpecsJSI.h
which name is generated by:
NativeSampleModule
(JavaScript 规范中 C++ Turbo Native 模块的名称)+Base
(常量)+CustomType
(JavaScript 规范中的类型名称)
相同的命名模式适用于通过 struct Bridging<CustomType>
定义的必要 Bridging
结构。
英The same naming schema applies to the necessary Bridging
struct which is defined via struct Bridging<CustomType>
.