Skip to content

Commit

Permalink
support non-copyable typemaps
Browse files Browse the repository at this point in the history
  • Loading branch information
mmomtchev committed Nov 16, 2023
1 parent 58c1518 commit aa99abf
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 155 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ public:
// An optional public member may specify the number
// of consumed JS arguments (considered 1 if not present)
int Inputs;
// Optionally, if the typemap has a costly state, only move
// semantics may be specified, nobind can work with this type
FromJS(const FromJS &) = delete;
FromJS(FromJS &&) = default;
};

// This typemap will be used when C++ returns an int
Expand All @@ -261,6 +265,10 @@ public:
// The second part will be called on the main V8 thread
// It should produce a JS value
inline Napi::Value Get() { return Napi::String::New(env_, std::to_string(val_)); }
// Optionally, if the typemap has a costly state, only move
// semantics may be specified, nobind can work with this type
ToJS(const ToJS &) = delete;
ToJS(ToJS &&) = default;
};
} // namespace TypemapOverrides

Expand Down
2 changes: 1 addition & 1 deletion include/nofunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class FunctionWrapperTasklet : public Napi::AsyncWorker {
std::tuple<FromJS_t<ARGS>...> args_;

public:
FunctionWrapperTasklet(Napi::Env env, Napi::Promise::Deferred deferred, const std::tuple<FromJS_t<ARGS>...> &&args)
FunctionWrapperTasklet(Napi::Env env, Napi::Promise::Deferred deferred, std::tuple<FromJS_t<ARGS>...> &&args)
: AsyncWorker(env, "nobind_AsyncWorker"), env_(env), deferred_(deferred), output(), args_(std::move(args)) {}

template <std::size_t... I> void ExecuteImpl(std::index_sequence<I...>) {
Expand Down
40 changes: 16 additions & 24 deletions include/nonumbermaps.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ namespace Typemap {

template <typename T> class FromJSInt32 {
T val_;
#ifdef DEBUG_COPYING
FromJSInt32(FromJSInt32 &) = delete;
#endif

public:
inline explicit FromJSInt32(const Napi::Value &val) {
Expand All @@ -19,25 +16,23 @@ template <typename T> class FromJSInt32 {
val_ = static_cast<T>(val.ToNumber().Int32Value());
}
inline T Get() { return val_; }
FromJSInt32(const FromJSInt32 &) = delete;
FromJSInt32(FromJSInt32 &&) = default;
};

template <typename T, const ReturnAttribute &RETATTR> class ToJSInt32 {
Napi::Env env_;
T val_;
#ifdef DEBUG_COPYING
ToJSInt32(ToJSInt32 &) = delete;
#endif

public:
inline explicit ToJSInt32(Napi::Env env, T val) : env_(env), val_(val) {}
inline Napi::Value Get() { return Napi::Number::New(env_, static_cast<int32_t>(val_)); }
ToJSInt32(const ToJSInt32 &) = delete;
ToJSInt32(ToJSInt32 &&) = default;
};

template <typename T> class FromJSUint32 {
T val_;
#ifdef DEBUG_COPYING
FromJSUint32(FromJSUint32 &) = delete;
#endif

public:
inline explicit FromJSUint32(const Napi::Value &val) {
Expand All @@ -47,25 +42,23 @@ template <typename T> class FromJSUint32 {
val_ = static_cast<T>(val.ToNumber().Uint32Value());
}
inline T Get() { return val_; }
FromJSUint32(const FromJSUint32 &) = delete;
FromJSUint32(FromJSUint32 &&) = default;
};

template <typename T, const ReturnAttribute &RETATTR> class ToJSUint32 {
Napi::Env env_;
T val_;
#ifdef DEBUG_COPYING
ToJSUint32(ToJSUint32 &) = delete;
#endif

public:
inline explicit ToJSUint32(Napi::Env env, T val) : env_(env), val_(val) {}
inline Napi::Value Get() { return Napi::Number::New(env_, static_cast<uint32_t>(val_)); }
ToJSUint32(const ToJSUint32 &) = delete;
ToJSUint32(ToJSUint32 &&) = default;
};

template <typename T> class FromJSInt64 {
T val_;
#ifdef DEBUG_COPYING
FromJSInt64(FromJSInt64 &) = delete;
#endif

public:
inline explicit FromJSInt64(const Napi::Value &val) {
Expand All @@ -75,25 +68,23 @@ template <typename T> class FromJSInt64 {
val_ = static_cast<T>(val.ToNumber().Int64Value());
}
inline T Get() { return val_; }
FromJSInt64(const FromJSInt64 &) = delete;
FromJSInt64(FromJSInt64 &&) = default;
};

template <typename T, const ReturnAttribute &RETATTR> class ToJSInt64 {
Napi::Env env_;
T val_;
#ifdef DEBUG_COPYING
ToJSInt64(ToJSInt64 &) = delete;
#endif

public:
inline explicit ToJSInt64(Napi::Env env, T val) : env_(env), val_(val) {}
inline Napi::Value Get() { return Napi::Number::New(env_, static_cast<int64_t>(val_)); }
ToJSInt64(const ToJSInt64 &) = delete;
ToJSInt64(ToJSInt64 &&) = default;
};

template <typename T> class FromJSDouble {
T val_;
#ifdef DEBUG_COPYING
FromJSDouble(FromJSDouble &) = delete;
#endif

public:
inline explicit FromJSDouble(const Napi::Value &val) {
Expand All @@ -103,18 +94,19 @@ template <typename T> class FromJSDouble {
val_ = static_cast<T>(val.ToNumber().DoubleValue());
}
inline T Get() { return val_; }
FromJSDouble(const FromJSDouble &) = delete;
FromJSDouble(FromJSDouble &&) = default;
};

template <typename T, const ReturnAttribute &RETATTR> class ToJSDouble {
Napi::Env env_;
T val_;
#ifdef DEBUG_COPYING
ToJSDouble(ToJSDouble &) = delete;
#endif

public:
inline explicit ToJSDouble(Napi::Env env, T val) : env_(env), val_(val) {}
inline Napi::Value Get() { return Napi::Number::New(env_, static_cast<double>(val_)); }
ToJSDouble(const ToJSDouble &) = delete;
ToJSDouble(ToJSDouble &&) = default;
};

#define TYPEMAPS_FOR_NUMBER(CTYPE, JSTYPE) \
Expand Down
6 changes: 3 additions & 3 deletions include/noobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ template <typename CLASS> class NoObjectWrap : public Napi::ObjectWrap<NoObjectW

public:
MethodWrapperTasklet(Napi::Env env, Napi::Promise::Deferred deferred, CLASS *self,
const std::tuple<FromJS_t<ARGS>...> &&args)
std::tuple<FromJS_t<ARGS>...> &&args)
: AsyncWorker(env, "nobind_AsyncWorker"), env_(env), deferred_(deferred), output(), args_(std::move(args)),
self_(static_cast<BASE *>(self)) {}

Expand Down Expand Up @@ -230,8 +230,8 @@ template <typename CLASS> class NoObjectWrap : public Napi::ObjectWrap<NoObjectW
// Alas, std::forward_as_tuple does not guarantee
// the evaluation order of its arguments, only *braced-init-list* lists do
// https://en.cppreference.com/w/cpp/language/list_initialization
auto tasklet = new MethodWrapperTasklet<RETATTR, BASE, FUNC, RETURN, ARGS...>(
env, deferred, self, {FromJSArgs<ARGS>(info, idx)...});
auto tasklet = new MethodWrapperTasklet<RETATTR, BASE, FUNC, RETURN, ARGS...>(env, deferred, self,
{FromJSArgs<ARGS>(info, idx)...});
CheckArgLength(env, idx, info.Length());

tasklet->Queue();
Expand Down
157 changes: 86 additions & 71 deletions include/nostl.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,83 +8,98 @@ namespace Nobind {

namespace Typemap {

#define TYPEMAPS_FOR_STD_VECTOR(VECTOR) \
template <typename T> class FromJS<VECTOR> { \
std::remove_cv_t<std::remove_reference_t<VECTOR>> val_; \
\
public: \
inline explicit FromJS(const Napi::Value &val) { \
if (!val.IsArray()) { \
throw Napi::TypeError::New(val.Env(), "Expected an array"); \
} \
Napi::Array array = val.As<Napi::Array>(); \
val_.reserve(array.Length()); \
for (size_t i = 0; i < array.Length(); i++) { \
val_.push_back(FromJSValue<T>(array.Get(i)).Get()); \
} \
} \
\
inline VECTOR Get() { return val_; } \
}; \
\
template <typename T, const ReturnAttribute &RETATTR> class ToJS<VECTOR, RETATTR> { \
Napi::Env env_; \
std::remove_cv_t<std::remove_reference_t<VECTOR>> val_; \
\
public: \
inline explicit ToJS(Napi::Env env, VECTOR val) : env_(env), val_(val) {} \
inline Napi::Value Get() { \
Napi::Array array = Napi::Array::New(env_, val_.size()); \
for (size_t i = 0; i < val_.size(); i++) { \
array.Set(i, ToJS<T, RETATTR>(env_, val_[i]).Get()); \
} \
return array; \
} \
};
template <typename V, typename T> class FromJSVector {
std::remove_cv_t<std::remove_reference_t<V>> val_;

public:
inline explicit FromJSVector(const Napi::Value &val) {
if (!val.IsArray()) {
throw Napi::TypeError::New(val.Env(), "Expected an array");
}
Napi::Array array = val.As<Napi::Array>();
val_.reserve(array.Length());
for (size_t i = 0; i < array.Length(); i++) {
val_.push_back(FromJSValue<T>(array.Get(i)).Get());
}
}

inline V Get() { return val_; }
FromJSVector(const FromJSVector &) = delete;
FromJSVector(FromJSVector &&) = default;
};

template <typename V, typename T, const ReturnAttribute &RETATTR> class ToJSVector {
Napi::Env env_;
std::remove_cv_t<std::remove_reference_t<V>> val_;

public:
inline explicit ToJSVector(Napi::Env env, V val) : env_(env), val_(val) {}
inline Napi::Value Get() {
Napi::Array array = Napi::Array::New(env_, val_.size());
for (size_t i = 0; i < val_.size(); i++) {
array.Set(i, ToJS<T, RETATTR>(env_, val_[i]).Get());
}
return array;
}
ToJSVector(const ToJSVector &) = delete;
ToJSVector(ToJSVector &&) = default;
};

template <typename M, typename T> class FromJSMap {
std::remove_cv_t<std::remove_reference_t<M>> val_;

public:
inline explicit FromJSMap(const Napi::Value &val) {
if (!val.IsObject()) {
throw Napi::TypeError::New(val.Env(), "Expected an object");
}
Napi::Object object = val.ToObject();
for (auto prop : object) {
val_.insert({prop.first.ToString().Utf8Value(), FromJSValue<T>(prop.second).Get()});
}
}

inline M Get() { return val_; }
FromJSMap(const FromJSMap &) = delete;
FromJSMap(FromJSMap &&) = default;
};

template <typename M, typename T, const ReturnAttribute &RETATTR> class ToJSMap {
Napi::Env env_;
std::remove_cv_t<std::remove_reference_t<M>> val_;

public:
inline explicit ToJSMap(Napi::Env env, M val) : env_(env), val_(val) {}
inline Napi::Value Get() {
Napi::Object object = Napi::Object::New(env_);
for (auto const &prop : val_) {
object.Set(Napi::String::New(env_, prop.first), ToJS<T, RETATTR>(env_, prop.second).Get());
}
return object;
}
ToJSMap(const ToJSMap &) = delete;
ToJSMap(ToJSMap &&) = default;
};

#define TYPEMAPS_FOR_STD_MAP(MAP) \
template <typename T> class FromJS<MAP> { \
std::remove_cv_t<std::remove_reference_t<MAP>> val_; \
\
public: \
inline explicit FromJS(const Napi::Value &val) { \
if (!val.IsObject()) { \
throw Napi::TypeError::New(val.Env(), "Expected an object"); \
} \
Napi::Object object = val.ToObject(); \
for (auto prop : object) { \
val_.insert({prop.first.ToString().Utf8Value(), FromJSValue<T>(prop.second).Get()}); \
} \
} \
\
inline MAP Get() { return val_; } \
#define TYPEMAPS_FOR_STL(CONTAINER, CLASS) \
template <typename T> struct FromJS<CONTAINER> : public FromJS##CLASS<CONTAINER, T> { \
using FromJS##CLASS<CONTAINER, T>::FromJS##CLASS; \
}; \
\
template <typename T, const ReturnAttribute &RETATTR> class ToJS<MAP, RETATTR> { \
Napi::Env env_; \
std::remove_cv_t<std::remove_reference_t<MAP>> val_; \
\
public: \
inline explicit ToJS(Napi::Env env, MAP val) : env_(env), val_(val) {} \
inline Napi::Value Get() { \
Napi::Object object = Napi::Object::New(env_); \
for (auto const &prop : val_) { \
object.Set(Napi::String::New(env_, prop.first), ToJS<T, RETATTR>(env_, prop.second).Get()); \
} \
return object; \
} \
template <typename T, const ReturnAttribute &RETATTR> \
struct ToJS<CONTAINER, RETATTR> : public ToJS##CLASS<CONTAINER, T, RETATTR> { \
using ToJS##CLASS<CONTAINER, T, RETATTR>::ToJS##CLASS; \
};

TYPEMAPS_FOR_STD_VECTOR(std::vector<T>);
TYPEMAPS_FOR_STD_VECTOR(std::vector<T> &);
TYPEMAPS_FOR_STD_VECTOR(const std::vector<T>);
TYPEMAPS_FOR_STD_VECTOR(const std::vector<T> &);
TYPEMAPS_FOR_STL(std::vector<T>, Vector);
TYPEMAPS_FOR_STL(std::vector<T> &, Vector);
TYPEMAPS_FOR_STL(const std::vector<T>, Vector);
TYPEMAPS_FOR_STL(const std::vector<T> &, Vector);

template <typename T> using string_map = std::map<std::string, T>;
TYPEMAPS_FOR_STD_MAP(string_map<T>);
TYPEMAPS_FOR_STD_MAP(string_map<T> &);
TYPEMAPS_FOR_STD_MAP(const string_map<T>);
TYPEMAPS_FOR_STD_MAP(const string_map<T> &);
TYPEMAPS_FOR_STL(string_map<T>, Map);
TYPEMAPS_FOR_STL(string_map<T> &, Map);
TYPEMAPS_FOR_STL(const string_map<T>, Map);
TYPEMAPS_FOR_STL(const string_map<T> &, Map);

} // namespace Typemap

Expand Down
Loading

0 comments on commit aa99abf

Please sign in to comment.