Skip to content

class_<T>.constructor to accept std::function and function object #9051

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions site/source/docs/api_reference/bind.h.rst
Original file line number Diff line number Diff line change
Expand Up @@ -533,17 +533,32 @@ Classes
.. _embind-class-function-pointer-constructor:


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

.. code-block:: cpp

//prototype
template<typename... Args, typename ReturnType, typename... Policies>
EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(ReturnType (*factory)(Args...), Policies...) const
template<typename Signature = internal::DeduceArgumentsTag, typename Callable, typename... Policies>
EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(Callable callable, Policies...) const

Class constructor for objects that use a factory function to create the object. This method will accept either a function pointer, ``std::function``
object or function object which will return a newly constructed object. When the ``Callable`` is a function object the function signature must be
explicitly specified in the ``Signature`` template parameter in the format ``ReturnType (Args...)``. For ``Callable`` types other than function objects
the method signature will be deduced.

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

:param ReturnType (\*factory)(Args...): The address of the class factory function.
.. code-block:: cpp

using namespace std::placeholders;
myClass1.constructor(&my_factory);
myClass2.constructor(std::function<ClassType2(float, float)>(&class2_factory));
myClass3.constructor<ClassType3(const val&)>(std::bind(Class3Functor(), _1));

See :ref:`embind-external-constructors` for more information.


:param Callable callable Note that ``Callable`` may be either a member function pointer, function pointer, ``std::function`` or function object.
:param Policies... policies: |policies-argument|
:returns: |class_-function-returns|

Expand Down
135 changes: 91 additions & 44 deletions system/include/emscripten/bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,31 @@ namespace emscripten {
);
}
};

template<typename FunctorType, typename ReturnType, typename... Args>
struct FunctorInvoker {
static typename internal::BindingType<ReturnType>::WireType invoke(
FunctorType& function,
typename internal::BindingType<Args>::WireType... args
) {
return internal::BindingType<ReturnType>::toWireType(
function(
internal::BindingType<Args>::fromWireType(args)...)
);
}
};

template<typename FunctorType, typename... Args>
struct FunctorInvoker<FunctorType, void, Args...> {
static void invoke(
FunctorType& function,
typename internal::BindingType<Args>::WireType... args
) {
function(
internal::BindingType<Args>::fromWireType(args)...);
}
};

}

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -487,37 +512,6 @@ namespace emscripten {
}
};

template<typename FunctorType, typename ReturnType, typename ThisType, typename... Args>
struct FunctorInvoker {

static typename internal::BindingType<ReturnType>::WireType invoke(
FunctorType& function,
typename internal::BindingType<ThisType>::WireType wireThis,
typename internal::BindingType<Args>::WireType... args
) {
return internal::BindingType<ReturnType>::toWireType(
function(
internal::BindingType<ThisType>::fromWireType(wireThis),
internal::BindingType<Args>::fromWireType(args)...)
);
}
};

template<typename FunctorType, typename ThisType, typename... Args>
struct FunctorInvoker<FunctorType, void, ThisType, Args...> {
using FunctionType = std::function<void (ThisType, Args...)>;

static void invoke(
FunctorType& function,
typename internal::BindingType<ThisType>::WireType wireThis,
typename internal::BindingType<Args>::WireType... args
) {
function(
internal::BindingType<ThisType>::fromWireType(wireThis),
internal::BindingType<Args>::fromWireType(args)...);
}
};

template<typename MemberPointer,
typename ReturnType,
typename ThisType,
Expand Down Expand Up @@ -1142,6 +1136,64 @@ namespace emscripten {

struct DeduceArgumentsTag {};

////////////////////////////////////////////////////////////////////////////
// RegisterClassConstructor
////////////////////////////////////////////////////////////////////////////

template <typename T>
struct RegisterClassConstructor;

template<typename ReturnType, typename... Args>
struct RegisterClassConstructor<ReturnType (*)(Args...)> {

template <typename ClassType, typename... Policies>
static void invoke(ReturnType (*factory)(Args...)) {
typename WithPolicies<allow_raw_pointers, Policies...>::template ArgTypeList<ReturnType, Args...> args;
auto invoke = &Invoker<ReturnType, Args...>::invoke;
_embind_register_class_constructor(
TypeID<ClassType>::get(),
args.getCount(),
args.getTypes(),
getSignature(invoke),
reinterpret_cast<GenericFunction>(invoke),
reinterpret_cast<GenericFunction>(factory));
}
};

template<typename ReturnType, typename... Args>
struct RegisterClassConstructor<std::function<ReturnType (Args...)>> {

template <typename ClassType, typename... Policies>
static void invoke(std::function<ReturnType (Args...)> factory) {
typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args;
auto invoke = &FunctorInvoker<decltype(factory), ReturnType, Args...>::invoke;
_embind_register_class_constructor(
TypeID<ClassType>::get(),
args.getCount(),
args.getTypes(),
getSignature(invoke),
reinterpret_cast<GenericFunction>(invoke),
reinterpret_cast<GenericFunction>(getContext(factory)));
}
};

template<typename ReturnType, typename... Args>
struct RegisterClassConstructor<ReturnType (Args...)> {

template <typename ClassType, typename Callable, typename... Policies>
static void invoke(Callable& factory) {
typename WithPolicies<Policies...>::template ArgTypeList<ReturnType, Args...> args;
auto invoke = &FunctorInvoker<decltype(factory), ReturnType, Args...>::invoke;
_embind_register_class_constructor(
TypeID<ClassType>::get(),
args.getCount(),
args.getTypes(),
getSignature(invoke),
reinterpret_cast<GenericFunction>(invoke),
reinterpret_cast<GenericFunction>(getContext(factory)));
}
};

////////////////////////////////////////////////////////////////////////////
// RegisterClassMethod
////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1328,20 +1380,15 @@ namespace emscripten {
policies...);
}

template<typename... Args, typename ReturnType, typename... Policies>
EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(ReturnType (*factory)(Args...), Policies...) const {
using namespace internal;
template<typename Signature = internal::DeduceArgumentsTag, typename Callable, typename... Policies>
EMSCRIPTEN_ALWAYS_INLINE const class_& constructor(Callable callable, Policies...) const {

// TODO: allows all raw pointers... policies need a rethink
typename WithPolicies<allow_raw_pointers, Policies...>::template ArgTypeList<ReturnType, Args...> args;
auto invoke = &Invoker<ReturnType, Args...>::invoke;
_embind_register_class_constructor(
TypeID<ClassType>::get(),
args.getCount(),
args.getTypes(),
getSignature(invoke),
reinterpret_cast<GenericFunction>(invoke),
reinterpret_cast<GenericFunction>(factory));
using invoker = internal::RegisterClassConstructor<
typename std::conditional<std::is_same<Signature, internal::DeduceArgumentsTag>::value,
Callable,
Signature>::type>;

invoker::template invoke<ClassType, Policies...>(callable);
return *this;
}

Expand Down
13 changes: 13 additions & 0 deletions tests/embind/embind.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,19 @@ module({
b.delete();
});

test("function objects as class constructors", function() {
let a = new cm.ConstructFromStdFunction("foo", 10);
assert.equal("foo", a.getVal());
assert.equal(10, a.getA());

let b = new cm.ConstructFromFunctionObject("bar", 12);
assert.equal("bar", b.getVal());
assert.equal(12, b.getA());

a.delete();
b.delete();
});

test("function objects as class methods", function() {
let b = cm.ValHolder.makeValHolder("foo");

Expand Down
38 changes: 38 additions & 0 deletions tests/embind/embind_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1690,6 +1690,32 @@ unsigned long load_unsigned_long() {
return ulong;
}

template <int I>
class ConstructFromFunctor {
public:
ConstructFromFunctor(const val& v, int a)
: v_(v), a_(a)
{}

val getVal() const {
return v_;
}

int getA() const {
return a_;
}

private:
val v_;
int a_;
};

template <int I>
ConstructFromFunctor<I> construct_from_functor_mixin(const val& v, int i)
{
return {v, i};
}

EMSCRIPTEN_BINDINGS(tests) {
register_vector<int>("IntegerVector");
register_vector<char>("CharVector");
Expand Down Expand Up @@ -1794,6 +1820,18 @@ EMSCRIPTEN_BINDINGS(tests) {

using namespace std::placeholders;

class_<ConstructFromFunctor<1>>("ConstructFromStdFunction")
.constructor(std::function<ConstructFromFunctor<1>(const val&, int)>(&construct_from_functor_mixin<1>))
.function("getVal", &ConstructFromFunctor<1>::getVal)
.function("getA", &ConstructFromFunctor<1>::getA)
;

class_<ConstructFromFunctor<2>>("ConstructFromFunctionObject")
.constructor<ConstructFromFunctor<2>(const val&, int)>(std::bind(&construct_from_functor_mixin<2>, _1, _2))
.function("getVal", &ConstructFromFunctor<2>::getVal)
.function("getA", &ConstructFromFunctor<2>::getA)
;

class_<ValHolder>("ValHolder")
.smart_ptr<std::shared_ptr<ValHolder>>("std::shared_ptr<ValHolder>")
.constructor<val>()
Expand Down