Skip to content

react-native code-gen > C++ TurboModules struct support - #35265

Closed
christophpurrer wants to merge 2 commits into
react:mainfrom
christophpurrer:export-D41133761
Closed

react-native code-gen > C++ TurboModules struct support#35265
christophpurrer wants to merge 2 commits into
react:mainfrom
christophpurrer:export-D41133761

Conversation

@christophpurrer

Copy link
Copy Markdown
Contributor

Summary:
This adds a templating layer for C++ TurboModules to automatically generate struct templates from TurboModule specs.

You have to define concrete types for those templates to use them in your C++ TurboModule.

E.g. for the JS flow type:

export type ObjectStruct = {|
a: number,
b: string,
c?: ?string,
|};

code-gen will now generate the following template code:

#pragma mark - NativeCxxModuleExampleCxxBaseObjectStruct
template <typename P0, typename P1, typename P2>
struct NativeCxxModuleExampleCxxBaseObjectStruct {
P0 a;
P1 b;
P2 c;
bool operator==(const NativeCxxModuleExampleCxxBaseObjectStruct &other) const {
return a == other.a && b == other.b && c == other.c;
}
};
template <typename P0, typename P1, typename P2>
struct NativeCxxModuleExampleCxxBaseObjectStructBridging {
static NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> fromJs(
jsi::Runtime &rt,
const jsi::Object &value,
const std::shared_ptr<CallInvoker> &jsInvoker) {
NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> result{
bridging::fromJs<P0>(rt, value.getProperty(rt, "a"), jsInvoker),
bridging::fromJs<P1>(rt, value.getProperty(rt, "b"), jsInvoker),
bridging::fromJs<P2>(rt, value.getProperty(rt, "c"), jsInvoker)};
return result;
}
static jsi::Object toJs(
jsi::Runtime &rt,
const NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> &value) {
auto result = facebook::jsi::Object(rt);
result.setProperty(rt, "a", bridging::toJs(rt, value.a));
result.setProperty(rt, "b", bridging::toJs(rt, value.b));
if (value.c) {
result.setProperty(rt, "c", bridging::toJs(rt, value.c.value()));
}
return result;
}
};

and you can use it in our C++ TurboModule for example as:

using ObjectStruct = NativeCxxModuleExampleCxxBaseObjectStruct<
int32_t,
std::string,
std::optional<std::string>>;
template <>
struct Bridging<ObjectStruct>
: NativeCxxModuleExampleCxxBaseObjectStructBridging<
int32_t,
std::string,
std::optional<std::string>> {};

or as

using ObjectStruct = NativeCxxModuleExampleCxxBaseObjectStruct<
float,
folly::StringPiece,
std::optional<std::string>>;
template <>
struct Bridging<ObjectStruct>
: NativeCxxModuleExampleCxxBaseObjectStructBridging<
float,
folly::StringPiece,
std::optional<std::string>> {};

Or as
...

Changelog: [Internal]

Differential Revision: D41133761

@facebook-github-botfacebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. p: Facebook Partner: Facebook Partner fb-exported labels Nov 9, 2022
@facebook-github-bot

Copy link
Copy Markdown
Contributor

This pull request was exported from Phabricator. Differential Revision: D41133761

@analysis-bot

Copy link
Copy Markdown
PlatformEngineArchSize (bytes)Diff
ios-universaln/a--

Base commit: cf37479
Branch: main

@analysis-bot

analysis-bot commented Nov 9, 2022

Copy link
Copy Markdown
PlatformEngineArchSize (bytes)Diff
androidhermesarm64-v8a7,102,063+31,923
androidhermesarmeabi-v7a6,471,837+29,553
androidhermesx867,519,581+34,445
androidhermesx86_647,378,156+33,438
androidjscarm64-v8a8,966,324+31,485
androidjscarmeabi-v7a7,698,075+29,127
androidjscx869,028,908+34,022
androidjscx86_649,506,829+33,004

Base commit: cf37479
Branch: main

@facebook-github-bot

Copy link
Copy Markdown
Contributor

This pull request was exported from Phabricator. Differential Revision: D41133761

@pull-bot

Copy link
Copy Markdown

PR build artifact for 6b120b5f6602f4eedaa9e3950fe9b1839172c54f is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.

christophpurrerand others added 2 commits November 8, 2022 17:19
…id/iOS/macOS/Windows)
Summary:
Changelog:
[General][Added] - Add a C++ only TurboModule example (for Android/iOS/macOS/Windows)
react-native@0.69 introduced a new bridging layer to ease integration for pure C++ TurboModules using C++ std:: types directly instead of the lower level jsi:: types:
https://github.com/facebook/react-native/tree/v0.69.0/ReactCommon/react/bridging
This bridging layer can be used in JSI functions or more conveniently in C++ TurboModules.
Here is a example of an C++ only TurboModule which will work on Android and iOS and macOS/Windows (using microsoft/react-native-macos|windows) only using flow/TypeScript and standard C++ types.
C++ only TurboModules are very handy as they do not require to work with JSI APIs - instead std:: or custom C++ can by used.
Differential Revision: https://internalfb.com/D39011736
fbshipit-source-id: f4dadd46bdd65e45193dd6ddffee503bd473a110
Summary:
Pull Request resolved: react#35265
This adds a templating layer for C++ TurboModules to automatically generate struct templates from TurboModule specs.
You have to define concrete types for those templates to use them in your C++ TurboModule.
E.g. for the JS flow type:
```
export type ObjectStruct = {|
a: number,
b: string,
c?: ?string,
|};
```
code-gen will now generate the following template code:
```
#pragma mark - NativeCxxModuleExampleCxxBaseObjectStruct
template <typename P0, typename P1, typename P2>
struct NativeCxxModuleExampleCxxBaseObjectStruct {
P0 a;
P1 b;
P2 c;
bool operator==(const NativeCxxModuleExampleCxxBaseObjectStruct &other) const {
return a == other.a && b == other.b && c == other.c;
}
};
template <typename P0, typename P1, typename P2>
struct NativeCxxModuleExampleCxxBaseObjectStructBridging {
static NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> fromJs(
jsi::Runtime &rt,
const jsi::Object &value,
const std::shared_ptr<CallInvoker> &jsInvoker) {
NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> result{
bridging::fromJs<P0>(rt, value.getProperty(rt, "a"), jsInvoker),
bridging::fromJs<P1>(rt, value.getProperty(rt, "b"), jsInvoker),
bridging::fromJs<P2>(rt, value.getProperty(rt, "c"), jsInvoker)};
return result;
}
static jsi::Object toJs(
jsi::Runtime &rt,
const NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> &value) {
auto result = facebook::jsi::Object(rt);
result.setProperty(rt, "a", bridging::toJs(rt, value.a));
result.setProperty(rt, "b", bridging::toJs(rt, value.b));
if (value.c) {
result.setProperty(rt, "c", bridging::toJs(rt, value.c.value()));
}
return result;
}
};
```
and you can use it in our C++ TurboModule for example as:
```
using ObjectStruct = NativeCxxModuleExampleCxxBaseObjectStruct<
int32_t,
std::string,
std::optional<std::string>>;
template <>
struct Bridging<ObjectStruct>
: NativeCxxModuleExampleCxxBaseObjectStructBridging<
int32_t,
std::string,
std::optional<std::string>> {};
```
or as
```
using ObjectStruct = NativeCxxModuleExampleCxxBaseObjectStruct<
float,
folly::StringPiece,
std::optional<std::string>>;
template <>
struct Bridging<ObjectStruct>
: NativeCxxModuleExampleCxxBaseObjectStructBridging<
float,
folly::StringPiece,
std::optional<std::string>> {};
```
Or as
...
Changelog: [Internal]
Differential Revision: D41133761
fbshipit-source-id: 9ed5a118834e067951b5b45d2551188dff38219f
@facebook-github-bot

Copy link
Copy Markdown
Contributor

This pull request was exported from Phabricator. Differential Revision: D41133761

@pull-bot

Copy link
Copy Markdown

PR build artifact for 7ffab16 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.

kelset pushed a commit that referenced this pull request Nov 22, 2022
Summary:
Pull Request resolved: #35265
This adds a templating layer for C++ TurboModules to automatically generate struct templates from TurboModule specs.
You have to define concrete types for those templates to use them in your C++ TurboModule.
E.g. for the JS flow type:
```
export type ObjectStruct = {|
a: number,
b: string,
c?: ?string,
|};
```
code-gen will now generate the following template code:
```
#pragma mark - NativeCxxModuleExampleCxxBaseObjectStruct
template <typename P0, typename P1, typename P2>
struct NativeCxxModuleExampleCxxBaseObjectStruct {
P0 a;
P1 b;
P2 c;
bool operator==(const NativeCxxModuleExampleCxxBaseObjectStruct &other) const {
return a == other.a && b == other.b && c == other.c;
}
};
template <typename P0, typename P1, typename P2>
struct NativeCxxModuleExampleCxxBaseObjectStructBridging {
static NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> fromJs(
jsi::Runtime &rt,
const jsi::Object &value,
const std::shared_ptr<CallInvoker> &jsInvoker) {
NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> result{
bridging::fromJs<P0>(rt, value.getProperty(rt, "a"), jsInvoker),
bridging::fromJs<P1>(rt, value.getProperty(rt, "b"), jsInvoker),
bridging::fromJs<P2>(rt, value.getProperty(rt, "c"), jsInvoker)};
return result;
}
static jsi::Object toJs(
jsi::Runtime &rt,
const NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> &value) {
auto result = facebook::jsi::Object(rt);
result.setProperty(rt, "a", bridging::toJs(rt, value.a));
result.setProperty(rt, "b", bridging::toJs(rt, value.b));
if (value.c) {
result.setProperty(rt, "c", bridging::toJs(rt, value.c.value()));
}
return result;
}
};
```
and you can use it in our C++ TurboModule for example as:
```
using ObjectStruct = NativeCxxModuleExampleCxxBaseObjectStruct<
int32_t,
std::string,
std::optional<std::string>>;
template <>
struct Bridging<ObjectStruct>
: NativeCxxModuleExampleCxxBaseObjectStructBridging<
int32_t,
std::string,
std::optional<std::string>> {};
```
or as
```
using ObjectStruct = NativeCxxModuleExampleCxxBaseObjectStruct<
float,
folly::StringPiece,
std::optional<std::string>>;
template <>
struct Bridging<ObjectStruct>
: NativeCxxModuleExampleCxxBaseObjectStructBridging<
float,
folly::StringPiece,
std::optional<std::string>> {};
```
Or as
...
Changelog: [Internal]
Reviewed By: rshest
Differential Revision: D41133761
fbshipit-source-id: fdf36e51073cb46c5234f6121842c79a884899c7
christophpurrer added a commit to christophpurrer/react-native-macos that referenced this pull request Dec 16, 2022
Summary:
Pull Request resolved: react#35656
Changelog: [Internal]
## Change:
react#35265 added a struct generator - but it is not type safe :-(
E.g. you can write a TM Spec type as:
```
export type CustomType = {
key: string;
enabled: boolean;
time?: number;
}
```
And a C++ type as:
```
using CustomType = NativeSampleModuleBaseCustomType<float, bool, std::optional<int32_t>>;
template <>
struct Bridging<CustomType>
: NativeSampleModuleBaseCustomTypeBridging<float, bool, std::optional<int32_t>> {};
```
and it will still compile :-( - which should not.
The reason is that the generated structs don't validate the members type :-(
```
template <typename P0, typename P1, typename P2>
struct NativeSampleModuleBaseCustomType {
P0 key;
P1 enabled;
P2 time;
bool operator==(const NativeSampleModuleBaseCustomType &other) const {
return key == other.key && enabled == other.enabled && time == other.time;
}
};
template <typename P0, typename P1, typename P2>
struct NativeSampleModuleBaseCustomTypeBridging {
static NativeSampleModuleBaseCustomType<P0, P1, P2> fromJs(
jsi::Runtime &rt,
const jsi::Object &value,
const std::shared_ptr<CallInvoker> &jsInvoker) {
NativeSampleModuleBaseCustomType<P0, P1, P2> result{
bridging::fromJs<P0>(rt, value.getProperty(rt, "key"), jsInvoker),
bridging::fromJs<P1>(rt, value.getProperty(rt, "enabled"), jsInvoker),
bridging::fromJs<P2>(rt, value.getProperty(rt, "time"), jsInvoker)};
return result;
}
static jsi::Object toJs(
jsi::Runtime &rt,
const NativeSampleModuleBaseCustomType<P0, P1, P2> &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()));
}
keyToJs(rt, value.key);
return result;
}
};
```
This fixes that, by simply emitting conversion functions for each member such as
```
#ifdef DEBUG
static bool keyToJs(jsi::Runtime &rt, P0 value) {
return bridging::toJs(rt, value);
}
static double enabledToJs(jsi::Runtime &rt, P1 value) {
return bridging::toJs(rt, value);
}
static jsi::String timeToJs(jsi::Runtime &rt, P2 value) {
return bridging::toJs(rt, value);
}
#endif
```
Differential Revision: D42082423
fbshipit-source-id: c7857944ba5d5a57623fc627df4d885867b025cf
facebook-github-bot pushed a commit that referenced this pull request Dec 16, 2022
Summary:
Pull Request resolved: #35656
Changelog: [Internal]
## Change:
#35265 added a struct generator - but it is not type safe :-(
E.g. you can write a TM Spec type as:
```
export type CustomType = {
key: string;
enabled: boolean;
time?: number;
}
```
And a C++ type as:
```
using CustomType = NativeSampleModuleBaseCustomType<float, bool, std::optional<int32_t>>;
template <>
struct Bridging<CustomType>
: NativeSampleModuleBaseCustomTypeBridging<float, bool, std::optional<int32_t>> {};
```
and it will still compile :-( - which should not.
The reason is that the generated structs don't validate the members type :-(
```
template <typename P0, typename P1, typename P2>
struct NativeSampleModuleBaseCustomType {
P0 key;
P1 enabled;
P2 time;
bool operator==(const NativeSampleModuleBaseCustomType &other) const {
return key == other.key && enabled == other.enabled && time == other.time;
}
};
template <typename P0, typename P1, typename P2>
struct NativeSampleModuleBaseCustomTypeBridging {
static NativeSampleModuleBaseCustomType<P0, P1, P2> fromJs(
jsi::Runtime &rt,
const jsi::Object &value,
const std::shared_ptr<CallInvoker> &jsInvoker) {
NativeSampleModuleBaseCustomType<P0, P1, P2> result{
bridging::fromJs<P0>(rt, value.getProperty(rt, "key"), jsInvoker),
bridging::fromJs<P1>(rt, value.getProperty(rt, "enabled"), jsInvoker),
bridging::fromJs<P2>(rt, value.getProperty(rt, "time"), jsInvoker)};
return result;
}
static jsi::Object toJs(
jsi::Runtime &rt,
const NativeSampleModuleBaseCustomType<P0, P1, P2> &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()));
}
keyToJs(rt, value.key);
return result;
}
};
```
This fixes that, by simply emitting conversion functions for each member such as
```
#ifdef DEBUG
static bool keyToJs(jsi::Runtime &rt, P0 value) {
return bridging::toJs(rt, value);
}
static double enabledToJs(jsi::Runtime &rt, P1 value) {
return bridging::toJs(rt, value);
}
static jsi::String timeToJs(jsi::Runtime &rt, P2 value) {
return bridging::toJs(rt, value);
}
#endif
```
Reviewed By: cipolleschi
Differential Revision: D42082423
fbshipit-source-id: 5133f14e2aa8351e9bbbf614117a3d5894b17fa6
@cipolleschicipolleschi mentioned this pull request Oct 11, 2023
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.fb-exportedp: FacebookPartner: FacebookPartner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@christophpurrer@facebook-github-bot@analysis-bot@pull-bot