Skip to content

Commit ba2313a

Browse files
authored
feat: Add enums to the HybridObject (#7)
Adds support for enums to `HybridObject`: When declaring custom enums there's two steps you need to do: 1. Create your enum (e.g. in `TestEnum.h`) ```cpp enum TestEnum { FIRST, SECOND, THIRD }; ``` 2. Add the enum to the `EnumMapper` by editing `cpp/jsi/EnumMapper.h`: ```cpp template<> struct EnumMapper<TestEnum> { public: static constexpr TestEnum fromJSUnion(const std::string& jsUnion) { if (jsUnion == "first") return FIRST; if (jsUnion == "second") return SECOND; if (jsUnion == "third") return THIRD; throw invalidUnion(jsUnion); } static std::string toJSUnion(TestEnum value) { switch (value) { case FIRST: return "first"; case SECOND: return "second"; case THIRD: return "third"; } throw invalidEnum(value); } }; ``` This way compile-time safety is guaranteed. All other approaches to parse enums from C++ to JS are not compile-time but rather run-time, so currently there's no way around editing `EnumMapper.h` unless we do runtime type checking.
1 parent 42a1c60 commit ba2313a

File tree

7 files changed

+106
-4
lines changed

7 files changed

+106
-4
lines changed

package/cpp/jsi/EnumMapper.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// Created by Marc Rousavy on 22.02.24.
3+
//
4+
5+
#pragma once
6+
7+
#include "test/TestEnum.h"
8+
#include <unordered_map>
9+
10+
namespace margelo {
11+
12+
using namespace facebook;
13+
14+
static std::runtime_error invalidUnion(const std::string jsUnion) {
15+
return std::runtime_error("Cannot convert JS Value to Enum: Invalid Union value passed! (\"" + jsUnion + "\")");
16+
}
17+
template <typename Enum> static std::runtime_error invalidEnum(Enum passedEnum) {
18+
return std::runtime_error("Cannot convert Enum to JS Value: Invalid Enum passed! (Value #" + std::to_string(passedEnum) +
19+
" does not exist in " + typeid(Enum).name() + ")");
20+
}
21+
22+
template <typename Enum> struct EnumMapper {
23+
static Enum fromJSUnion(const std::string&) {
24+
static_assert(always_false<Enum>::value, "This type is not supported by the EnumMapper!");
25+
return Enum();
26+
}
27+
static std::string toJSUnion(Enum) {
28+
static_assert(always_false<Enum>::value, "This type is not supported by the EnumMapper!");
29+
return std::string();
30+
}
31+
32+
private:
33+
template <typename> struct always_false : std::false_type {};
34+
};
35+
36+
template <> struct EnumMapper<TestEnum> {
37+
public:
38+
static constexpr TestEnum fromJSUnion(const std::string& jsUnion) {
39+
if (jsUnion == "first")
40+
return FIRST;
41+
if (jsUnion == "second")
42+
return SECOND;
43+
if (jsUnion == "third")
44+
return THIRD;
45+
throw invalidUnion(jsUnion);
46+
}
47+
static std::string toJSUnion(TestEnum value) {
48+
switch (value) {
49+
case FIRST:
50+
return "first";
51+
case SECOND:
52+
return "second";
53+
case THIRD:
54+
return "third";
55+
}
56+
throw invalidEnum(value);
57+
}
58+
};
59+
60+
} // namespace margelo

package/cpp/jsi/JSIConverter.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include "EnumMapper.h"
78
#include "HybridObject.h"
89
#include <array>
910
#include <jsi/jsi.h>
@@ -100,6 +101,17 @@ template <typename TInner> struct JSIConverter<std::optional<TInner>> {
100101
}
101102
};
102103

104+
template <typename TEnum> struct JSIConverter<TEnum, std::enable_if_t<std::is_enum<TEnum>::value>> {
105+
static TEnum fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
106+
std::string string = arg.asString(runtime).utf8(runtime);
107+
return EnumMapper<TEnum>::fromJSUnion(string);
108+
}
109+
static jsi::Value toJSI(jsi::Runtime& runtime, TEnum arg) {
110+
std::string string = EnumMapper<TEnum>::toJSUnion(arg);
111+
return jsi::String::createFromUtf8(runtime, string);
112+
}
113+
};
114+
103115
template <typename ReturnType, typename... Args> struct JSIConverter<std::function<ReturnType(Args...)>> {
104116
static std::function<ReturnType(Args...)> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
105117
jsi::Function function = arg.asObject(runtime).asFunction(runtime);

package/cpp/test/TestEnum.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// Created by Marc Rousavy on 22.02.24.
3+
//
4+
5+
#pragma once
6+
7+
#include "jsi/EnumMapper.h"
8+
9+
namespace margelo {
10+
11+
enum TestEnum { FIRST, SECOND, THIRD };
12+
13+
}

package/cpp/test/TestHybridObject.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ void TestHybridObject::loadHybridMethods() {
1212
// this.string get & set
1313
registerHybridGetter("string", &TestHybridObject::getString, this);
1414
registerHybridSetter("string", &TestHybridObject::setString, this);
15+
// this.enum
16+
registerHybridGetter("enum", &TestHybridObject::getEnum, this);
17+
registerHybridSetter("enum", &TestHybridObject::setEnum, this);
1518
// methods
1619
registerHybridMethod("multipleArguments", &TestHybridObject::multipleArguments, this);
1720
// callbacks

package/cpp/test/TestHybridObject.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include "TestEnum.h"
78
#include "jsi/HybridObject.h"
89
#include <string>
910
#include <vector>
@@ -24,6 +25,12 @@ class TestHybridObject : public HybridObject {
2425
void setString(std::string newValue) {
2526
_string = newValue;
2627
}
28+
void setEnum(TestEnum testEnum) {
29+
_enum = testEnum;
30+
}
31+
TestEnum getEnum() {
32+
return _enum;
33+
}
2734

2835
std::unordered_map<std::string, double> multipleArguments(int first, bool second, std::string third) {
2936
return std::unordered_map<std::string, double>{{"first", 5312}, {"second", 532233}, {"third", 2786}};
@@ -42,6 +49,7 @@ class TestHybridObject : public HybridObject {
4249
private:
4350
int _int;
4451
std::string _string;
52+
TestEnum _enum;
4553

4654
void loadHybridMethods() override;
4755
};

package/src/native/FilamentProxy.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ interface TestHybridObject {
88
getIntGetter(): () => number
99
sayHelloCallback(callback: () => string): void
1010
createNewHybridObject: () => TestHybridObject
11+
enum: 'first' | 'second' | 'third'
1112
}
1213

1314
export interface TFilamentProxy {

package/src/test/TestHybridObject.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,26 @@ export function testHybridObject() {
1515
hybridObject.int = 6723
1616
console.log(`New Int: ${hybridObject.int}`)
1717

18-
// 4. String Getter & Setter
18+
// 4. Enum Getter & Setter
19+
console.log(`Enum: ${hybridObject.enum}`)
20+
hybridObject.enum = 'second'
21+
console.log(`New Enum: ${hybridObject.enum}`)
22+
23+
// 5. String Getter & Setter
1924
console.log(`String: ${hybridObject.string}`)
2025
hybridObject.string = 'new string value!'
2126
console.log(`New String: ${hybridObject.string}`)
2227

23-
// 5. Testing multiple arguments and maps
28+
// 6. Testing multiple arguments and maps
2429
const result = hybridObject.multipleArguments(5, true, 'hahah!')
2530
console.log(`multipleArguments() -> ${JSON.stringify(result)}`)
2631

27-
// 6. Testing callbacks
32+
// 7. Testing callbacks
2833
hybridObject.sayHelloCallback(() => 'hello from JS!')
2934
const getter = hybridObject.getIntGetter()
3035
console.log(`Int getter: ${getter()}`)
3136

32-
// 7. Create a new one
37+
// 8. Create a new one
3338
const newObject = hybridObject.createNewHybridObject()
3439
console.log(`Created new hybrid object!`, newObject)
3540

0 commit comments

Comments
 (0)