From 8f6f698fb551cf7f82a4ce41ae1c309a29a6bd27 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 15 Oct 2024 15:42:02 +0100 Subject: [PATCH] [RN][Docs] Add guide on custom types in C++ (#4269) * [WIP]Add guide for Pure Cxx TM * Integrate C++ code with iOS * Remove video if too big * Readd video * Add base info for Android * Add Android integration * fix sidebar * Fix linter * Fix case police * Apply first round of feedbacks * Apply other review feedback * Add guide on custom types in C++ * fix broken link * Apply review feedback * Fix note block --- docs/the-new-architecture/custom-cxx-types.md | 422 ++++++++++++++++++ website/sidebars.json | 5 +- 2 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 docs/the-new-architecture/custom-cxx-types.md diff --git a/docs/the-new-architecture/custom-cxx-types.md b/docs/the-new-architecture/custom-cxx-types.md new file mode 100644 index 00000000000..f851e408636 --- /dev/null +++ b/docs/the-new-architecture/custom-cxx-types.md @@ -0,0 +1,422 @@ +import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import constants from '@site/core/TabsConstants'; + +# Advanced: Custom C++ Types + +:::note +This guide assumes that you are familiar with the [**Pure C++ Turbo Native Modules**](pure-cxx-modules.md) guide. This will build on top of that guide. +::: + +C++ Turbo Native Modules support [bridging functionality](https://github.com/facebook/react-native/tree/main/packages/react-native/ReactCommon/react/bridging) for most `std::` standard types. You can use most of those types in your modules without any additional code required. + +If you want to add support for new and custom types in your app or library, you need to provide the necessary `bridging` header file. + +## Adding a New Custom: Int64 + +C++ Turbo Native Modules don't support `int64_t` numbers yet - because JavaScript doesn't support numbers greater 2^53. To represent numbers greater than 2^53, we can use a `string` type in JS and automatically convert it to `int64_t` in C++. + +### 1. Create the Bridging Header file + +The first step to support a new custom type is to define the bridging header that takes care of converting the type **from** the JS representation to the C++ representation, and from the C++ representation **to** the JS one. + +1. In the `shared` folder, add a new file called `Int64.h` +2. Add the following code to that file: + +```cpp title="Int64.h" +#pragma once + +#include + +namespace facebook::react { + +template <> +struct Bridging { + // Converts from the JS representation to the C++ representation + 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()); + } + } + + // Converts from the C++ representation to the JS representation + static jsi::String toJs(jsi::Runtime &rt, int64_t value) { + return bridging::toJs(rt, std::to_string(value)); + } +}; + +} +``` + +The key components for your custom bridging header are: + +- Explicit specialization of the `Bridging` struct for your custom type. In this case, the template specify the `int64_t` type. +- A `fromJs` function to convert from the JS representation to the C++ representation +- A `toJs` function to convert from the C++ representation to the JS representation + +:::note +On iOS, remember to add the `Int64.h` file to the Xcode project. +::: + +### 2. Modify the JS Spec + +Now, we can modify the JS spec to add a method that uses the new type. As usual, we can use either Flow or TypeScript for our specs. + +1. Open the `specs/NativeSampleTurbomodule` +2. Modify the spec as follows: + + + + +```diff title="NativeSampleModule.ts" +import {TurboModule, TurboModuleRegistry} from 'react-native'; + +export interface Spec extends TurboModule { + readonly reverseString: (input: string) => string; ++ readonly cubicRoot: (input: string) => number; +} + +export default TurboModuleRegistry.getEnforcing( + 'NativeSampleModule', +); +``` + + + + +```diff title="NativeSampleModule.js" +// @flow +import type {TurboModule} from 'react-native'; +import { TurboModuleRegistry } from "react-native"; + +export interface Spec extends TurboModule { + +reverseString: (input: string) => string; ++ +cubicRoot: (input: string) => number; +} + +export default (TurboModuleRegistry.getEnforcing( + "NativeSampleModule" +): Spec); +``` + + + + +In this files, we are defining the function that needs to be implemented in C++. + +### 3. Implement the Native Code. + +Now, we need to implement the function that we declared in the JS specification. + +1. Open the `specs/NativeSampleModule.h` file and apply the following changes: + +```diff title="NativeSampleModule.h" +#pragma once + +#include +#include +#include + ++ #include "Int64.h" + +namespace facebook::react { + +class NativeSampleModule : public NativeSampleModuleCxxSpec { +public: + NativeSampleModule(std::shared_ptr jsInvoker); + + std::string reverseString(jsi::Runtime& rt, std::string input); ++ int32_t cubicRoot(jsi::Runtime& rt, int64_t input); +}; + +} // namespace facebook::react + +``` + +2. Open the `specs/NativeSampleModule.cpp` file and apply the implement the new function: + +```diff title="NativeSampleModule.cpp" +#include "NativeSampleModule.h" ++ #include + +namespace facebook::react { + +NativeSampleModule::NativeSampleModule(std::shared_ptr jsInvoker) + : NativeSampleModuleCxxSpec(std::move(jsInvoker)) {} + +std::string NativeSampleModule::reverseString(jsi::Runtime& rt, std::string input) { + return std::string(input.rbegin(), input.rend()); +} + ++int32_t NativeSampleModule::cubicRoot(jsi::Runtime& rt, int64_t input) { ++ return std::cbrt(input); ++} + +} // namespace facebook::react +``` + +The implementation imports the `` C++ library to perform mathematical operations, then it implements the `cubicRoot` function usinf the `cbrt` primitive from the `` module. + +### 4. Test your code in Your App + +Now, we can test the code in our app. + +First, we need to update the `App.tsx` file to use the new method from the TurboModule. Then, we can build our apps in Android and iOS. + +1. Open the `App.tsx` code apply the following changes: + +```diff title="App.tsx" +// ... ++ const [cubicSource, setCubicSource] = React.useState('') ++ const [cubicRoot, setCubicRoot] = React.useState(0) + return ( + + + + Welcome to C++ Turbo Native Module Example + + Write down here the text you want to revert + +