#pragma once #include "detail_types.h" #include "detail_typeinfo.h" #include "dukvalue.h" #include #include #include // for std::shared_ptr namespace dukglue { namespace types { #define DUKGLUE_SIMPLE_VALUE_TYPE(TYPE, DUK_IS_FUNC, DUK_GET_FUNC, DUK_PUSH_FUNC, PUSH_VALUE) \ template<> \ struct DukType { \ typedef std::true_type IsValueType; \ \ template \ static TYPE read(duk_context* ctx, duk_idx_t arg_idx) { \ if (DUK_IS_FUNC(ctx, arg_idx)) { \ return static_cast(DUK_GET_FUNC(ctx, arg_idx)); \ } else { \ duk_int_t type_idx = duk_get_type(ctx, arg_idx); \ duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected " #TYPE ", got %s", arg_idx, detail::get_type_name(type_idx)); \ } \ } \ \ template \ static void push(duk_context* ctx, TYPE value) { \ DUK_PUSH_FUNC(ctx, PUSH_VALUE); \ } \ }; DUKGLUE_SIMPLE_VALUE_TYPE(bool, duk_is_boolean, 0 != duk_get_boolean, duk_push_boolean, value) DUKGLUE_SIMPLE_VALUE_TYPE(uint8_t, duk_is_number, duk_get_uint, duk_push_uint, value) DUKGLUE_SIMPLE_VALUE_TYPE(uint16_t, duk_is_number, duk_get_uint, duk_push_uint, value) DUKGLUE_SIMPLE_VALUE_TYPE(uint32_t, duk_is_number, duk_get_uint, duk_push_uint, value) DUKGLUE_SIMPLE_VALUE_TYPE(uint64_t, duk_is_number, duk_get_number, duk_push_number, value) // have to cast to double DUKGLUE_SIMPLE_VALUE_TYPE(int8_t, duk_is_number, duk_get_int, duk_push_int, value) DUKGLUE_SIMPLE_VALUE_TYPE(int16_t, duk_is_number, duk_get_int, duk_push_int, value) DUKGLUE_SIMPLE_VALUE_TYPE(int32_t, duk_is_number, duk_get_int, duk_push_int, value) DUKGLUE_SIMPLE_VALUE_TYPE(int64_t, duk_is_number, duk_get_number, duk_push_number, value) // have to cast to double // signed char and unsigned char are surprisingly *both* different from char, at least in MSVC DUKGLUE_SIMPLE_VALUE_TYPE(char, duk_is_number, duk_get_int, duk_push_int, value) DUKGLUE_SIMPLE_VALUE_TYPE(float, duk_is_number, duk_get_number, duk_push_number, value) DUKGLUE_SIMPLE_VALUE_TYPE(double, duk_is_number, duk_get_number, duk_push_number, value) DUKGLUE_SIMPLE_VALUE_TYPE(std::string, duk_is_string, duk_get_string, duk_push_string, value.c_str()) // We have to do some magic for const char* to work correctly. // We override the "bare type" and "storage type" to both be const char*. // char* is a bit tricky because its "bare type" should still be const char*, to differentiate it from just char template<> struct Bare { typedef const char* type; }; template<> struct Bare { typedef const char* type; }; // the storage type should also be const char* - if we don't do this, it will end up as just "char" template<> struct ArgStorage { typedef const char* type; }; template<> struct DukType { typedef std::true_type IsValueType; template static const char* read(duk_context* ctx, duk_idx_t arg_idx) { if (duk_is_string(ctx, arg_idx)) { return duk_get_string(ctx, arg_idx); } else { duk_int_t type_idx = duk_get_type(ctx, arg_idx); duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected string, got %s", arg_idx, detail::get_type_name(type_idx)); } } template static void push(duk_context* ctx, const char* value) { duk_push_string(ctx, value); } }; // DukValue template<> struct DukType { typedef std::true_type IsValueType; template static DukValue read(duk_context* ctx, duk_idx_t arg_idx) { try { return DukValue::copy_from_stack(ctx, arg_idx); } catch (DukException& e) { // only DukException can be thrown by DukValue::copy_from_stack duk_error(ctx, DUK_ERR_ERROR, e.what()); } } template static void push(duk_context* ctx, const DukValue& value) { if (value.context() == NULL) { duk_error(ctx, DUK_ERR_ERROR, "DukValue is uninitialized"); return; } if (value.context() != ctx) { duk_error(ctx, DUK_ERR_ERROR, "DukValue comes from a different context"); return; } try { value.push(); } catch (DukException& e) { // only DukException can be thrown by DukValue::copy_from_stack duk_error(ctx, DUK_ERR_ERROR, e.what()); } } }; // std::vector (as value) template struct DukType< std::vector > { typedef std::true_type IsValueType; template static std::vector read(duk_context* ctx, duk_idx_t arg_idx) { if (!duk_is_array(ctx, arg_idx)) { duk_int_t type_idx = duk_get_type(ctx, arg_idx); duk_error(ctx, DUK_ERR_TYPE_ERROR, "Argument %d: expected array, got %s", arg_idx, detail::get_type_name(type_idx)); } duk_size_t len = duk_get_length(ctx, arg_idx); const duk_idx_t elem_idx = duk_get_top(ctx); std::vector vec; vec.reserve(len); for (duk_size_t i = 0; i < len; i++) { duk_get_prop_index(ctx, arg_idx, i); vec.push_back(DukType< typename Bare::type >::template read(ctx, elem_idx)); duk_pop(ctx); } return vec; } template static void push(duk_context* ctx, const std::vector& value) { duk_idx_t obj_idx = duk_push_array(ctx); for (size_t i = 0; i < value.size(); i++) { DukType< typename Bare::type >::template push(ctx, value[i]); duk_put_prop_index(ctx, obj_idx, i); } } }; // std::shared_ptr (as value) template struct DukType< std::shared_ptr > { typedef std::true_type IsValueType; static_assert(std::is_same::IsValueType, std::false_type>::value, "Dukglue can only use std::shared_ptr to non-value types!"); template static std::shared_ptr read(duk_context* ctx, duk_idx_t arg_idx) { if (duk_is_null(ctx, arg_idx)) return nullptr; if (!duk_is_object(ctx, arg_idx)) { duk_int_t type_idx = duk_get_type(ctx, arg_idx); duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected shared_ptr object, got ", arg_idx, detail::get_type_name(type_idx)); } duk_get_prop_string(ctx, arg_idx, "\xFF" "type_info"); if (!duk_is_pointer(ctx, -1)) // missing type_info, must not be a native object duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected shared_ptr object (missing type_info)", arg_idx); // make sure this object can be safely returned as a T* dukglue::detail::TypeInfo* info = static_cast(duk_get_pointer(ctx, -1)); if (!info->can_cast()) duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: wrong type of shared_ptr object", arg_idx); duk_pop(ctx); // pop type_info duk_get_prop_string(ctx, arg_idx, "\xFF" "shared_ptr"); if (!duk_is_pointer(ctx, -1)) duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: not a shared_ptr object (missing shared_ptr)", arg_idx); void* ptr = duk_get_pointer(ctx, -1); duk_pop(ctx); // pop pointer to shared_ptr return *((std::shared_ptr*) ptr); } static duk_ret_t shared_ptr_finalizer(duk_context* ctx) { duk_get_prop_string(ctx, 0, "\xFF" "shared_ptr"); std::shared_ptr* ptr = (std::shared_ptr*) duk_require_pointer(ctx, -1); duk_pop(ctx); // pop shared_ptr ptr if (ptr != NULL) { delete ptr; // for safety, set the pointer to undefined // (finalizers can run multiple times) duk_push_undefined(ctx); duk_put_prop_string(ctx, 0, "\xFF" "shared_ptr"); } return 0; } template static void push(duk_context* ctx, const std::shared_ptr& value) { if (value == nullptr) { duk_push_null(ctx); } else { dukglue::detail::ProtoManager::make_script_object(ctx, value.get()); // create + set shared_ptr duk_push_pointer(ctx, new std::shared_ptr(value)); duk_put_prop_string(ctx, -2, "\xFF" "shared_ptr"); // set shared_ptr finalizer duk_push_c_function(ctx, &shared_ptr_finalizer, 1); duk_set_finalizer(ctx, -2); } } }; // std::function /*template struct DukType< std::function > { typedef std::true_type IsValueType; template static std::function read(duk_context* ctx, duk_idx_t arg_idx) { DukValue callable = DukValue::copy_from_stack(ctx, -1, DUK_TYPE_MASK_OBJECT); return [ctx, callable] (ArgTs... args) -> RetT { dukglue_call(ctx, callable, args...); }; } template static void push(duk_context* ctx, std::function value) { static_assert(false, "Pushing an std::function has not been implemented yet. Sorry!"); } };*/ } }