Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Avanzado: Tipos C++ Personalizados
Esta guía asume que estás familiarizado con la guía de Módulos Turbo Nativos en C++ Puro. Continuaremos sobre esa base.
Los Módulos Turbo Nativos en C++ incluyen funcionalidad de bridging para la mayoría de tipos estándar std::. Puedes usar estos tipos en tus módulos sin código adicional.
Si deseas agregar soporte para tipos nuevos y personalizados en tu app o biblioteca, debes proporcionar el archivo de cabecera bridging necesario.
Agregar un Tipo Personalizado: Int64
Los Módulos Turbo Nativos en C++ aún no soportan números int64_t porque JavaScript no admite números mayores a 2^53. Para representar números mayores, podemos usar tipo string en JS y convertirlo automáticamente a int64_t en C++.
1. Crear el archivo de cabecera Bridging
El primer paso para soportar un tipo personalizado es definir la cabecera de bridging que maneja la conversión desde la representación JS a C++, y hacia la representación JS desde C++.
-
En la carpeta
shared, añade un nuevo archivo llamadoInt64.h -
Agrega el siguiente código a ese archivo:
#pragma once
#include <react/bridging/Bridging.h>
namespace facebook::react {
template <>
struct Bridging<int64_t> {
// 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));
}
};
}
Los componentes clave para tu cabecera de bridging personalizada son:
-
Especialización explícita de la estructura
Bridgingpara tu tipo personalizado. En este caso, la plantilla especifica el tipoint64_t. -
Una función
fromJspara convertir desde la representación JS a C++ -
Una función
toJspara convertir desde la representación C++ a JS
En iOS, recuerda añadir el archivo Int64.h al proyecto de Xcode.
2. Modificar la Especificación JS
Ahora podemos modificar la especificación JS para añadir un método que use el nuevo tipo. Como siempre, podemos usar Flow o TypeScript para nuestras especificaciones.
-
Abre
specs/NativeSampleTurbomodule -
Modifica la especificación como sigue:
- TypeScript
- Flow
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<Spec>(
'NativeSampleModule',
);
// @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<Spec>(
"NativeSampleModule"
): Spec);
En estos archivos, definimos la función que debe implementarse en C++.
3. Implementar el código nativo
Ahora necesitamos implementar la función declarada en la especificación JS.
- Abre el archivo
specs/NativeSampleModule.hy aplica estos cambios:
#pragma once
#include <AppSpecsJSI.h>
#include <memory>
#include <string>
+ #include "Int64.h"
namespace facebook::react {
class NativeSampleModule : public NativeSampleModuleCxxSpec<NativeSampleModule> {
public:
NativeSampleModule(std::shared_ptr<CallInvoker> jsInvoker);
std::string reverseString(jsi::Runtime& rt, std::string input);
+ int32_t cubicRoot(jsi::Runtime& rt, int64_t input);
};
} // namespace facebook::react
- Abre el archivo
specs/NativeSampleModule.cppe implementa la nueva función:
#include "NativeSampleModule.h"
+ #include <cmath>
namespace facebook::react {
NativeSampleModule::NativeSampleModule(std::shared_ptr<CallInvoker> 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
La implementación importa la biblioteca C++ <cmath> para operaciones matemáticas, luego implementa la función cubicRoot usando el primitivo cbrt de <cmath>.
4. Probar tu Código en la App
Ahora podemos probar el código en nuestra aplicación.
Primero, actualizamos el archivo App.tsx para usar el nuevo método del TurboModule. Luego, podemos compilar para Android e iOS.
- Abre
App.tsxy aplica estos cambios:
// ...
+ const [cubicSource, setCubicSource] = React.useState('')
+ const [cubicRoot, setCubicRoot] = React.useState(0)
return (
<SafeAreaView style={styles.container}>
<View>
<Text style={styles.title}>
Welcome to C++ Turbo Native Module Example
</Text>
<Text>Write down here the text you want to revert</Text>
<TextInput
style={styles.textInput}
placeholder="Write your text here"
onChangeText={setValue}
value={value}
/>
<Button title="Reverse" onPress={onPress} />
<Text>Reversed text: {reversedValue}</Text>
+ <Text>For which number do you want to compute the Cubic Root?</Text>
+ <TextInput
+ style={styles.textInput}
+ placeholder="Write your text here"
+ onChangeText={setCubicSource}
+ value={cubicSource}
+ />
+ <Button title="Get Cubic Root" onPress={() => setCubicRoot(SampleTurboModule.cubicRoot(cubicSource))} />
+ <Text>The cubic root is: {cubicRoot}</Text>
</View>
</SafeAreaView>
);
}
//...
-
Para probar en Android, ejecuta
yarn androiddesde la carpeta raíz de tu proyecto. -
Para probar en iOS, ejecuta
yarn iosdesde la carpeta raíz de tu proyecto.
Agregar un Tipo Estructurado Personalizado: Dirección
Este enfoque se puede generalizar a cualquier tipo de dato. Para tipos estructurados, React Native ofrece funciones auxiliares que facilitan su conversión entre JavaScript y C++.
Supongamos que queremos implementar un tipo personalizado Address con estas propiedades:
interface Address {
street: string;
num: number;
isInUS: boolean;
}
1. Definir el tipo en las especificaciones
Primero definamos el nuevo tipo personalizado en las especificaciones JS para que Codegen genere automáticamente el código de soporte.
- Abre el archivo
specs/NativeSampleModuley realiza estos cambios:
- TypeScript
- Flow
import {TurboModule, TurboModuleRegistry} from 'react-native';
+export type Address = {
+ street: string,
+ num: number,
+ isInUS: boolean,
+};
export interface Spec extends TurboModule {
readonly reverseString: (input: string) => string;
+ readonly validateAddress: (input: Address) => boolean;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'NativeSampleModule',
);
// @flow
import type {TurboModule} from 'react-native';
import { TurboModuleRegistry } from "react-native";
+export type Address = {
+ street: string,
+ num: number,
+ isInUS: boolean,
+};
export interface Spec extends TurboModule {
+reverseString: (input: string) => string;
+ +validateAddress: (input: Address) => boolean;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
"NativeSampleModule"
): Spec);
Este código define el nuevo tipo Address y declara una función validateAddress para el Turbo Native Module. Observa que validateFunction requiere un objeto Address como parámetro.
También es posible tener funciones que retornen tipos personalizados.
2. Definir el código de conversión
A partir del tipo Address definido en las especificaciones, Codegen generará dos tipos auxiliares: NativeSampleModuleAddress y NativeSampleModuleAddressBridging.
El primer tipo define la estructura de Address. El segundo contiene la infraestructura para convertir el tipo personalizado entre JS y C++. Solo necesitamos definir una especialización de Bridging que extienda NativeSampleModuleAddressBridging.
-
Abre el archivo
shared/NativeSampleModule.h -
Agrega este código:
#include "Int64.h"
#include <memory>
#include <string>
namespace facebook::react {
+ using Address = NativeSampleModuleAddress<std::string, int32_t, bool>;
+ template <>
+ struct Bridging<Address>
+ : NativeSampleModuleAddressBridging<Address> {};
// ...
}
Este código define un alias Address para el tipo genérico NativeSampleModuleAddress. El orden de los genéricos es importante: el primer argumento del template corresponde al primer tipo de dato de la estructura, el segundo al siguiente, etc.
Luego, el código agrega la especialización de Bridging para Address extendiendo NativeSampleModuleAddressBridging generado por Codegen.
Existe una convención para generar estos tipos:
- La primera parte del nombre siempre es el tipo del módulo (
NativeSampleModuleen este ejemplo) - La segunda parte siempre es el nombre del tipo JS definido en las especificaciones (
Addressaquí)
3. Implementar el código nativo
Ahora implementaremos la función validateAddress en C++. Primero declaramos la función en el archivo .h, luego la implementamos en .cpp.
- Abre
shared/NativeSampleModule.hy agrega la definición de la función
std::string reverseString(jsi::Runtime& rt, std::string input);
+ bool validateAddress(jsi::Runtime &rt, jsi::Object input);
};
} // namespace facebook::react
- Abre
shared/NativeSampleModule.cppy agrega la implementación
bool NativeSampleModule::validateAddress(jsi::Runtime &rt, jsi::Object input) {
std::string street = input.getProperty(rt, "street").asString(rt).utf8(rt);
int32_t number = input.getProperty(rt, "num").asNumber();
return !street.empty() && number > 0;
}
En la implementación, el objeto que representa Address es un jsi::Object. Para extraer sus valores usamos los accesores proporcionados por JSI:
-
getProperty()obtiene una propiedad del objeto por nombre -
asString()convierte la propiedad ajsi::String -
utf8()conviertejsi::Stringastd::string -
asNumber()convierte la propiedad adouble
Tras analizar manualmente el objeto, implementamos la lógica necesaria.
Para profundizar en JSI, mira esta excelente charla de App.JS 2024
4. Probar el código en la aplicación
Para probar el código, modificamos el archivo App.tsx.
-
Abre el archivo
App.tsx. Elimina el contenido de la funciónApp(). -
Reemplaza el cuerpo de la función
App()con el siguiente código:
const [street, setStreet] = React.useState('');
const [num, setNum] = React.useState('');
const [isValidAddress, setIsValidAddress] = React.useState<
boolean | null
>(null);
const onPress = () => {
let houseNum = parseInt(num, 10);
if (isNaN(houseNum)) {
houseNum = -1;
}
const address = {
street,
num: houseNum,
isInUS: false,
};
const result = SampleTurboModule.validateAddress(address);
setIsValidAddress(result);
};
return (
<SafeAreaView style={styles.container}>
<View>
<Text style={styles.title}>
Welcome to C Turbo Native Module Example
</Text>
<Text>Address:</Text>
<TextInput
style={styles.textInput}
placeholder="Write your address here"
onChangeText={setStreet}
value={street}
/>
<Text>Number:</Text>
<TextInput
style={styles.textInput}
placeholder="Write your address here"
onChangeText={setNum}
value={num}
/>
<Button title="Validate" onPress={onPress} />
{isValidAddress != null && (
<Text>
Your address is {isValidAddress ? 'valid' : 'not valid'}
</Text>
)}
</View>
</SafeAreaView>
);
¡Felicidades! 🎉
Acabas de hacer el bridge de tus primeros tipos desde JS a C++.