Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed

Conversation

christophpurrer
Copy link
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-bot facebook-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
Contributor

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

@analysis-bot
Copy link

Platform Engine Arch Size (bytes) Diff
ios - universal n/a --

Base commit: cf37479
Branch: main

@analysis-bot
Copy link

analysis-bot commented Nov 9, 2022

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 7,102,063 +31,923
android hermes armeabi-v7a 6,471,837 +29,553
android hermes x86 7,519,581 +34,445
android hermes x86_64 7,378,156 +33,438
android jsc arm64-v8a 8,966,324 +31,485
android jsc armeabi-v7a 7,698,075 +29,127
android jsc x86 9,028,908 +34,022
android jsc x86_64 9,506,829 +33,004

Base commit: cf37479
Branch: main

@facebook-github-bot
Copy link
Contributor

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

christophpurrer added a commit to christophpurrer/react-native-macos that referenced this pull request Nov 9, 2022
Summary:
Pull Request resolved: facebook#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: ec4e944fed6304b3b6a59dad3952abc9b98644ac
@pull-bot
Copy link

pull-bot commented Nov 9, 2022

PR build artifact for 6b120b5 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.

christophpurrer and 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: facebook#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
Contributor

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

@pull-bot
Copy link

pull-bot commented Nov 9, 2022

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 15, 2022
Summary:
Changelog: [Internal]

## Change:

facebook#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: 0022bcc3ce292c38dffc051c25959b2e3d5b17cb
christophpurrer added a commit to christophpurrer/react-native-macos that referenced this pull request Dec 16, 2022
Summary:
Pull Request resolved: facebook#35656

Changelog: [Internal]

## Change:

facebook#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
OlimpiaZurek pushed a commit to OlimpiaZurek/react-native that referenced this pull request May 22, 2023
Summary:
Pull Request resolved: facebook#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
OlimpiaZurek pushed a commit to OlimpiaZurek/react-native that referenced this pull request May 22, 2023
Summary:
Pull Request resolved: facebook#35656

Changelog: [Internal]

## Change:

facebook#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
@cipolleschi cipolleschi mentioned this pull request Oct 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. fb-exported p: Facebook Partner: Facebook Partner
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants