Skip to content
New issue

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

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

Already on GitHub? # 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
# for free to join this conversation on GitHub. Already have an account? # 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