From c9b106a24cac675148e2c7f684554fc30327cbb7 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 17 Jan 2024 22:10:28 +0000 Subject: [PATCH] embind: Use optional return type for vector and maps. This helps create better TypeScript definitions for what is actually returned. --- system/include/emscripten/bind.h | 59 ++++++++++++++++++++++++++++---- test/other/embind_tsgen.cpp | 2 ++ test/other/embind_tsgen.d.ts | 13 +++++-- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 5cad86590ce6..f29343e21b6a 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -1875,6 +1875,21 @@ class class_ { } }; +#if __cplusplus >= 201703L +template +void register_optional() { + // Optional types are automatically registered for some internal types so + // only run the register method once so we don't conflict with a user's + // bindings if they also register the optional type. + thread_local bool hasRun; + if (hasRun) { + return; + } + hasRun = true; + internal::_embind_register_optional(internal::TypeID>::get(), internal::TypeID::get()); +} +#endif + //////////////////////////////////////////////////////////////////////////////// // VECTORS //////////////////////////////////////////////////////////////////////////////// @@ -1883,6 +1898,20 @@ namespace internal { template struct VectorAccess { +// This nearly duplicated code is used for generating more specific TypeScript +// types when using more modern C++ versions. +#if __cplusplus >= 201703L + static std::optional get( + const VectorType& v, + typename VectorType::size_type index + ) { + if (index < v.size()) { + return v[index]; + } else { + return {}; + } + } +#else static val get( const VectorType& v, typename VectorType::size_type index @@ -1893,6 +1922,7 @@ struct VectorAccess { return val::undefined(); } } +#endif static bool set( VectorType& v, @@ -1909,6 +1939,9 @@ struct VectorAccess { template class_> register_vector(const char* name) { typedef std::vector VecType; +#if __cplusplus >= 201703L + register_optional(); +#endif void (VecType::*push_back)(const T&) = &VecType::push_back; void (VecType::*resize)(const size_t, const T&) = &VecType::resize; @@ -1923,13 +1956,6 @@ class_> register_vector(const char* name) { ; } -#if __cplusplus >= 201703L -template -void register_optional() { - internal::_embind_register_optional(internal::TypeID>::get(), internal::TypeID::get()); -} -#endif - //////////////////////////////////////////////////////////////////////////////// // MAPS //////////////////////////////////////////////////////////////////////////////// @@ -1938,6 +1964,21 @@ namespace internal { template struct MapAccess { +// This nearly duplicated code is used for generating more specific TypeScript +// types when using more modern C++ versions. +#if __cplusplus >= 201703L + static std::optional get( + const MapType& m, + const typename MapType::key_type& k + ) { + auto i = m.find(k); + if (i == m.end()) { + return {}; + } else { + return i->second; + } + } +#else static val get( const MapType& m, const typename MapType::key_type& k @@ -1949,6 +1990,7 @@ struct MapAccess { return val(i->second); } } +#endif static void set( MapType& m, @@ -1975,6 +2017,9 @@ struct MapAccess { template class_> register_map(const char* name) { typedef std::map MapType; +#if __cplusplus >= 201703L + register_optional(); +#endif size_t (MapType::*size)() const = &MapType::size; return class_(name) diff --git a/test/other/embind_tsgen.cpp b/test/other/embind_tsgen.cpp index 09f309399a0b..d85024c62c28 100644 --- a/test/other/embind_tsgen.cpp +++ b/test/other/embind_tsgen.cpp @@ -165,6 +165,8 @@ EMSCRIPTEN_BINDINGS(Test) { register_vector("IntVec"); + register_map("MapIntInt"); + class_("Foo").function("process", &Foo::process); function("global_fn", &global_fn); diff --git a/test/other/embind_tsgen.d.ts b/test/other/embind_tsgen.d.ts index 56abfe50cac5..aa36d14402c7 100644 --- a/test/other/embind_tsgen.d.ts +++ b/test/other/embind_tsgen.d.ts @@ -28,8 +28,16 @@ export interface IntVec { push_back(_0: number): void; resize(_0: number, _1: number): void; size(): number; + get(_0: number): number | undefined; set(_0: number, _1: number): boolean; - get(_0: number): any; + delete(): void; +} + +export interface MapIntInt { + keys(): IntVec; + get(_0: number): number | undefined; + set(_0: number, _1: number): void; + size(): number; delete(): void; } @@ -79,6 +87,7 @@ export interface MainModule { EmptyEnum: {}; enum_returning_fn(): Bar; IntVec: {new(): IntVec}; + MapIntInt: {new(): MapIntInt}; Foo: {}; ClassWithConstructor: {new(_0: number, _1: ValArr): ClassWithConstructor}; ClassWithTwoConstructors: {new(): ClassWithTwoConstructors; new(_0: number): ClassWithTwoConstructors}; @@ -87,8 +96,8 @@ export interface MainModule { DerivedClass: {}; a_bool: boolean; an_int: number; - global_fn(_0: number, _1: number): number; optional_test(_0: Foo | undefined): number | undefined; + global_fn(_0: number, _1: number): number; smart_ptr_function(_0: ClassWithSmartPtrConstructor): number; smart_ptr_function_with_params(foo: ClassWithSmartPtrConstructor): number; function_with_callback_param(_0: (message: string) => void): number;