Skip to content

Commit c9b106a

Browse files
committed
embind: Use optional return type for vector and maps.
This helps create better TypeScript definitions for what is actually returned.
1 parent c4d76b8 commit c9b106a

File tree

3 files changed

+65
-9
lines changed

3 files changed

+65
-9
lines changed

system/include/emscripten/bind.h

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,21 @@ class class_ {
18751875
}
18761876
};
18771877

1878+
#if __cplusplus >= 201703L
1879+
template<typename T>
1880+
void register_optional() {
1881+
// Optional types are automatically registered for some internal types so
1882+
// only run the register method once so we don't conflict with a user's
1883+
// bindings if they also register the optional type.
1884+
thread_local bool hasRun;
1885+
if (hasRun) {
1886+
return;
1887+
}
1888+
hasRun = true;
1889+
internal::_embind_register_optional(internal::TypeID<std::optional<T>>::get(), internal::TypeID<T>::get());
1890+
}
1891+
#endif
1892+
18781893
////////////////////////////////////////////////////////////////////////////////
18791894
// VECTORS
18801895
////////////////////////////////////////////////////////////////////////////////
@@ -1883,6 +1898,20 @@ namespace internal {
18831898

18841899
template<typename VectorType>
18851900
struct VectorAccess {
1901+
// This nearly duplicated code is used for generating more specific TypeScript
1902+
// types when using more modern C++ versions.
1903+
#if __cplusplus >= 201703L
1904+
static std::optional<typename VectorType::value_type> get(
1905+
const VectorType& v,
1906+
typename VectorType::size_type index
1907+
) {
1908+
if (index < v.size()) {
1909+
return v[index];
1910+
} else {
1911+
return {};
1912+
}
1913+
}
1914+
#else
18861915
static val get(
18871916
const VectorType& v,
18881917
typename VectorType::size_type index
@@ -1893,6 +1922,7 @@ struct VectorAccess {
18931922
return val::undefined();
18941923
}
18951924
}
1925+
#endif
18961926

18971927
static bool set(
18981928
VectorType& v,
@@ -1909,6 +1939,9 @@ struct VectorAccess {
19091939
template<typename T>
19101940
class_<std::vector<T>> register_vector(const char* name) {
19111941
typedef std::vector<T> VecType;
1942+
#if __cplusplus >= 201703L
1943+
register_optional<T>();
1944+
#endif
19121945

19131946
void (VecType::*push_back)(const T&) = &VecType::push_back;
19141947
void (VecType::*resize)(const size_t, const T&) = &VecType::resize;
@@ -1923,13 +1956,6 @@ class_<std::vector<T>> register_vector(const char* name) {
19231956
;
19241957
}
19251958

1926-
#if __cplusplus >= 201703L
1927-
template<typename T>
1928-
void register_optional() {
1929-
internal::_embind_register_optional(internal::TypeID<std::optional<T>>::get(), internal::TypeID<T>::get());
1930-
}
1931-
#endif
1932-
19331959
////////////////////////////////////////////////////////////////////////////////
19341960
// MAPS
19351961
////////////////////////////////////////////////////////////////////////////////
@@ -1938,6 +1964,21 @@ namespace internal {
19381964

19391965
template<typename MapType>
19401966
struct MapAccess {
1967+
// This nearly duplicated code is used for generating more specific TypeScript
1968+
// types when using more modern C++ versions.
1969+
#if __cplusplus >= 201703L
1970+
static std::optional<typename MapType::mapped_type> get(
1971+
const MapType& m,
1972+
const typename MapType::key_type& k
1973+
) {
1974+
auto i = m.find(k);
1975+
if (i == m.end()) {
1976+
return {};
1977+
} else {
1978+
return i->second;
1979+
}
1980+
}
1981+
#else
19411982
static val get(
19421983
const MapType& m,
19431984
const typename MapType::key_type& k
@@ -1949,6 +1990,7 @@ struct MapAccess {
19491990
return val(i->second);
19501991
}
19511992
}
1993+
#endif
19521994

19531995
static void set(
19541996
MapType& m,
@@ -1975,6 +2017,9 @@ struct MapAccess {
19752017
template<typename K, typename V>
19762018
class_<std::map<K, V>> register_map(const char* name) {
19772019
typedef std::map<K,V> MapType;
2020+
#if __cplusplus >= 201703L
2021+
register_optional<V>();
2022+
#endif
19782023

19792024
size_t (MapType::*size)() const = &MapType::size;
19802025
return class_<MapType>(name)

test/other/embind_tsgen.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ EMSCRIPTEN_BINDINGS(Test) {
165165

166166
register_vector<int>("IntVec");
167167

168+
register_map<int, int>("MapIntInt");
169+
168170
class_<Foo>("Foo").function("process", &Foo::process);
169171

170172
function("global_fn", &global_fn);

test/other/embind_tsgen.d.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,16 @@ export interface IntVec {
2828
push_back(_0: number): void;
2929
resize(_0: number, _1: number): void;
3030
size(): number;
31+
get(_0: number): number | undefined;
3132
set(_0: number, _1: number): boolean;
32-
get(_0: number): any;
33+
delete(): void;
34+
}
35+
36+
export interface MapIntInt {
37+
keys(): IntVec;
38+
get(_0: number): number | undefined;
39+
set(_0: number, _1: number): void;
40+
size(): number;
3341
delete(): void;
3442
}
3543

@@ -79,6 +87,7 @@ export interface MainModule {
7987
EmptyEnum: {};
8088
enum_returning_fn(): Bar;
8189
IntVec: {new(): IntVec};
90+
MapIntInt: {new(): MapIntInt};
8291
Foo: {};
8392
ClassWithConstructor: {new(_0: number, _1: ValArr): ClassWithConstructor};
8493
ClassWithTwoConstructors: {new(): ClassWithTwoConstructors; new(_0: number): ClassWithTwoConstructors};
@@ -87,8 +96,8 @@ export interface MainModule {
8796
DerivedClass: {};
8897
a_bool: boolean;
8998
an_int: number;
90-
global_fn(_0: number, _1: number): number;
9199
optional_test(_0: Foo | undefined): number | undefined;
100+
global_fn(_0: number, _1: number): number;
92101
smart_ptr_function(_0: ClassWithSmartPtrConstructor): number;
93102
smart_ptr_function_with_params(foo: ClassWithSmartPtrConstructor): number;
94103
function_with_callback_param(_0: (message: string) => void): number;

0 commit comments

Comments
 (0)