Skip to content

Commit ecb7bb3

Browse files
hkleynhanskripken
authored andcommitted
[embind] class_<T>.constructor to accept std::function and function object (#9051)
Enable `class_<T>.constructor` to accept both `std::function` and function object types. This is useful in scenarios where you want a stateful function object for purposes such as gathering statistics or performing other generic functionality on each call.
1 parent ea85994 commit ecb7bb3

File tree

4 files changed

+162
-49
lines changed

4 files changed

+162
-49
lines changed

site/source/docs/api_reference/bind.h.rst

+20-5
Original file line numberDiff line numberDiff line change
@@ -533,17 +533,32 @@ Classes
533533
.. _embind-class-function-pointer-constructor:
534534

535535

536-
.. cpp:function:: const class_& constructor(ReturnType (*factory)(Args...), Policies...) const
536+
.. cpp:function:: const class_& constructor(Callable callable, Policies...) const
537537

538538
.. code-block:: cpp
539539
540540
//prototype
541-
template<typename... Args, typename ReturnType, typename... Policies>
542-
EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(ReturnType (*factory)(Args...), Policies...) const
541+
template<typename Signature = internal::DeduceArgumentsTag, typename Callable, typename... Policies>
542+
EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(Callable callable, Policies...) const
543+
544+
Class constructor for objects that use a factory function to create the object. This method will accept either a function pointer, ``std::function``
545+
object or function object which will return a newly constructed object. When the ``Callable`` is a function object the function signature must be
546+
explicitly specified in the ``Signature`` template parameter in the format ``ReturnType (Args...)``. For ``Callable`` types other than function objects
547+
the method signature will be deduced.
543548

544-
Class constructor for objects that use a factory function to create the object. See :ref:`embind-external-constructors` for more information.
549+
The following are all valid calls to ``constructor``:
545550

546-
:param ReturnType (\*factory)(Args...): The address of the class factory function.
551+
.. code-block:: cpp
552+
553+
using namespace std::placeholders;
554+
myClass1.constructor(&my_factory);
555+
myClass2.constructor(std::function<ClassType2(float, float)>(&class2_factory));
556+
myClass3.constructor<ClassType3(const val&)>(std::bind(Class3Functor(), _1));
557+
558+
See :ref:`embind-external-constructors` for more information.
559+
560+
561+
:param Callable callable Note that ``Callable`` may be either a member function pointer, function pointer, ``std::function`` or function object.
547562
:param Policies... policies: |policies-argument|
548563
:returns: |class_-function-returns|
549564

system/include/emscripten/bind.h

+91-44
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,31 @@ namespace emscripten {
352352
);
353353
}
354354
};
355+
356+
template<typename FunctorType, typename ReturnType, typename... Args>
357+
struct FunctorInvoker {
358+
static typename internal::BindingType<ReturnType>::WireType invoke(
359+
FunctorType& function,
360+
typename internal::BindingType<Args>::WireType... args
361+
) {
362+
return internal::BindingType<ReturnType>::toWireType(
363+
function(
364+
internal::BindingType<Args>::fromWireType(args)...)
365+
);
366+
}
367+
};
368+
369+
template<typename FunctorType, typename... Args>
370+
struct FunctorInvoker<FunctorType, void, Args...> {
371+
static void invoke(
372+
FunctorType& function,
373+
typename internal::BindingType<Args>::WireType... args
374+
) {
375+
function(
376+
internal::BindingType<Args>::fromWireType(args)...);
377+
}
378+
};
379+
355380
}
356381

357382
////////////////////////////////////////////////////////////////////////////////
@@ -487,37 +512,6 @@ namespace emscripten {
487512
}
488513
};
489514

490-
template<typename FunctorType, typename ReturnType, typename ThisType, typename... Args>
491-
struct FunctorInvoker {
492-
493-
static typename internal::BindingType<ReturnType>::WireType invoke(
494-
FunctorType& function,
495-
typename internal::BindingType<ThisType>::WireType wireThis,
496-
typename internal::BindingType<Args>::WireType... args
497-
) {
498-
return internal::BindingType<ReturnType>::toWireType(
499-
function(
500-
internal::BindingType<ThisType>::fromWireType(wireThis),
501-
internal::BindingType<Args>::fromWireType(args)...)
502-
);
503-
}
504-
};
505-
506-
template<typename FunctorType, typename ThisType, typename... Args>
507-
struct FunctorInvoker<FunctorType, void, ThisType, Args...> {
508-
using FunctionType = std::function<void (ThisType, Args...)>;
509-
510-
static void invoke(
511-
FunctorType& function,
512-
typename internal::BindingType<ThisType>::WireType wireThis,
513-
typename internal::BindingType<Args>::WireType... args
514-
) {
515-
function(
516-
internal::BindingType<ThisType>::fromWireType(wireThis),
517-
internal::BindingType<Args>::fromWireType(args)...);
518-
}
519-
};
520-
521515
template<typename MemberPointer,
522516
typename ReturnType,
523517
typename ThisType,
@@ -1142,6 +1136,64 @@ namespace emscripten {
11421136

11431137
struct DeduceArgumentsTag {};
11441138

1139+
////////////////////////////////////////////////////////////////////////////
1140+
// RegisterClassConstructor
1141+
////////////////////////////////////////////////////////////////////////////
1142+
1143+
template <typename T>
1144+
struct RegisterClassConstructor;
1145+
1146+
template<typename ReturnType, typename... Args>
1147+
struct RegisterClassConstructor<ReturnType (*)(Args...)> {
1148+
1149+
template <typename ClassType, typename... Policies>
1150+
static void invoke(ReturnType (*factory)(Args...)) {
1151+
typename WithPolicies<allow_raw_pointers, Policies...>::template ArgTypeList<ReturnType, Args...> args;
1152+
auto invoke = &Invoker<ReturnType, Args...>::invoke;
1153+
_embind_register_class_constructor(
1154+
TypeID<ClassType>::get(),
1155+
args.getCount(),
1156+
args.getTypes(),
1157+
getSignature(invoke),
1158+
reinterpret_cast<GenericFunction>(invoke),
1159+
reinterpret_cast<GenericFunction>(factory));
1160+
}
1161+
};
1162+
1163+
template<typename ReturnType, typename... Args>
1164+
struct RegisterClassConstructor<std::function<ReturnType (Args...)>> {
1165+
1166+
template <typename ClassType, typename... Policies>
1167+
static void invoke(std::function<ReturnType (Args...)> factory) {
1168+
typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args;
1169+
auto invoke = &FunctorInvoker<decltype(factory), ReturnType, Args...>::invoke;
1170+
_embind_register_class_constructor(
1171+
TypeID<ClassType>::get(),
1172+
args.getCount(),
1173+
args.getTypes(),
1174+
getSignature(invoke),
1175+
reinterpret_cast<GenericFunction>(invoke),
1176+
reinterpret_cast<GenericFunction>(getContext(factory)));
1177+
}
1178+
};
1179+
1180+
template<typename ReturnType, typename... Args>
1181+
struct RegisterClassConstructor<ReturnType (Args...)> {
1182+
1183+
template <typename ClassType, typename Callable, typename... Policies>
1184+
static void invoke(Callable& factory) {
1185+
typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args;
1186+
auto invoke = &FunctorInvoker<decltype(factory), ReturnType, Args...>::invoke;
1187+
_embind_register_class_constructor(
1188+
TypeID<ClassType>::get(),
1189+
args.getCount(),
1190+
args.getTypes(),
1191+
getSignature(invoke),
1192+
reinterpret_cast<GenericFunction>(invoke),
1193+
reinterpret_cast<GenericFunction>(getContext(factory)));
1194+
}
1195+
};
1196+
11451197
////////////////////////////////////////////////////////////////////////////
11461198
// RegisterClassMethod
11471199
////////////////////////////////////////////////////////////////////////////
@@ -1328,20 +1380,15 @@ namespace emscripten {
13281380
policies...);
13291381
}
13301382

1331-
template<typename... Args, typename ReturnType, typename... Policies>
1332-
EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(ReturnType (*factory)(Args...), Policies...) const {
1333-
using namespace internal;
1383+
template<typename Signature = internal::DeduceArgumentsTag, typename Callable, typename... Policies>
1384+
EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(Callable callable, Policies...) const {
13341385

1335-
// TODO: allows all raw pointers... policies need a rethink
1336-
typename WithPolicies<allow_raw_pointers, Policies...>::template ArgTypeList<ReturnType, Args...> args;
1337-
auto invoke = &Invoker<ReturnType, Args...>::invoke;
1338-
_embind_register_class_constructor(
1339-
TypeID<ClassType>::get(),
1340-
args.getCount(),
1341-
args.getTypes(),
1342-
getSignature(invoke),
1343-
reinterpret_cast<GenericFunction>(invoke),
1344-
reinterpret_cast<GenericFunction>(factory));
1386+
using invoker = internal::RegisterClassConstructor<
1387+
typename std::conditional<std::is_same<Signature, internal::DeduceArgumentsTag>::value,
1388+
Callable,
1389+
Signature>::type>;
1390+
1391+
invoker::template invoke<ClassType, Policies...>(callable);
13451392
return *this;
13461393
}
13471394

tests/embind/embind.test.js

+13
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,19 @@ module({
11921192
b.delete();
11931193
});
11941194

1195+
test("function objects as class constructors", function() {
1196+
let a = new cm.ConstructFromStdFunction("foo", 10);
1197+
assert.equal("foo", a.getVal());
1198+
assert.equal(10, a.getA());
1199+
1200+
let b = new cm.ConstructFromFunctionObject("bar", 12);
1201+
assert.equal("bar", b.getVal());
1202+
assert.equal(12, b.getA());
1203+
1204+
a.delete();
1205+
b.delete();
1206+
});
1207+
11951208
test("function objects as class methods", function() {
11961209
let b = cm.ValHolder.makeValHolder("foo");
11971210

tests/embind/embind_test.cpp

+38
Original file line numberDiff line numberDiff line change
@@ -1690,6 +1690,32 @@ unsigned long load_unsigned_long() {
16901690
return ulong;
16911691
}
16921692

1693+
template <int I>
1694+
class ConstructFromFunctor {
1695+
public:
1696+
ConstructFromFunctor(const val& v, int a)
1697+
: v_(v), a_(a)
1698+
{}
1699+
1700+
val getVal() const {
1701+
return v_;
1702+
}
1703+
1704+
int getA() const {
1705+
return a_;
1706+
}
1707+
1708+
private:
1709+
val v_;
1710+
int a_;
1711+
};
1712+
1713+
template <int I>
1714+
ConstructFromFunctor<I> construct_from_functor_mixin(const val& v, int i)
1715+
{
1716+
return {v, i};
1717+
}
1718+
16931719
EMSCRIPTEN_BINDINGS(tests) {
16941720
register_vector<int>("IntegerVector");
16951721
register_vector<char>("CharVector");
@@ -1794,6 +1820,18 @@ EMSCRIPTEN_BINDINGS(tests) {
17941820

17951821
using namespace std::placeholders;
17961822

1823+
class_<ConstructFromFunctor<1>>("ConstructFromStdFunction")
1824+
.constructor(std::function<ConstructFromFunctor<1>(const val&, int)>(&construct_from_functor_mixin<1>))
1825+
.function("getVal", &ConstructFromFunctor<1>::getVal)
1826+
.function("getA", &ConstructFromFunctor<1>::getA)
1827+
;
1828+
1829+
class_<ConstructFromFunctor<2>>("ConstructFromFunctionObject")
1830+
.constructor<ConstructFromFunctor<2>(const val&, int)>(std::bind(&construct_from_functor_mixin<2>, _1, _2))
1831+
.function("getVal", &ConstructFromFunctor<2>::getVal)
1832+
.function("getA", &ConstructFromFunctor<2>::getA)
1833+
;
1834+
17971835
class_<ValHolder>("ValHolder")
17981836
.smart_ptr<std::shared_ptr<ValHolder>>("std::shared_ptr<ValHolder>")
17991837
.constructor<val>()

0 commit comments

Comments
 (0)