1 #pragma once 2 3 #include "detail_types.h" 4 #include "detail_typeinfo.h" 5 #include "dukvalue.h" 6 7 #include <vector> 8 #include <stdint.h> 9 #include <memory> // for std::shared_ptr 10 11 namespace dukglue { 12 namespace types { 13 14 #define DUKGLUE_SIMPLE_VALUE_TYPE(TYPE, DUK_IS_FUNC, DUK_GET_FUNC, DUK_PUSH_FUNC, PUSH_VALUE) \ 15 template<> \ 16 struct DukType<TYPE> { \ 17 typedef std::true_type IsValueType; \ 18 \ 19 template<typename FullT> \ 20 static TYPE read(duk_context* ctx, duk_idx_t arg_idx) { \ 21 if (DUK_IS_FUNC(ctx, arg_idx)) { \ 22 return static_cast<TYPE>(DUK_GET_FUNC(ctx, arg_idx)); \ 23 } else { \ 24 duk_int_t type_idx = duk_get_type(ctx, arg_idx); \ 25 duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected " #TYPE ", got %s", arg_idx, detail::get_type_name(type_idx)); \ 26 } \ 27 } \ 28 \ 29 template<typename FullT> \ 30 static void push(duk_context* ctx, TYPE value) { \ 31 DUK_PUSH_FUNC(ctx, PUSH_VALUE); \ 32 } \ 33 }; 34 35 DUKGLUE_SIMPLE_VALUE_TYPE(bool, duk_is_boolean, 0 != duk_get_boolean, duk_push_boolean, value) 36 37 DUKGLUE_SIMPLE_VALUE_TYPE(uint8_t, duk_is_number, duk_get_uint, duk_push_uint, value) 38 DUKGLUE_SIMPLE_VALUE_TYPE(uint16_t, duk_is_number, duk_get_uint, duk_push_uint, value) 39 DUKGLUE_SIMPLE_VALUE_TYPE(uint32_t, duk_is_number, duk_get_uint, duk_push_uint, value) 40 DUKGLUE_SIMPLE_VALUE_TYPE(uint64_t, duk_is_number, duk_get_number, duk_push_number, value) // have to cast to double 41 42 DUKGLUE_SIMPLE_VALUE_TYPE(int8_t, duk_is_number, duk_get_int, duk_push_int, value) 43 DUKGLUE_SIMPLE_VALUE_TYPE(int16_t, duk_is_number, duk_get_int, duk_push_int, value) 44 DUKGLUE_SIMPLE_VALUE_TYPE(int32_t, duk_is_number, duk_get_int, duk_push_int, value) 45 DUKGLUE_SIMPLE_VALUE_TYPE(int64_t, duk_is_number, duk_get_number, duk_push_number, value) // have to cast to double 46 47 // signed char and unsigned char are surprisingly *both* different from char, at least in MSVC 48 DUKGLUE_SIMPLE_VALUE_TYPE(char, duk_is_number, duk_get_int, duk_push_int, value) 49 50 DUKGLUE_SIMPLE_VALUE_TYPE(float, duk_is_number, duk_get_number, duk_push_number, value) 51 DUKGLUE_SIMPLE_VALUE_TYPE(double, duk_is_number, duk_get_number, duk_push_number, value) 52 53 DUKGLUE_SIMPLE_VALUE_TYPE(std::string, duk_is_string, duk_get_string, duk_push_string, value.c_str()) 54 55 // We have to do some magic for const char* to work correctly. 56 // We override the "bare type" and "storage type" to both be const char*. 57 // char* is a bit tricky because its "bare type" should still be const char*, to differentiate it from just char 58 template<> 59 struct Bare<char*> { 60 typedef const char* type; 61 }; 62 template<> 63 struct Bare<const char*> { 64 typedef const char* type; 65 }; 66 67 // the storage type should also be const char* - if we don't do this, it will end up as just "char" 68 template<> 69 struct ArgStorage<const char*> { 70 typedef const char* type; 71 }; 72 73 template<> 74 struct DukType<const char*> { 75 typedef std::true_type IsValueType; 76 77 template<typename FullT> 78 static const char* read(duk_context* ctx, duk_idx_t arg_idx) { 79 if (duk_is_string(ctx, arg_idx)) { 80 return duk_get_string(ctx, arg_idx); 81 } else { 82 duk_int_t type_idx = duk_get_type(ctx, arg_idx); 83 duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected string, got %s", arg_idx, detail::get_type_name(type_idx)); 84 } 85 } 86 87 template<typename FullT> 88 static void push(duk_context* ctx, const char* value) { 89 duk_push_string(ctx, value); 90 } 91 }; 92 93 // DukValue 94 template<> 95 struct DukType<DukValue> { 96 typedef std::true_type IsValueType; 97 98 template <typename FullT> 99 static DukValue read(duk_context* ctx, duk_idx_t arg_idx) { 100 try { 101 return DukValue::copy_from_stack(ctx, arg_idx); 102 } catch (DukException& e) { 103 // only DukException can be thrown by DukValue::copy_from_stack 104 duk_error(ctx, DUK_ERR_ERROR, e.what()); 105 } 106 } 107 108 template <typename FullT> 109 static void push(duk_context* ctx, const DukValue& value) { 110 if (value.context() == NULL) { 111 duk_error(ctx, DUK_ERR_ERROR, "DukValue is uninitialized"); 112 return; 113 } 114 115 if (value.context() != ctx) { 116 duk_error(ctx, DUK_ERR_ERROR, "DukValue comes from a different context"); 117 return; 118 } 119 120 try { 121 value.push(); 122 } catch (DukException& e) { 123 // only DukException can be thrown by DukValue::copy_from_stack 124 duk_error(ctx, DUK_ERR_ERROR, e.what()); 125 } 126 } 127 }; 128 129 // std::vector (as value) 130 template<typename T> 131 struct DukType< std::vector<T> > { 132 typedef std::true_type IsValueType; 133 134 template <typename FullT> 135 static std::vector<T> read(duk_context* ctx, duk_idx_t arg_idx) { 136 if (!duk_is_array(ctx, arg_idx)) { 137 duk_int_t type_idx = duk_get_type(ctx, arg_idx); 138 duk_error(ctx, DUK_ERR_TYPE_ERROR, "Argument %d: expected array, got %s", arg_idx, detail::get_type_name(type_idx)); 139 } 140 141 duk_size_t len = duk_get_length(ctx, arg_idx); 142 const duk_idx_t elem_idx = duk_get_top(ctx); 143 144 std::vector<T> vec; 145 vec.reserve(len); 146 for (duk_size_t i = 0; i < len; i++) { 147 duk_get_prop_index(ctx, arg_idx, i); 148 vec.push_back(DukType< typename Bare<T>::type >::template read<T>(ctx, elem_idx)); 149 duk_pop(ctx); 150 } 151 return vec; 152 } 153 154 template <typename FullT> 155 static void push(duk_context* ctx, const std::vector<T>& value) { 156 duk_idx_t obj_idx = duk_push_array(ctx); 157 158 for (size_t i = 0; i < value.size(); i++) { 159 DukType< typename Bare<T>::type >::template push<T>(ctx, value[i]); 160 duk_put_prop_index(ctx, obj_idx, i); 161 } 162 } 163 }; 164 165 // std::shared_ptr (as value) 166 template<typename T> 167 struct DukType< std::shared_ptr<T> > { 168 typedef std::true_type IsValueType; 169 170 static_assert(std::is_same<typename DukType<T>::IsValueType, std::false_type>::value, "Dukglue can only use std::shared_ptr to non-value types!"); 171 172 template <typename FullT> 173 static std::shared_ptr<T> read(duk_context* ctx, duk_idx_t arg_idx) { 174 if (duk_is_null(ctx, arg_idx)) 175 return nullptr; 176 177 if (!duk_is_object(ctx, arg_idx)) { 178 duk_int_t type_idx = duk_get_type(ctx, arg_idx); 179 duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected shared_ptr object, got ", arg_idx, detail::get_type_name(type_idx)); 180 } 181 182 duk_get_prop_string(ctx, arg_idx, "\xFF" "type_info"); 183 if (!duk_is_pointer(ctx, -1)) // missing type_info, must not be a native object 184 duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: expected shared_ptr object (missing type_info)", arg_idx); 185 186 // make sure this object can be safely returned as a T* 187 dukglue::detail::TypeInfo* info = static_cast<dukglue::detail::TypeInfo*>(duk_get_pointer(ctx, -1)); 188 if (!info->can_cast<T>()) 189 duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: wrong type of shared_ptr object", arg_idx); 190 duk_pop(ctx); // pop type_info 191 192 duk_get_prop_string(ctx, arg_idx, "\xFF" "shared_ptr"); 193 if (!duk_is_pointer(ctx, -1)) 194 duk_error(ctx, DUK_RET_TYPE_ERROR, "Argument %d: not a shared_ptr object (missing shared_ptr)", arg_idx); 195 void* ptr = duk_get_pointer(ctx, -1); 196 duk_pop(ctx); // pop pointer to shared_ptr 197 198 return *((std::shared_ptr<T>*) ptr); 199 } 200 201 static duk_ret_t shared_ptr_finalizer(duk_context* ctx) 202 { 203 duk_get_prop_string(ctx, 0, "\xFF" "shared_ptr"); 204 std::shared_ptr<T>* ptr = (std::shared_ptr<T>*) duk_require_pointer(ctx, -1); 205 duk_pop(ctx); // pop shared_ptr ptr 206 207 if (ptr != NULL) { 208 delete ptr; 209 210 // for safety, set the pointer to undefined 211 // (finalizers can run multiple times) 212 duk_push_undefined(ctx); 213 duk_put_prop_string(ctx, 0, "\xFF" "shared_ptr"); 214 } 215 216 return 0; 217 } 218 219 template <typename FullT> 220 static void push(duk_context* ctx, const std::shared_ptr<T>& value) { 221 if (value == nullptr) { 222 duk_push_null(ctx); 223 } else { 224 dukglue::detail::ProtoManager::make_script_object(ctx, value.get()); 225 226 // create + set shared_ptr 227 duk_push_pointer(ctx, new std::shared_ptr<T>(value)); 228 duk_put_prop_string(ctx, -2, "\xFF" "shared_ptr"); 229 230 // set shared_ptr finalizer 231 duk_push_c_function(ctx, &shared_ptr_finalizer, 1); 232 duk_set_finalizer(ctx, -2); 233 } 234 } 235 }; 236 237 // std::function 238 /*template <typename RetT, typename... ArgTs> 239 struct DukType< std::function<RetT(ArgTs...)> > { 240 typedef std::true_type IsValueType; 241 242 template<typename FullT> 243 static std::function<RetT(ArgTs...)> read(duk_context* ctx, duk_idx_t arg_idx) { 244 DukValue callable = DukValue::copy_from_stack(ctx, -1, DUK_TYPE_MASK_OBJECT); 245 return [ctx, callable] (ArgTs... args) -> RetT { 246 dukglue_call<RetT>(ctx, callable, args...); 247 }; 248 } 249 250 template<typename FullT> 251 static void push(duk_context* ctx, std::function<RetT(ArgTs...)> value) { 252 static_assert(false, "Pushing an std::function has not been implemented yet. Sorry!"); 253 } 254 };*/ 255 } 256 } 257