diff --git a/test/test_convert.cpp b/test/test_convert.cpp index ca0b80e..957b969 100644 --- a/test/test_convert.cpp +++ b/test/test_convert.cpp @@ -148,6 +148,9 @@ void test_convert_tuple(v8::Isolate* isolate) std::tuple const tuple_3{ 1, 2, 3 }; test_conv(isolate, tuple_3); + std::pair const pair{ false, 'N' }; + test_conv(isolate, pair); + check_ex("Tuple", [isolate, &tuple_1]() { // incorrect number of elements diff --git a/test/test_utility.cpp b/test/test_utility.cpp index b409cce..fdab319 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -6,6 +6,8 @@ #include #include +namespace { + template void test_ret(F&&) { @@ -61,11 +63,11 @@ void test_function_traits() char i(float) const volatile { return 0; } void operator()(int, char&) const volatile {} - float w; + float w = 0; const int x = 1; - volatile char y; + volatile char y = 0; const volatile bool z = true; - mutable volatile short zz; + mutable volatile short zz = 0; }; test_ret(&X::f); @@ -171,83 +173,107 @@ void test_is_callable() static_assert(!is_callable::value, "Y is not callable"); } -void test_type_traits() +void test_concepts() { - static_assert(v8pp::detail::is_string::value, "std::string"); - static_assert(!v8pp::detail::is_sequence::value, "std::string"); - static_assert(!v8pp::detail::is_mapping::value, "std::string"); - static_assert(!v8pp::detail::is_array::value, "std::string"); - - static_assert(v8pp::detail::is_string::value, "std::string_view"); - static_assert(v8pp::detail::is_string::value, "std::u16string"); - static_assert(v8pp::detail::is_string::value, "std::u16string_view"); - static_assert(v8pp::detail::is_string::value, "std::u32string"); - static_assert(v8pp::detail::is_string::value, "std::u32string_view"); - static_assert(v8pp::detail::is_string::value, "std::wstring"); - static_assert(v8pp::detail::is_string::value, "std::wstring_view"); - static_assert(v8pp::detail::is_string::value, "char const*"); - static_assert(v8pp::detail::is_string::value, "char16_t const*"); - static_assert(v8pp::detail::is_string::value, "char32_t const*"); - static_assert(v8pp::detail::is_string::value, "wchar_t const*"); - - static_assert(!v8pp::detail::is_string>::value, "std::array"); - static_assert(!v8pp::detail::is_mapping>::value, "std::array"); - static_assert(!v8pp::detail::is_sequence>::value, "std::array"); - static_assert(!v8pp::detail::is_sequence>::value, "std::array"); - static_assert(v8pp::detail::is_array>::value, "std::array"); - static_assert(!v8pp::detail::has_reserve>::value, "std::array"); - - static_assert(!v8pp::detail::is_string>::value, "std::vector"); - static_assert(!v8pp::detail::is_mapping>::value, "std::vector"); - static_assert(v8pp::detail::is_sequence>::value, "std::vector"); - static_assert(!v8pp::detail::is_array>::value, "std::vector"); - static_assert(v8pp::detail::has_reserve>::value, "std::vector"); - - static_assert(!v8pp::detail::is_string>::value, "std::deque"); - static_assert(!v8pp::detail::is_mapping>::value, "std::deque"); - static_assert(v8pp::detail::is_sequence>::value, "std::deque"); - static_assert(!v8pp::detail::is_array>::value, "std::deque"); - static_assert(!v8pp::detail::has_reserve>::value, "std::deque"); - - static_assert(!v8pp::detail::is_string>::value, "std::list"); - static_assert(!v8pp::detail::is_mapping>::value, "std::list"); - static_assert(v8pp::detail::is_sequence>::value, "std::list"); - static_assert(!v8pp::detail::is_array>::value, "std::list"); - static_assert(!v8pp::detail::has_reserve>::value, "std::list"); - - static_assert(!v8pp::detail::is_string>::value, "std::tuple"); - static_assert(!v8pp::detail::is_mapping>::value, "std::tuple"); - static_assert(!v8pp::detail::is_sequence>::value, "std::tuple"); - static_assert(!v8pp::detail::is_array>::value, "std::tuple"); - static_assert(v8pp::detail::is_tuple>::value, "std::tuple"); - - static_assert(!v8pp::detail::is_string>::value, "std::map"); - static_assert(v8pp::detail::is_mapping>::value, "std::map"); - static_assert(!v8pp::detail::is_sequence>::value, "std::map"); - static_assert(!v8pp::detail::is_array>::value, "std::map"); - - static_assert(!v8pp::detail::is_string>::value, "std::multimap"); - static_assert(v8pp::detail::is_mapping>>::value, "std::multimap"); - static_assert(!v8pp::detail::is_sequence>::value, "std::multimap"); - static_assert(!v8pp::detail::is_array>::value, "std::multimap"); - - static_assert(!v8pp::detail::is_string>::value, "std::unordered_map"); - static_assert(v8pp::detail::is_mapping>::value, "std::unordered_map"); - static_assert(!v8pp::detail::is_sequence>::value, "std::unordered_map"); - static_assert(!v8pp::detail::is_array>::value, "std::unordered_map"); - - static_assert(!v8pp::detail::is_array>::value, "std::unordered_multimap"); - static_assert(v8pp::detail::is_mapping>::value, "std::unordered_multimap"); - static_assert(!v8pp::detail::is_sequence>::value, "std::unordered_multimap"); - static_assert(!v8pp::detail::is_array>::value, "std::unordered_multimap"); + static_assert(v8pp::detail::String, "std::string"); + static_assert(!v8pp::detail::Sequence, "std::string"); + static_assert(!v8pp::detail::Mapping, "std::string"); + static_assert(!v8pp::detail::Array, "std::string"); + static_assert(v8pp::detail::HasReserve, "std::string"); + static_assert(!v8pp::detail::Tuple, "std::string"); + + static_assert(v8pp::detail::String, "std::string_view"); + static_assert(v8pp::detail::String, "std::u16string"); + static_assert(v8pp::detail::String, "std::u16string_view"); + static_assert(v8pp::detail::String, "std::u32string"); + static_assert(v8pp::detail::String, "std::u32string_view"); + static_assert(v8pp::detail::String, "std::wstring"); + static_assert(v8pp::detail::String, "std::wstring_view"); + static_assert(v8pp::detail::String, "char const*"); + static_assert(v8pp::detail::String, "char16_t const*"); + static_assert(v8pp::detail::String, "char32_t const*"); + static_assert(v8pp::detail::String, "wchar_t const*"); + + static_assert(!v8pp::detail::String>, "std::array"); + static_assert(!v8pp::detail::Mapping>, "std::array"); + static_assert(!v8pp::detail::Sequence>, "std::array"); + static_assert(!v8pp::detail::Sequence>, "std::array"); + static_assert(v8pp::detail::Array>, "std::array"); + static_assert(!v8pp::detail::HasReserve>, "std::array"); + static_assert(!v8pp::detail::Tuple>, "std::array"); + + static_assert(!v8pp::detail::String>, "std::vector"); + static_assert(!v8pp::detail::Mapping>, "std::vector"); + static_assert(v8pp::detail::Sequence>, "std::vector"); + static_assert(!v8pp::detail::Array>, "std::vector"); + static_assert(v8pp::detail::HasReserve>, "std::vector"); + static_assert(!v8pp::detail::Tuple>, "std::vector"); + + static_assert(!v8pp::detail::String>, "std::deque"); + static_assert(!v8pp::detail::Mapping>, "std::deque"); + static_assert(v8pp::detail::Sequence>, "std::deque"); + static_assert(!v8pp::detail::Array>, "std::deque"); + static_assert(!v8pp::detail::HasReserve>, "std::deque"); + static_assert(!v8pp::detail::Tuple>, "std::deque"); + + static_assert(!v8pp::detail::String>, "std::list"); + static_assert(!v8pp::detail::Mapping>, "std::list"); + static_assert(v8pp::detail::Sequence>, "std::list"); + static_assert(!v8pp::detail::Array>, "std::list"); + static_assert(!v8pp::detail::HasReserve>, "std::list"); + static_assert(!v8pp::detail::Tuple>, "std::list"); + + static_assert(!v8pp::detail::String>, "std::map"); + static_assert(v8pp::detail::Mapping>, "std::map"); + static_assert(!v8pp::detail::Sequence>, "std::map"); + static_assert(!v8pp::detail::Array>, "std::map"); + static_assert(!v8pp::detail::HasReserve>, "std::map"); + static_assert(!v8pp::detail::Tuple>, "std::map"); + + static_assert(!v8pp::detail::String>, "std::multimap"); + static_assert(v8pp::detail::Mapping>>, "std::multimap"); + static_assert(!v8pp::detail::Sequence>, "std::multimap"); + static_assert(!v8pp::detail::Array>, "std::multimap"); + static_assert(!v8pp::detail::HasReserve>, "std::multimap"); + static_assert(!v8pp::detail::Tuple>, "std::multimap"); + + static_assert(!v8pp::detail::String>, "std::unordered_map"); + static_assert(v8pp::detail::Mapping>, "std::unordered_map"); + static_assert(!v8pp::detail::Sequence>, "std::unordered_map"); + static_assert(!v8pp::detail::Array>, "std::unordered_map"); + static_assert(v8pp::detail::HasReserve>, "std::unordered_map"); + static_assert(!v8pp::detail::Tuple>, "std::unordered_map"); + + static_assert(!v8pp::detail::String>, "std::unordered_multimap"); + static_assert(v8pp::detail::Mapping>, "std::unordered_multimap"); + static_assert(!v8pp::detail::Sequence>, "std::unordered_multimap"); + static_assert(!v8pp::detail::Array>, "std::unordered_multimap"); + static_assert(v8pp::detail::HasReserve>, "std::unordered_multimap"); + static_assert(!v8pp::detail::Tuple>, "std::unordered_multimap"); + + static_assert(v8pp::detail::Tuple>, "std::tuple"); + static_assert(v8pp::detail::Tuple>>, "std::tuple"); + static_assert(v8pp::detail::Tuple>, "std::tuple"); + static_assert(!v8pp::detail::Tuple>, "std::tuple"); + static_assert(!v8pp::detail::Tuple, "std::tuple"); + static_assert(!v8pp::detail::Tuple, "std::tuple"); + static_assert(!v8pp::detail::String>, "std::tuple"); + static_assert(!v8pp::detail::Mapping>, "std::tuple"); + static_assert(!v8pp::detail::Sequence>, "std::tuple"); + static_assert(!v8pp::detail::Array>, "std::tuple"); + static_assert(!v8pp::detail::HasReserve>, "std::tuple"); static_assert(!v8pp::detail::is_shared_ptr::value, "int"); static_assert(v8pp::detail::is_shared_ptr>::value, "int"); + static_assert(v8pp::detail::is_shared_ptr>>::value, "std::vector::value, "std::string"); } +} // unnamed namespace + void test_utility() { - test_type_traits(); + test_concepts(); test_function_traits(); test_tuple_tail(); test_is_callable(); diff --git a/v8pp/convert.hpp b/v8pp/convert.hpp index 4e68aa6..5600c9d 100644 --- a/v8pp/convert.hpp +++ b/v8pp/convert.hpp @@ -44,14 +44,13 @@ struct invalid_argument : std::invalid_argument }; // converter specializations for string types -template -struct convert::value>::type> +template +struct convert { - using Char = typename String::value_type; - using Traits = typename String::traits_type; + using Char = typename T::value_type; + using Traits = typename T::traits_type; - static_assert(sizeof(Char) <= sizeof(uint16_t), - "only UTF-8 and UTF-16 strings are supported"); + static_assert(sizeof(Char) <= sizeof(uint16_t), "only UTF-8 and UTF-16 strings are supported"); // A string that converts to Char const* struct convertible_string : std::basic_string @@ -76,15 +75,22 @@ struct convert::value> throw invalid_argument(isolate, value, "String"); } + v8::HandleScope scope(isolate); + v8::Local str = value->ToString(isolate->GetCurrentContext()).ToLocalChecked(); + if constexpr (sizeof(Char) == 1) { - v8::String::Utf8Value const str(isolate, value); - return from_type(reinterpret_cast(*str), str.length()); + auto const len = str->Utf8Length(isolate); + from_type result(len, 0); + result.resize(str->WriteUtf8(isolate, result.data(), len, nullptr, v8::String::NO_NULL_TERMINATION | v8::String::REPLACE_INVALID_UTF8)); + return result; } else { - v8::String::Value const str(isolate, value); - return from_type(reinterpret_cast(*str), str.length()); + auto const len = str->Length(); + from_type result(len, 0); + result.resize(str->Write(isolate, reinterpret_cast(result.data()), 0, len, v8::String::NO_NULL_TERMINATION)); + return result; } } @@ -154,8 +160,8 @@ struct convert } }; -template -struct convert::value>::type> +template +struct convert { using from_type = T; using to_type = v8::Local; @@ -212,10 +218,10 @@ struct convert::value>::type> } }; -template -struct convert::value>::type> +template requires std::is_enum_v +struct convert { - using underlying_type = typename std::underlying_type::type; + using underlying_type = typename std::underlying_type_t; using from_type = T; using to_type = typename convert::to_type; @@ -232,13 +238,12 @@ struct convert::value>::type> static to_type to_v8(v8::Isolate* isolate, T value) { - return convert::to_v8(isolate, - static_cast(value)); + return convert::to_v8(isolate, static_cast(value)); } }; -template -struct convert::value>::type> +template +struct convert { using from_type = T; using to_type = v8::Local; @@ -265,18 +270,17 @@ struct convert::value>::typ }; // convert std::tuple <-> Array -template -struct convert> +template +struct convert { - using from_type = std::tuple; + using from_type = T; using to_type = v8::Local; - static constexpr size_t N = sizeof...(Ts); + static constexpr size_t N = std::tuple_size_v; static bool is_valid(v8::Isolate*, v8::Local value) { - return !value.IsEmpty() && value->IsArray() - && value.As()->Length() == N; + return !value.IsEmpty() && value->IsArray() && value.As()->Length() == N; } static from_type from_v8(v8::Isolate* isolate, v8::Local value) @@ -295,24 +299,23 @@ struct convert> private: template - static from_type from_v8_impl(v8::Isolate* isolate, v8::Local value, - std::index_sequence) + static from_type from_v8_impl(v8::Isolate* isolate, v8::Local value, std::index_sequence) { v8::HandleScope scope(isolate); v8::Local context = isolate->GetCurrentContext(); v8::Local array = value.As(); - return std::tuple{ v8pp::convert::from_v8(isolate, array->Get(context, Is).ToLocalChecked())... }; + return T{ v8pp::convert>::from_v8(isolate, array->Get(context, Is).ToLocalChecked())... }; } template - static to_type to_v8_impl(v8::Isolate* isolate, std::tuple const& value, std::index_sequence) + static to_type to_v8_impl(v8::Isolate* isolate, T const& value, std::index_sequence) { v8::EscapableHandleScope scope(isolate); v8::Local context = isolate->GetCurrentContext(); v8::Local result = v8::Array::New(isolate, N); - (void)std::initializer_list{ result->Set(context, Is, convert::to_v8(isolate, std::get(value))).FromJust()... }; + (void)std::initializer_list{ result->Set(context, Is, convert>::to_v8(isolate, std::get(value))).FromJust()... }; return scope.Escape(result); } @@ -322,7 +325,6 @@ struct convert> template struct convert> { -public: using from_type = std::variant; using to_type = v8::Local; @@ -472,12 +474,12 @@ struct convert> }; // convert Array <-> std::array, vector, deque, list -template -struct convert::value || detail::is_array::value>::type> +template requires detail::Sequence || detail::Array +struct convert { - using from_type = Sequence; + using from_type = T; using to_type = v8::Local; - using item_type = typename Sequence::value_type; + using item_type = typename T::value_type; static bool is_valid(v8::Isolate*, v8::Local value) { @@ -497,10 +499,9 @@ struct convert:: from_type result{}; - constexpr bool is_array = detail::is_array::value; - if constexpr (is_array) + if constexpr (detail::Array) { - constexpr size_t length = detail::is_array::length; + constexpr size_t length = std::tuple_size_v; if (array->Length() != length) { throw std::runtime_error("Invalid array length: expected " @@ -508,7 +509,7 @@ struct convert:: + std::to_string(array->Length())); } } - else if constexpr (detail::has_reserve::value) + else if constexpr (detail::HasReserve) { result.reserve(array->Length()); } @@ -516,7 +517,7 @@ struct convert:: for (uint32_t i = 0, count = array->Length(); i < count; ++i) { v8::Local item = array->Get(context, i).ToLocalChecked(); - if constexpr (is_array) + if constexpr (detail::Array) { result[i] = convert::from_v8(isolate, item); } @@ -551,14 +552,14 @@ struct convert:: }; // convert Object <-> std::{unordered_}{multi}map -template -struct convert::value>::type> +template +struct convert { - using from_type = Mapping; + using from_type = T; using to_type = v8::Local; - using Key = typename Mapping::key_type; - using Value = typename Mapping::mapped_type; + using Key = typename T::key_type; + using Value = typename T::mapped_type; static bool is_valid(v8::Isolate*, v8::Local value) { @@ -582,9 +583,7 @@ struct convert::val { v8::Local key = prop_names->Get(context, i).ToLocalChecked(); v8::Local val = object->Get(context, key).ToLocalChecked(); - const auto k = convert::from_v8(isolate, key); - const auto v = convert::from_v8(isolate, val); - result.emplace(k, v); + result.emplace(convert::from_v8(isolate, key), convert::from_v8(isolate, val)); } return result; } @@ -596,9 +595,7 @@ struct convert::val v8::Local result = v8::Object::New(isolate); for (auto const& item : value) { - const auto k = convert::to_v8(isolate, item.first); - const auto v = convert::to_v8(isolate, item.second); - result->Set(context, k, v).FromJust(); + result->Set(context, convert::to_v8(isolate, item.first), convert::to_v8(isolate, item.second)).FromJust(); } return scope.Escape(result); } @@ -654,12 +651,12 @@ struct is_wrapped_class> : std::false_type { }; -template -struct convert::value>::type> +template requires is_wrapped_class::value +struct convert { using from_type = T*; using to_type = v8::Local; - using class_type = typename std::remove_cv::type; + using class_type = typename std::remove_cv_t; static bool is_valid(v8::Isolate*, v8::Local value) { @@ -681,8 +678,8 @@ struct convert::value>::type> } }; -template -struct convert::value>::type> +template requires is_wrapped_class::value +struct convert { using from_type = T&; using to_type = v8::Local; @@ -715,8 +712,8 @@ struct convert::value>::type> } }; -template -struct convert, typename std::enable_if::value>::type> +template requires is_wrapped_class::value +struct convert> { using from_type = std::shared_ptr; using to_type = v8::Local; @@ -789,14 +786,12 @@ struct convert : convert template auto from_v8(v8::Isolate* isolate, v8::Local value) - -> decltype(convert::from_v8(isolate, value)) { return convert::from_v8(isolate, value); } template auto from_v8(v8::Isolate* isolate, v8::Local value, U const& default_value) - -> decltype(convert::from_v8(isolate, value)) { using return_type = decltype(convert::from_v8(isolate, value)); return convert::is_valid(isolate, value) ? @@ -814,8 +809,7 @@ inline v8::Local to_v8(v8::Isolate* isolate, char const* str, size_t } template -v8::Local to_v8(v8::Isolate* isolate, - char const (&str)[N], size_t len = N - 1) +v8::Local to_v8(v8::Isolate* isolate, char const (&str)[N], size_t len = N - 1) { return convert::to_v8(isolate, std::string_view(str, len)); } @@ -831,8 +825,7 @@ inline v8::Local to_v8(v8::Isolate* isolate, char16_t const* str, si } template -v8::Local to_v8(v8::Isolate* isolate, - char16_t const (&str)[N], size_t len = N - 1) +v8::Local to_v8(v8::Isolate* isolate, char16_t const (&str)[N], size_t len = N - 1) { return convert::to_v8(isolate, std::u16string_view(str, len)); } @@ -849,8 +842,7 @@ inline v8::Local to_v8(v8::Isolate* isolate, wchar_t const* str, siz } template -v8::Local to_v8(v8::Isolate* isolate, - wchar_t const (&str)[N], size_t len = N - 1) +v8::Local to_v8(v8::Isolate* isolate, wchar_t const (&str)[N], size_t len = N - 1) { return convert::to_v8(isolate, std::wstring_view(str, len)); } diff --git a/v8pp/utility.hpp b/v8pp/utility.hpp index 1fcee57..d0cb544 100644 --- a/v8pp/utility.hpp +++ b/v8pp/utility.hpp @@ -26,141 +26,75 @@ struct none template concept WideChar = std::same_as || std::same_as || std::same_as; -///////////////////////////////////////////////////////////////////////////// -// -// is_string -// template -struct is_string : std::false_type -{ -}; - -template -struct is_string> : std::true_type -{ -}; - -template -struct is_string> : std::true_type -{ -}; +concept String = std::same_as || std::same_as + || std::same_as || std::same_as + || std::same_as || std::same_as + || std::same_as || std::same_as + || std::same_as || std::same_as + || std::same_as || std::same_as; -template<> -struct is_string : std::true_type -{ -}; - -template<> -struct is_string : std::true_type -{ -}; - -template<> -struct is_string : std::true_type -{ +template +concept Mapping = requires(T t) { + typename T::key_type; + typename T::mapped_type; + t.begin(); + t.end(); }; -template<> -struct is_string : std::true_type -{ -}; - -///////////////////////////////////////////////////////////////////////////// -// -// is_mapping -// -template -struct is_mapping_impl : std::false_type -{ -}; template -struct is_mapping_impl().begin()), decltype(std::declval().end())>> : std::true_type -{ -}; +concept Sequence = requires(T t) { + typename T::value_type; + t.begin(); + t.end(); + t.emplace_back(std::declval()); +} && !String; template -using is_mapping = is_mapping_impl; - -///////////////////////////////////////////////////////////////////////////// -// -// is_sequence -// -template -struct is_sequence_impl : std::false_type -{ +concept HasReserve = requires(T t) { + t.reserve(0); }; template -struct is_sequence_impl().begin()), decltype(std::declval().end()), - decltype(std::declval().emplace_back(std::declval()))>> : std::negation> -{ +concept Array = requires(T t) { + typename std::tuple_size::type; + typename T::value_type; + t.begin(); + t.end(); }; template -using is_sequence = is_sequence_impl; - -///////////////////////////////////////////////////////////////////////////// -// -// has_reserve -// -template -struct has_reserve_impl : std::false_type -{ -}; +concept Tuple = requires(T t) { + typename std::tuple_size::type; + typename std::tuple_element<0, T>::type; + typename std::tuple_element_t<0, T>; + std::get<0>(t); +} && !Array; template -struct has_reserve_impl().reserve(0))>> : std::true_type -{ -}; +concept SharedPtr = std::same_as>; template -using has_reserve = has_reserve_impl; +using is_string = std::bool_constant>; -///////////////////////////////////////////////////////////////////////////// -// -// is_array -// template -struct is_array : std::false_type -{ -}; +using is_mapping = std::bool_constant>; -template -struct is_array> : std::true_type -{ - static constexpr size_t length = N; -}; +template +using is_sequence = std::bool_constant>; -///////////////////////////////////////////////////////////////////////////// -// -// is_tuple -// template -struct is_tuple : std::false_type -{ -}; +using has_reserve = std::bool_constant>; -template -struct is_tuple> : std::true_type -{ -}; +template +using is_array = std::bool_constant>; -///////////////////////////////////////////////////////////////////////////// -// -// is_shared_ptr -// template -struct is_shared_ptr : std::false_type -{ -}; +using is_tuple = std::bool_constant>; template -struct is_shared_ptr> : std::true_type -{ -}; +using is_shared_ptr = std::bool_constant>; ///////////////////////////////////////////////////////////////////////////// //