/* * Tencent is pleased to support the open source community by making ScriptX available. * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "Reference.h" #include "Scope.h" #include "Utils.h" #include "types.h" namespace script::converter { /** * traits to convert type from Local to native cpp types and vice versa. * * natively supported C++ types are: * * 1. void * * 2. bool * * 3. any float type : float double * * 4. any int type : int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_t size_t * * 5. any string type: string string_view char* char8_t* u8string u8string_view * * 6. all kind of Local reference * * 7. any pointer of subclass of ScriptClass * * see docs and UnitTests for more detail * */ /** * specialize this template to define your custom converter * * 1. MUST under namespace ::script::converter namespace * * 2. do as the following code shows * * \code * namespace script::converter { * * template <> * struct Converter { * // convert custom type T to Local * static Local toScript(T&& value) { ...; } * * // convert Local to custom type T * static T toCpp(const Local& value) { ...; } * }; * * } * \endcode * * @tparam T * @tparam enableIfHolder */ template struct Converter; // specialized as the following code /* namespace script::converter { template <> struct Converter { // convert custom type T to Local static Local toScript(T&& value) { ...; } // convert Local to custom type T static T toCpp(const Local& value) { ...; } }; } */ // ===== implementations ===== template <> struct Converter { static Local toScript(float value) { return Number::newNumber(value); } static float toCpp(const Local& value) { return static_cast(value.asNumber().toFloat()); } }; // all other floating types are converted using double as bridge template struct Converter>> { static Local toScript(T value) { return Number::newNumber(static_cast(value)); } static double toCpp(const Local& value) { return static_cast(value.asNumber().toDouble()); } }; template <> struct Converter { static Local toScript(int32_t value) { return Number::newNumber(value); } static int32_t toCpp(const Local& value) { return value.asNumber().toInt32(); } }; // all other int types are converted using int64_t as bridge template struct Converter>> { static Local toScript(T value) { return Number::newNumber(static_cast(value)); } static T toCpp(const Local& value) { return static_cast(value.asNumber().toInt64()); } }; template <> struct Converter { static Local toScript(bool value) { return Boolean::newBoolean(value); } static bool toCpp(const Local& value) { return value.asBoolean().value(); } }; /** * convert any sub class of ScriptClass* to reference */ template struct Converter>> { static Local toScript(const T* value) { return value->getScriptObject(); } static T* toCpp(const Local& value) { // `T*` may be `const Script*`, so we must remove const of `T` auto ptr = EngineScope::currentEngine()->getNativeInstance>(value); if (!ptr) { throw Exception("value is not an instance of given native class"); } return ptr; } }; template struct Converter, std::enable_if_t>> { static Local toScript(const T& value) { return *value->getScriptObject(); } static std::reference_wrapper toCpp(const Local& value) { return *Converter::toCpp(value); } }; // not actually used, just to make isConvertible be true on void return type template <> struct Converter { static Local toScript() { return {}; } static void toCpp(const Local&) {} }; /** * any supported string */ template struct Converter> { static Local toScript(const T& value) { return String::newString(value); } static T toCpp(const StringHolder& holder) { if constexpr (std::is_same_v) { return holder.string(); } else if constexpr (std::is_same_v) { return holder.stringView(); } else if constexpr (std::is_same_v) { return holder.c_str(); } #ifdef __cpp_lib_char8_t else if constexpr (std::is_same_v) { return holder.u8string(); } else if constexpr (std::is_same_v) { return holder.u8stringView(); } else if constexpr (std::is_same_v) { return holder.c_u8str(); } #endif else { // unreachable abort(); } } static T toCpp(const Local& str) { return toCpp(str.asString().toStringHolder()); } }; // ScriptX types bypass template <> struct Converter> { static Local toScript(const Local& value) { return value; } static Local toCpp(const Local& value) { return value; } }; #define ConverterSubType(Type) \ template <> \ struct Converter> { \ static Local toScript(const Local& value) { return value; } \ static Local toCpp(const Local& value) { return value.as##Type(); } \ } ConverterSubType(Object); ConverterSubType(String); ConverterSubType(Number); ConverterSubType(Boolean); ConverterSubType(Function); ConverterSubType(Array); ConverterSubType(ByteBuffer); ConverterSubType(Unsupported); #undef ConverterSubType } // namespace script::converter namespace script { namespace internal { // like std::decay, // except decay ScriptClass& to std::reference_wrapper template struct ConverterDecay { using type = std::decay_t; }; template struct ConverterDecay> && std::is_lvalue_reference_v>> { using type = std::reference_wrapper>; }; template using TypeConverter = script::converter::Converter::type>; template struct IsConvertibleHelper : std::false_type {}; template struct IsConvertibleHelper::toScript)>> : std::true_type {}; } // namespace internal namespace converter { template constexpr bool isConvertible = internal::IsConvertibleHelper::value; } // namespace converter } // namespace script