Mercurial > code
changeset 397:6b2db5425836
Js: initial support for objects as shared_ptr
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 29 Sep 2015 12:50:28 +0200 |
parents | 30788c97c58c |
children | 94bfe7ba9a13 |
files | C++/modules/Js/Js.h C++/tests/Js/main.cpp |
diffstat | 2 files changed, 145 insertions(+), 221 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/modules/Js/Js.h Mon Sep 28 19:40:26 2015 +0200 +++ b/C++/modules/Js/Js.h Tue Sep 29 12:50:28 2015 +0200 @@ -149,17 +149,24 @@ public: + /** + * Create default context. + */ inline Context() : m_handle{duk_create_heap_default(), duk_destroy_heap} { } + /** + * Create borrowed context that will not be deleted. + * + * @param ctx the pointer to duk_context + */ inline Context(ContextPtr ctx) noexcept : m_handle{ctx, [] (ContextPtr) {}} { } -#if 0 /** * Convert the context to the native Duktape/C type. * @@ -167,7 +174,7 @@ */ inline operator duk_context *() noexcept { - return get(); + return m_handle.get(); } /** @@ -177,156 +184,22 @@ */ inline operator duk_context *() const noexcept { - return get(); - } -#endif - - /* -------------------------------------------------------- - * Push functions - * -------------------------------------------------------- */ - -#if 0 - - /** - * Push a boolean. - * - * @param ctx the context - * @param value the boolean value - */ - inline void push(bool value) - { - duk_push_boolean(m_handle.get(), value); - } - - /** - * Push an integer. - * - * @param ctx the context - * @param value the integer value - */ - inline void push(int value) - { - duk_push_int(m_handle.get(), value); - } - - /** - * Push a real value. - * - * @param ctx the context - * @param value the value - */ - void push(double value) - { - duk_push_number(m_handle.get(), value); - } - - /** - * Push a string. - * - * @param ctx the context - * @param value the string - */ - void push(const std::string &value) - { - duk_push_lstring(m_handle.get(), value.c_str(), value.length()); - } - - /** - * Push a literal or C-string. - * - * @param ctx the context - * @param value the value - */ - void push(const char *value) - { - duk_push_string(m_handle.get(), value); + return m_handle.get(); } - /** - * Push a Duktape/C function. - * - * @param ctx the context - * @param function the function - */ - void push(const Function &fn) - { - assert(fn.function); - - duk_push_c_function(m_handle.get(), fn.function, fn.nargs); - } - - /** - * Push an empty object on the stack. - * - * @param ctx the context - * @param object the empty object - */ - void push(const Object &) - { - duk_push_object(m_handle.get()); - } - - /** - * Push an empty array at the top of the stack. - * - * @param ctx the context - * @param array the empty array - */ - void push(const Array &) - { - duk_push_array(m_handle.get()); - } - - /* -------------------------------------------------------- - * Get functions - * -------------------------------------------------------- */ - - /** - * Get a boolean. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ - void get(ContextPtr ctx, duk_idx_t index, bool &value); - - /** - * Get an integer. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ - void get(ContextPtr ctx, duk_idx_t index, int &value); - - /** - * Get a real. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ - void get(ContextPtr ctx, duk_idx_t index, double &value); - - /** - * Get a string. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ - void get(ContextPtr ctx, duk_idx_t index, std::string &value); - -#endif - /* ---------------------------------------------------------- * Push / Get / Require * ---------------------------------------------------------- */ + /** + * Push a value into the stack. Calls TypeInfo<T>::push(*this, value); + * + * @param value the value to forward + */ template <typename Type> inline void push(Type &&value) { - TypeInfo<std::decay_t<Type>>::push(m_handle.get(), std::forward<Type>(value)); + TypeInfo<std::decay_t<Type>>::push(*this, std::forward<Type>(value)); } /** @@ -336,15 +209,15 @@ * @return the value */ template <typename Type> - inline Type get(duk_idx_t index) + inline auto get(duk_idx_t index) -> decltype(TypeInfo<Type>::get(*this, 0)) { - return TypeInfo<Type>::get(m_handle.get(), index); + return TypeInfo<Type>::get(*this, index); } template <typename Type> - inline Type require(duk_idx_t index) + inline auto require(duk_idx_t index) -> decltype(TypeInfo<Type>::require(*this, 0)) { - return TypeInfo<Type>::require(m_handle.get(), index); + return TypeInfo<Type>::require(*this, index); } /* -------------------------------------------------------- @@ -352,11 +225,11 @@ * -------------------------------------------------------- */ template <typename Type> - inline Type getObject(duk_idx_t index, const std::string &name) + inline auto getObject(duk_idx_t index, const std::string &name) -> decltype(get<Type>(0)) { assertBegin(m_handle.get()); duk_get_prop_string(m_handle.get(), index, name.c_str()); - Type value = get<Type>(-1); + auto &&value = get<Type>(-1); duk_pop(m_handle.get()); assertEquals(m_handle.get()); @@ -379,66 +252,6 @@ } /* -------------------------------------------------------- - * Require functions - * -------------------------------------------------------- */ - -#if 0 - - /** - * Requires a value or throw an exception if not a boolean. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ - void require(ContextPtr ctx, duk_idx_t index, bool &value); - - /** - * Requires a value or throw an exception if not an integer. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ - void require(ContextPtr ctx, duk_idx_t index, int &value); - - /** - * Requires a value or throw an exception if not a double. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store the value - */ - void require(ContextPtr ctx, duk_idx_t index, double &value); - - /** - * Requires a value or throw an exception if not a string. - * - * @param ctx the context - * @param index the index - * @param value the reference where to store string - */ - void require(ContextPtr ctx, duk_idx_t index, std::string &value); - - /** - * Generic function to require a value from the stack. - * - * @param ctx the context - * @param index the specified index - * @return the value - */ - template <typename Type> - inline Type require(ContextPtr ctx, duk_idx_t index) - { - Type value; - - require(ctx, index, value); - - return value; - } -#endif - - /* -------------------------------------------------------- * Basic functions * -------------------------------------------------------- */ @@ -589,11 +402,11 @@ * @return the value */ template <typename Type> - inline Type getGlobal(const std::string &name) + inline auto getGlobal(const std::string &name) -> decltype(get<Type>(0)) { assertBegin(m_handle.get()); duk_get_global_string(m_handle.get(), name.c_str()); - Type value = get<Type>(-1); + auto &&value = get<Type>(-1); duk_pop(m_handle.get()); assertEquals(m_handle.get()); @@ -631,6 +444,16 @@ duk_put_prop_string(m_handle.get(), -2, "name"); duk_throw(m_handle.get()); } + + template <typename T> + inline auto self() -> decltype(TypeInfo<T>::get(*this, 0)) + { + duk_push_this(m_handle.get()); + auto &&value = TypeInfo<T>::get(*this, -1); + duk_pop(m_handle.get()); + + return value; + } }; /* ------------------------------------------------------------------ @@ -756,12 +579,12 @@ template <> class TypeInfo<bool> { public: - static bool get(ContextPtr ctx, duk_idx_t index) + static bool get(Context &ctx, duk_idx_t index) { return duk_get_boolean(ctx, index); } - static void push(ContextPtr ctx, bool value) + static void push(Context &ctx, bool value) { duk_push_boolean(ctx, value); } @@ -770,12 +593,12 @@ template <> class TypeInfo<double> { public: - static double get(ContextPtr ctx, duk_idx_t index) + static double get(Context &ctx, duk_idx_t index) { return duk_get_number(ctx, index); } - static void push(ContextPtr ctx, double value) + static void push(Context &ctx, double value) { duk_push_number(ctx, value); } @@ -784,7 +607,7 @@ template <> class TypeInfo<std::string> { public: - static std::string get(ContextPtr ctx, duk_idx_t index) + static std::string get(Context &ctx, duk_idx_t index) { duk_size_t size; const char *text = duk_get_lstring(ctx, index, &size); @@ -792,7 +615,7 @@ return std::string{text, size}; } - static void push(ContextPtr ctx, const std::string &value) + static void push(Context &ctx, const std::string &value) { duk_push_lstring(ctx, value.c_str(), value.length()); } @@ -801,7 +624,7 @@ template <> class TypeInfo<const char *> { public: - static void push(ContextPtr ctx, const char *value) + static void push(Context &ctx, const char *value) { duk_push_string(ctx, value); } @@ -810,7 +633,7 @@ template <> class TypeInfo<Function> { public: - static void push(ContextPtr ctx, const Function &fn) + static void push(Context &ctx, const Function &fn) { assert(fn.function); @@ -821,7 +644,7 @@ template <> class TypeInfo<Object> { public: - static void push(ContextPtr ctx, const Object &) + static void push(Context &ctx, const Object &) { duk_push_object(ctx); } @@ -830,12 +653,59 @@ template <> class TypeInfo<Array> { public: - static void push(ContextPtr ctx, const Array &) + static void push(Context &ctx, const Array &) { duk_push_array(ctx); } }; +/* ------------------------------------------------------------------ + * Helpers for pointers and std::shared_ptr + * ------------------------------------------------------------------ */ + +/** + * @class TypeInfoShared + * @brief Generates push / get / require for std::shared_ptr<T> + * + * Specialize TypeInfo<std::shared_ptr<T>> and inherits from this class to implement the push(), + * get() and require() functions. + * + * You only need to implement `static void prototype(Context &ctx)` which must push the prototype + * to use for the underlying object. + */ +template <typename T> +class TypeInfoShared { +public: + static void push(Context &ctx, std::shared_ptr<T> value); + static std::shared_ptr<T> get(Context &ctx, duk_idx_t index); +}; + +template <typename T> +void TypeInfoShared<T>::push(Context &ctx, std::shared_ptr<T> value) +{ + duk_push_object(ctx); + duk_push_boolean(ctx, false); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); + duk_push_pointer(ctx, new std::shared_ptr<T>(value)); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-shared-ptr"); + + TypeInfo<std::shared_ptr<T>>::prototype(ctx); + + // TODO: set deleter + + duk_set_prototype(ctx, -2); +} + +template <typename T> +std::shared_ptr<T> TypeInfoShared<T>::get(Context &ctx, duk_idx_t index) +{ + duk_get_prop_string(ctx, index, "\xff""\xff""js-shared-ptr"); + std::shared_ptr<T> value = *static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return value; +} + } // !js #endif // !_JS_H_
--- a/C++/tests/Js/main.cpp Mon Sep 28 19:40:26 2015 +0200 +++ b/C++/tests/Js/main.cpp Tue Sep 29 12:50:28 2015 +0200 @@ -247,6 +247,60 @@ ASSERT_EQ("error thrown", context.getGlobal<std::string>("message")); } +/* -------------------------------------------------------- + * TypeInfo with shared + * -------------------------------------------------------- */ + +class Player { +public: + bool m_updated{false}; +}; + +template <> +class TypeInfo<std::shared_ptr<Player>> : public TypeInfoShared<Player> { +public: + static void prototype(Context &ctx) + { + /* Create a temporary for the test */ + ctx.push(Object{}); + + /* "update" method */ + ctx.setObject(-1, "update", Function{[] (ContextPtr pctx) -> duk_ret_t { + Context{pctx}.self<std::shared_ptr<Player>>()->m_updated = true; + + return 0; + }}); + } +}; + +TEST(Shared, simple) +{ + Context ctx; + std::shared_ptr<Player> p{new Player}; + + ctx.push(p); + std::shared_ptr<Player> p2 = ctx.get<std::shared_ptr<Player>>(-1); + + p2->m_updated = true; + + ASSERT_TRUE(p->m_updated); +} + +TEST(Shared, self) +{ + Context ctx; + std::shared_ptr<Player> p{new Player}; + + try { + ctx.setGlobal("player", p); + ctx.pevalString("player.update();"); + + ASSERT_TRUE(p->m_updated); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv);