Skip to content

Commit

Permalink
Support std::variant
Browse files Browse the repository at this point in the history
  • Loading branch information
zcbenz committed Apr 13, 2024
1 parent c1e738e commit 1452501
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/arguments.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ class Arguments {
napi_throw_type_error(env_, nullptr, ss.str().c_str());
}

bool NoMoreArgs() const { return insufficient_arguments_; }

napi_value This() const { return this_; }
void* Data() const { return data_; }
size_t Length() const { return argc_; }
Expand Down
22 changes: 20 additions & 2 deletions src/callback_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,30 @@ struct ArgConverter {
}
};

// Allow optional arguments.
// Allow optional arguments at the end.
template<typename T>
struct ArgConverter<std::optional<T>> {
static inline std::optional<std::optional<T>> GetNext(
Arguments* args, int flags, bool is_first) {
return ArgConverter<T>::GetNext(args, flags, is_first);
std::optional<T> result = ArgConverter<T>::GetNext(args, flags, is_first);
if (!result && !args->NoMoreArgs()) // error if type mis-match
return std::nullopt;
return result;
}
};

// Allow optional variant at the end if it contains monostate.
template<typename... ArgTypes>
struct ArgConverter<std::variant<std::monostate, ArgTypes...>> {
using V = std::variant<std::monostate, ArgTypes...>;
static inline std::optional<V> GetNext(
Arguments* args, int flags, bool is_first) {
std::optional<V> result = args->GetNext<V>();
if (result) // success conversion
return result;
if (args->NoMoreArgs()) // arg is omitted
return std::monostate();
return std::nullopt;
}
};

Expand Down
49 changes: 49 additions & 0 deletions src/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <set>
#include <string>
#include <tuple>
#include <variant>
#include <vector>

#include "src/template_util.h"
Expand Down Expand Up @@ -705,6 +706,54 @@ struct Type<std::tuple<ArgTypes...>> {
}
};

// Converter for converting std::variant between js and C++.
template<typename... ArgTypes>
struct Type<std::variant<ArgTypes...>> {
using V = std::variant<ArgTypes...>;

static constexpr const char* name = "Variant";
static napi_status ToNode(napi_env env, const V& var, napi_value* result) {
napi_status s = napi_generic_failure;
std::visit([env, result, &s](const auto& arg) {
s = ki::ToNode<decltype(arg)>(env, arg, result);
}, var);
return s;
}
static inline std::optional<V> FromNode(napi_env env, napi_value value) {
return GetVar(env, value);
}

private:
template<std::size_t I = 0>
static std::optional<V> GetVar(napi_env env, napi_value value) {
if constexpr (I < std::variant_size_v<V>) {
using T = std::variant_alternative_t<I, V>;
std::optional<T> result = Type<T>::FromNode(env, value);
return result ? std::move(*result) : GetVar<I + 1>(env, value);
}
return std::nullopt;
}
};

// The monostate is a special type for std::variant, when a variant includes it,
// we consider the type accepting undefined/null.
template<>
struct Type<std::monostate> {
static constexpr const char* name = ""; // no name for monostate
static napi_status ToNode(napi_env env, std::monostate, napi_value* result) {
return napi_get_undefined(env, result);
}
static inline std::optional<std::monostate> FromNode(napi_env env,
napi_value value) {
napi_valuetype type;
if (napi_typeof(env, value, &type) != napi_ok)
return std::nullopt;
if (type == napi_boolean || type == napi_null)
return std::monostate();
return std::nullopt;
}
};

} // namespace ki

#endif // SRC_TYPES_H_

0 comments on commit 1452501

Please sign in to comment.