Skip to content

Commit f5a30a0

Browse files
authored
[embind] Add pointer policies for val's call, operator(), and new_. (#24832)
After #24175, pointer policies were required in more places, but there was no way to pass the policy into val's `call`, `operator()`, and `new_` methods and use pointers. All of these methods take variadic template arguments already which made passing separate variadic policy arguments challenging. C++17 is required to separate the regular arguments and policy arguments. For older versions of C++, we don't allow policy arguments and just allow pointers by default (too match old behavior). Fixes #24398
1 parent d4ef57b commit f5a30a0

10 files changed

+293
-186
lines changed

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ See docs/process.md for more on how version tagging works.
2727
- Node: v10.19.0 -> v12.22.9
2828
- Chrome: v70 -> v74
2929
- Firefox: v55 -> v65
30+
- The Embind `val` functions `call`, `operator()`, and `new_` now support
31+
passing `pointer`s by using the `allow_raw_pointers()` argument. This feature
32+
is only enabled with C++17 and newer. Older versions will allow pointers by
33+
default.
3034

3135
4.0.15 - 09/17/25
3236
-----------------

system/include/emscripten/bind.h

Lines changed: 0 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -279,181 +279,6 @@ struct InitFunc {
279279

280280
} // end namespace internal
281281

282-
////////////////////////////////////////////////////////////////////////////////
283-
// POLICIES
284-
////////////////////////////////////////////////////////////////////////////////
285-
286-
template<int Index>
287-
struct arg {
288-
static constexpr int index = Index + 1;
289-
};
290-
291-
struct ret_val {
292-
static constexpr int index = 0;
293-
};
294-
295-
/*
296-
template<typename Slot>
297-
struct allow_raw_pointer {
298-
template<typename InputType, int Index>
299-
struct Transform {
300-
typedef typename std::conditional<
301-
Index == Slot::index,
302-
internal::AllowedRawPointer<typename std::remove_pointer<InputType>::type>,
303-
InputType
304-
>::type type;
305-
};
306-
};
307-
*/
308-
309-
// allow all raw pointers
310-
struct allow_raw_pointers {
311-
template<typename InputType, int Index>
312-
struct Transform {
313-
// Use decay to handle references to pointers e.g.(T*&)->(T*).
314-
typedef typename std::decay<InputType>::type DecayedType;
315-
typedef typename std::conditional<
316-
std::is_pointer<DecayedType>::value,
317-
internal::AllowedRawPointer<typename std::remove_pointer<DecayedType>::type>,
318-
InputType
319-
>::type type;
320-
};
321-
};
322-
323-
// this is temporary until arg policies are reworked
324-
template<typename Slot>
325-
struct allow_raw_pointer : public allow_raw_pointers {
326-
};
327-
328-
struct async {
329-
template<typename InputType, int Index>
330-
struct Transform {
331-
typedef InputType type;
332-
};
333-
};
334-
335-
struct pure_virtual {
336-
template<typename InputType, int Index>
337-
struct Transform {
338-
typedef InputType type;
339-
};
340-
};
341-
342-
template<typename Slot>
343-
struct nonnull {
344-
static_assert(std::is_same<Slot, ret_val>::value, "Only nonnull return values are currently supported.");
345-
template<typename InputType, int Index>
346-
struct Transform {
347-
typedef InputType type;
348-
};
349-
};
350-
351-
namespace return_value_policy {
352-
353-
struct take_ownership : public allow_raw_pointers {};
354-
struct reference : public allow_raw_pointers {};
355-
356-
} // end namespace return_value_policy
357-
358-
namespace internal {
359-
360-
#if __cplusplus >= 201703L
361-
template <typename... Args> using conjunction = std::conjunction<Args...>;
362-
template <typename... Args> using disjunction = std::disjunction<Args...>;
363-
#else
364-
// Helper available in C++14.
365-
template <bool _Test, class _T1, class _T2>
366-
using conditional_t = typename std::conditional<_Test, _T1, _T2>::type;
367-
368-
template<class...> struct conjunction : std::true_type {};
369-
template<class B1> struct conjunction<B1> : B1 {};
370-
template<class B1, class... Bn>
371-
struct conjunction<B1, Bn...>
372-
: conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};
373-
374-
template<class...> struct disjunction : std::false_type {};
375-
template<class B1> struct disjunction<B1> : B1 {};
376-
template<class B1, class... Bn>
377-
struct disjunction<B1, Bn...>
378-
: conditional_t<bool(B1::value), disjunction<Bn...>, B1> {};
379-
#endif
380-
381-
template<typename... Policies>
382-
struct isPolicy;
383-
384-
template<typename... Rest>
385-
struct isPolicy<return_value_policy::take_ownership, Rest...> {
386-
static constexpr bool value = true;
387-
};
388-
389-
template<typename... Rest>
390-
struct isPolicy<return_value_policy::reference, Rest...> {
391-
static constexpr bool value = true;
392-
};
393-
394-
template<typename... Rest>
395-
struct isPolicy<emscripten::async, Rest...> {
396-
static constexpr bool value = true;
397-
};
398-
399-
template <typename T, typename... Rest>
400-
struct isPolicy<emscripten::allow_raw_pointer<T>, Rest...> {
401-
static constexpr bool value = true;
402-
};
403-
404-
template<typename... Rest>
405-
struct isPolicy<allow_raw_pointers, Rest...> {
406-
static constexpr bool value = true;
407-
};
408-
409-
template<typename... Rest>
410-
struct isPolicy<emscripten::pure_virtual, Rest...> {
411-
static constexpr bool value = true;
412-
};
413-
414-
template<typename T, typename... Rest>
415-
struct isPolicy<emscripten::nonnull<T>, Rest...> {
416-
static constexpr bool value = true;
417-
};
418-
419-
template<typename T, typename... Rest>
420-
struct isPolicy<T, Rest...> {
421-
static constexpr bool value = isPolicy<Rest...>::value;
422-
};
423-
424-
template<>
425-
struct isPolicy<> {
426-
static constexpr bool value = false;
427-
};
428-
429-
template<typename ReturnType, typename... Rest>
430-
struct GetReturnValuePolicy {
431-
using tag = rvp::default_tag;
432-
};
433-
434-
template<typename ReturnType, typename... Rest>
435-
struct GetReturnValuePolicy<ReturnType, return_value_policy::take_ownership, Rest...> {
436-
using tag = rvp::take_ownership;
437-
};
438-
439-
template<typename ReturnType, typename... Rest>
440-
struct GetReturnValuePolicy<ReturnType, return_value_policy::reference, Rest...> {
441-
using tag = rvp::reference;
442-
};
443-
444-
template<typename ReturnType, typename T, typename... Rest>
445-
struct GetReturnValuePolicy<ReturnType, T, Rest...> {
446-
using tag = GetReturnValuePolicy<ReturnType, Rest...>::tag;
447-
};
448-
449-
template<typename... Policies>
450-
using isAsync = disjunction<std::is_same<async, Policies>...>;
451-
452-
template<typename... Policies>
453-
using isNonnullReturn = disjunction<std::is_same<nonnull<ret_val>, Policies>...>;
454-
455-
}
456-
457282
////////////////////////////////////////////////////////////////////////////////
458283
// select_overload and select_const
459284
////////////////////////////////////////////////////////////////////////////////

system/include/emscripten/val.h

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ class EMBIND_VISIBILITY_DEFAULT val {
342342
explicit val(T&& value, Policies...) {
343343
using namespace internal;
344344

345-
new (this) val(internalCall<EM_INVOKER_KIND::CAST, WithPolicies<Policies...>, val>(nullptr, nullptr, std::forward<T>(value)));
345+
new (this) val(internalCallWithPolicy<EM_INVOKER_KIND::CAST, WithPolicies<Policies...>, val>(nullptr, nullptr, std::forward<T>(value)));
346346
}
347347

348348
val() : val(EM_VAL(internal::_EMVAL_UNDEFINED)) {}
@@ -489,28 +489,28 @@ class EMBIND_VISIBILITY_DEFAULT val {
489489
val new_(Args&&... args) const {
490490
using namespace internal;
491491

492-
return internalCall<EM_INVOKER_KIND::CONSTRUCTOR, WithPolicies<>, val>(as_handle(), nullptr, std::forward<Args>(args)...);
492+
return internalCall<EM_INVOKER_KIND::CONSTRUCTOR, val>(as_handle(), nullptr, std::forward<Args>(args)...);
493493
}
494494

495495
template<typename... Args>
496496
val operator()(Args&&... args) const {
497497
using namespace internal;
498498

499-
return internalCall<EM_INVOKER_KIND::FUNCTION, WithPolicies<>, val>(as_handle(), nullptr, std::forward<Args>(args)...);
499+
return internalCall<EM_INVOKER_KIND::FUNCTION, val>(as_handle(), nullptr, std::forward<Args>(args)...);
500500
}
501501

502502
template<typename ReturnValue, typename... Args>
503503
ReturnValue call(const char* name, Args&&... args) const {
504504
using namespace internal;
505505

506-
return internalCall<EM_INVOKER_KIND::METHOD, WithPolicies<>, ReturnValue>(as_handle(), name, std::forward<Args>(args)...);
506+
return internalCall<EM_INVOKER_KIND::METHOD, ReturnValue>(as_handle(), name, std::forward<Args>(args)...);
507507
}
508508

509509
template<typename T, typename ...Policies>
510510
T as(Policies...) const {
511511
using namespace internal;
512512

513-
return internalCall<EM_INVOKER_KIND::CAST, WithPolicies<Policies...>, T>(as_handle(), nullptr, *this);
513+
return internalCallWithPolicy<EM_INVOKER_KIND::CAST, WithPolicies<Policies...>, T>(as_handle(), nullptr, *this);
514514
}
515515

516516
// Prefer calling val::typeOf() over val::typeof(), since this form works in both C++11 and GNU++11 build modes. "typeof" is a reserved word in GNU++11 extensions.
@@ -570,8 +570,28 @@ class EMBIND_VISIBILITY_DEFAULT val {
570570
template<typename WrapperType>
571571
friend val internal::wrapped_extend(const std::string& , const val& );
572572

573-
template<internal::EM_INVOKER_KIND Kind, typename Policy, typename Ret, typename... Args>
573+
template<internal::EM_INVOKER_KIND Kind, typename Ret, typename... Args>
574574
static Ret internalCall(EM_VAL handle, const char *methodName, Args&&... args) {
575+
using namespace internal;
576+
#if __cplusplus >= 201703L
577+
using Policy = WithPolicies<FilterTypes<isPolicy, Args...>>;
578+
auto filteredArgs = Filter<isNotPolicy>(args...);
579+
return std::apply(
580+
[&](auto&&... actualArgs) -> decltype(auto) {
581+
return internalCallWithPolicy<Kind, Policy, Ret>(handle, methodName, std::forward<decltype(actualArgs)>(actualArgs)...);
582+
},
583+
filteredArgs
584+
);
585+
#else
586+
// When std::apply is not available allow pointers by default. std::apply
587+
// could be polyfilled, but it requires a lot of code.
588+
static_assert(internal::conjunction<internal::isNotPolicy<Args>...>::value, "Using pointer policies with val requires C++17 or newer.");
589+
return internalCallWithPolicy<Kind, WithPolicies<allow_raw_pointers>, Ret>(handle, methodName, std::forward<decltype(args)>(args)...);
590+
#endif
591+
}
592+
593+
template<internal::EM_INVOKER_KIND Kind, typename Policy, typename Ret, typename... Args>
594+
static Ret internalCallWithPolicy(EM_VAL handle, const char *methodName, Args&&... args) {
575595
static_assert(!std::is_lvalue_reference<Ret>::value,
576596
"Cannot create a lvalue reference out of a JS value.");
577597

0 commit comments

Comments
 (0)