/
Callback.h
165 lines (112 loc) · 4.26 KB
/
Callback.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// This file is part of nbind, copyright (C) 2014-2015 BusFaster Ltd.
// Released under the MIT license, see LICENSE.
// This file handles JavaScript callback functions accessible from C++.
#pragma once
namespace nbind {
// Exception signaling a JavaScript callback has thrown, and an exception is
// now bubbling up the JavaScript stack.
class cbException : public std::exception {};
// cbFunction is a functor that can be called with any number of arguments of any type
// compatible with JavaScript. Types are autodetected from a parameter pack.
// Normally the function returns nothing when called, but it has a templated
// call<ReturnType>() method that accepts the expected return type as a template
// parameter, and handles conversion automatically.
template <typename DefaultReturnType>
class cbWrapper {
template<typename PolicyList, size_t Index, typename ArgType>
friend struct ArgFromWire;
public:
explicit cbWrapper(const v8::Local<v8::Function> &func) : func(func) {}
cbWrapper(const cbWrapper &func) : func(func.getJsFunction()) {}
~cbWrapper() {
func.Reset();
}
void reset() {
func.Reset();
}
template<typename... Args>
DefaultReturnType operator()(Args&&... args) {
return(call<DefaultReturnType>(std::move(args)...));
}
template <typename ReturnType, typename... Args>
typename TypeTransformer<ReturnType>::Type call(Args&&... args) const {
v8::Local<v8::Value> argv[] = {
(convertToWire(std::move(args)))...,
// Avoid error C2466: cannot allocate an array of constant size 0.
Nan::Null()
};
Nan::MaybeLocal<v8::Value> result = Nan::Call(func, Nan::GetCurrentContext()->Global(), sizeof...(Args), argv);
if(result.IsEmpty()) throw(cbException());
return(convertFromWire<ReturnType>(result.ToLocalChecked()));
}
template <typename ReturnType, typename... Args>
typename TypeTransformer<ReturnType>::Type callMethod(
v8::Local<v8::Object> target,
Args&&... args
) const {
v8::Local<v8::Value> argv[] = {
(convertToWire(std::move(args)))...,
// Avoid error C2466: cannot allocate an array of constant size 0.
Nan::Null()
};
Nan::MaybeLocal<v8::Value> result = Nan::Call(func, target, sizeof...(Args), argv);
if(result.IsEmpty()) throw(cbException());
return(convertFromWire<ReturnType>(result.ToLocalChecked()));
}
v8::Local<v8::Function> getJsFunction() const { return(func.GetFunction()); }
private:
Nan::Callback func;
};
template <> template<typename... Args>
void cbWrapper<void> :: operator()(Args&&... args) {
call<void>(std::move(args)...);
}
// Note: passing cbFunction by value on asm.js doesn't work.
template <> struct BindingType<cbFunction> {
typedef const cbFunction Type;
static inline bool checkType(WireType arg) {
return(arg->IsFunction());
}
static inline Type fromWireType(WireType arg) {
return(cbFunction(arg.As<v8::Function>()));
}
};
template <> struct BindingType<const cbFunction &> {
typedef const cbFunction &Type;
static inline bool checkType(WireType arg) {
return(arg->IsFunction());
}
};
template <> struct BindingType<cbFunction &> : public BindingType<const cbFunction &> {};
// Handle callback functions. They are converted to a functor of type cbFunction,
// which can be called directly from C++ with arguments of any type.
template<typename PolicyList, size_t Index>
struct ArgFromWire<PolicyList, Index, const cbFunction &> {
template <typename NanArgs>
ArgFromWire(const NanArgs &args) : val(args[Index].template As<v8::Function>()) {}
template <typename NanArgs>
inline const cbFunction &get(const NanArgs &args) {
return(val);
}
cbFunction val;
};
template<typename PolicyList, size_t Index>
struct ArgFromWire<PolicyList, Index, cbFunction &> {
template <typename NanArgs>
ArgFromWire(const NanArgs &args) : val(args[Index].template As<v8::Function>()) {}
template <typename NanArgs>
inline cbFunction &get(const NanArgs &args) {
return(val);
}
cbFunction val;
};
template <typename ReturnType, typename... Args>
void cbOutput :: call(Args... args) {
v8::Local<v8::Value> argv[] = {
(BindingType<Args>::toWireType(std::forward<Args>(args)))...
};
auto result = Nan::NewInstance(jsConstructor.getJsFunction(), sizeof...(Args), argv);
if(result.IsEmpty()) *output = Nan::Undefined();
else *output = result.ToLocalChecked();
}
} // namespace