Skip to content

Commit 1e7838b

Browse files
authored
Add iterator support to emval (#20364)
1 parent b682c33 commit 1e7838b

File tree

5 files changed

+51
-0
lines changed

5 files changed

+51
-0
lines changed

ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ See docs/process.md for more on how version tagging works.
4040
- `emscripten::val` now prevents accidental access to the underlying JavaScript
4141
value from threads other than its owner. This already didn't work correctly
4242
in majority of cases, but now it will throw a clear assertion failure. (#20344)
43+
- `emscripten::val` can now be iterated over with a C++ range-based for loop.
44+
(#20364)
4345

4446
3.1.46 - 09/15/23
4547
-----------------

src/embind/emval.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,19 @@ var LibraryEmVal = {
521521
});
522522
},
523523
#endif
524+
525+
_emval_iter_begin__deps: ['$Emval'],
526+
_emval_iter_begin: (iterable) => {
527+
iterable = Emval.toValue(iterable);
528+
return Emval.toHandle(iterable[Symbol.iterator]());
529+
},
530+
531+
_emval_iter_next__deps: ['$Emval'],
532+
_emval_iter_next: (iterator) => {
533+
iterator = Emval.toValue(iterator);
534+
var result = iterator.next();
535+
return result.done ? 0 : Emval.toHandle(result.value);
536+
},
524537
};
525538

526539
addToLibrary(LibraryEmVal);

src/library_sigs.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ sigs = {
352352
_emval_instanceof__sig: 'ipp',
353353
_emval_is_number__sig: 'ip',
354354
_emval_is_string__sig: 'ip',
355+
_emval_iter_begin__sig: 'pp',
356+
_emval_iter_next__sig: 'pp',
355357
_emval_less_than__sig: 'ipp',
356358
_emval_new__sig: 'ppipp',
357359
_emval_new_array__sig: 'p',

system/include/emscripten/val.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ bool _emval_in(EM_VAL item, EM_VAL object);
107107
bool _emval_delete(EM_VAL object, EM_VAL property);
108108
[[noreturn]] bool _emval_throw(EM_VAL object);
109109
EM_VAL _emval_await(EM_VAL promise);
110+
EM_VAL _emval_iter_begin(EM_VAL iterable);
111+
EM_VAL _emval_iter_next(EM_VAL iterator);
110112

111113
} // extern "C"
112114

@@ -586,6 +588,12 @@ class val {
586588
return val(internal::_emval_await(as_handle()));
587589
}
588590

591+
struct iterator;
592+
593+
iterator begin() const;
594+
// our iterators are sentinel-based range iterators; use nullptr as the end sentinel
595+
constexpr nullptr_t end() const { return nullptr; }
596+
589597
private:
590598
// takes ownership, assumes handle already incref'd and lives on the same thread
591599
explicit val(EM_VAL handle)
@@ -619,6 +627,27 @@ class val {
619627
friend struct internal::BindingType<val>;
620628
};
621629

630+
struct val::iterator {
631+
iterator() = delete;
632+
// Make sure iterator is only moveable, not copyable as it represents a mutable state.
633+
iterator(iterator&&) = default;
634+
iterator(const val& v) : iter(internal::_emval_iter_begin(v.as_handle())) {
635+
this->operator++();
636+
}
637+
val&& operator*() { return std::move(cur_value); }
638+
const val& operator*() const { return cur_value; }
639+
void operator++() { cur_value = val(internal::_emval_iter_next(iter.as_handle())); }
640+
bool operator!=(nullptr_t) const { return cur_value.as_handle() != nullptr; }
641+
642+
private:
643+
val iter;
644+
val cur_value;
645+
};
646+
647+
inline val::iterator val::begin() const {
648+
return iterator(*this);
649+
}
650+
622651
// Declare a custom type that can be used in conjuction with
623652
// emscripten::register_type to emit custom TypeScript defintions for val types.
624653
#define EMSCRIPTEN_DECLARE_VAL_TYPE(name) \

test/embind/test_val.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ int main() {
7373
ensure_js("a[1] == 1");
7474
ensure_js("a[2] == 3");
7575
ensure_js_not("a[2] == 2");
76+
vector<int> vec2_from_iter;
77+
for (val&& v : val::global("a")) {
78+
vec2_from_iter.push_back(v.as<int>());
79+
}
80+
ensure(vec2 == vec2_from_iter);
7681

7782
test("template<typename Iter> val array(Iter begin, Iter end)");
7883
val::global().set("a", val::array(vec1.begin(), vec1.end()));

0 commit comments

Comments
 (0)