Mercurial > code
changeset 425:bb550fbd85e9
Js: use Shared<T> and Pointer<T> instead of TypeInfoShared/TypeInfoPointer
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 12 Oct 2015 22:18:27 +0200 |
parents | 0dd7d9dd554a |
children | cee5c74c1c83 |
files | C++/modules/Js/Js.h C++/tests/Js/main.cpp |
diffstat | 2 files changed, 185 insertions(+), 181 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/modules/Js/Js.h Mon Oct 12 22:00:03 2015 +0200 +++ b/C++/modules/Js/Js.h Mon Oct 12 22:18:27 2015 +0200 @@ -126,6 +126,40 @@ */ /** + * @brief Manage shared_ptr from C++ and JavaScript + * + * This class allowed you to push and retrieve shared_ptr from C++ and JavaScript without taking care of ownership + * and deletion. + * + * The only requirement is to have the function `void prototype(Context &ctx)` in your class T. + */ +template <typename T> +class Shared { +public: + /** + * The shared object. + */ + std::shared_ptr<T> object; +}; + +/** + * @brief Manage pointers from C++ and JavaScript + * + * This class allowed you to push and retrieve C++ pointers from C++ and JavaScript. The object will be deleted when + * the JavaScript garbage collectors collect them so never store a pointer created with this. + * + * The only requirement is to have the function `void prototype(Context &ctx)` in your class T. + */ +template <typename T> +class Pointer { +public: + /** + * The object. + */ + T *object{nullptr}; +}; + +/** * @class Function * @brief Duktape/C function definition. * @@ -593,7 +627,6 @@ /** * Call in protected mode the object at the top of the stack. * - * @param ctx the context * @param nargs the number of arguments * @throw ErrorInfo on errors * @note Wrapper of duk_pcall @@ -988,6 +1021,7 @@ /** * Constructor with a type of error specified, specially designed for derived errors. * + * @param name the error name (e.g RangeError) * @param message the message */ inline Error(std::string name, std::string message) noexcept @@ -1039,7 +1073,9 @@ class EvalError : public Error { public: /** - * @copydoc Error + * Construct an EvalError. + * + * @param message the message */ inline EvalError(std::string message) noexcept : Error{"EvalError", std::move(message)} @@ -1054,7 +1090,9 @@ class RangeError : public Error { public: /** - * @copydoc Error + * Construct an RangeError. + * + * @param message the message */ inline RangeError(std::string message) noexcept : Error{"RangeError", std::move(message)} @@ -1069,7 +1107,9 @@ class ReferenceError : public Error { public: /** - * @copydoc Error + * Construct an ReferenceError. + * + * @param message the message */ inline ReferenceError(std::string message) noexcept : Error{"ReferenceError", std::move(message)} @@ -1084,7 +1124,9 @@ class SyntaxError : public Error { public: /** - * @copydoc Error + * Construct an SyntaxError. + * + * @param message the message */ inline SyntaxError(std::string message) noexcept : Error{"SyntaxError", std::move(message)} @@ -1099,7 +1141,9 @@ class TypeError : public Error { public: /** - * @copydoc Error + * Construct an TypeError. + * + * @param message the message */ inline TypeError(std::string message) noexcept : Error{"TypeError", std::move(message)} @@ -1114,7 +1158,9 @@ class URIError : public Error { public: /** - * @copydoc Error + * Construct an URIError. + * + * @param message the message */ inline URIError(std::string message) noexcept : Error{"URIError", std::move(message)} @@ -1863,20 +1909,36 @@ } }; -/* ------------------------------------------------------------------ - * Helpers for pointers and std::shared_ptr - * ------------------------------------------------------------------ */ - /** - * @class TypeInfoShared - * @brief Push objects as shared_ptr. - * - * Overload TypeInfo<T *> and inherits from this class to implement constrct, push and get function automatically. + * @brief Implementation of managed shared_ptr + * @see Shared */ template <typename T> -class TypeInfoShared { +class TypeInfo<Shared<T>> { private: - static void apply(Context &ctx, std::shared_ptr<T> value); + static void apply(Context &ctx, std::shared_ptr<T> value) + { + 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"); + duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { + duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); + + if (duk_to_boolean(ctx, -1)) { + duk_push_boolean(ctx, true); + duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); + duk_get_prop_string(ctx, 0, "\xff""\xff""js-shared-ptr"); + delete static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + } + + duk_pop(ctx); + + return 0; + }, 1); + duk_set_finalizer(ctx, -2); + } public: /** @@ -1885,7 +1947,12 @@ * @param ctx the context * @param value the value */ - static void construct(Context &ctx, std::shared_ptr<T> value); + static void construct(Context &ctx, Shared<T> value) + { + duk_push_this(ctx); + apply(ctx, std::move(value.object)); + duk_pop(ctx); + } /** * Push a managed shared_ptr as object. @@ -1893,7 +1960,13 @@ * @param ctx the context * @param value the value */ - static void push(Context &ctx, std::shared_ptr<T> value); + static void push(Context &ctx, Shared<T> value) + { + duk_push_object(ctx); + apply(ctx, value.object); + value.object->prototype(ctx); + duk_set_prototype(ctx, -2); + } /** * Get a managed shared_ptr from the stack. @@ -1902,78 +1975,46 @@ * @param index the object index * @return the shared_ptr */ - static std::shared_ptr<T> get(Context &ctx, int index); -}; - -template <typename T> -void TypeInfoShared<T>::apply(Context &ctx, std::shared_ptr<T> value) -{ - 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"); - duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { - duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - - if (duk_to_boolean(ctx, -1)) { - duk_push_boolean(ctx, true); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - duk_get_prop_string(ctx, 0, "\xff""\xff""js-shared-ptr"); - delete static_cast<std::shared_ptr<T> *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - } - + static std::shared_ptr<T> get(Context &ctx, int 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 0; - }, 1); - duk_set_finalizer(ctx, -2); -} - -template <typename T> -void TypeInfoShared<T>::construct(Context &ctx, std::shared_ptr<T> value) -{ - duk_push_this(ctx); - apply(ctx, std::move(value)); - duk_pop(ctx); -} - -template <typename T> -void TypeInfoShared<T>::push(Context &ctx, std::shared_ptr<T> value) -{ - duk_push_object(ctx); - apply(ctx, std::move(value)); - TypeInfo<std::shared_ptr<T>>::prototype(ctx); - duk_set_prototype(ctx, -2); -} - -template <typename T> -std::shared_ptr<T> TypeInfoShared<T>::get(Context &ctx, int 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; -} - -/* ------------------------------------------------------------------ - * Helpers for pointer - * ------------------------------------------------------------------ */ + return value; + } +}; /** - * @class TypeInfoPointer - * @brief Push managed pointers as objects. - * - * Overload TypeInfo<T *> and inherits from this class to implement constrct, push and get function automatically, - * the pointer is constructed and managed into the Duktape context. It will be deleted when the Duktape garbage - * collectors collects the value. It is unsafe to keep a pointer to the object from the C++ side, if you need - * to share values, look at TypeInfoShared. + * @brief Implementation of managed pointers + * @see Pointer */ template <typename T> -class TypeInfoPointer { +class TypeInfo<Pointer<T>> { private: - static void apply(Context &ctx, T *value); + static void apply(Context &ctx, T *value) + { + duk_push_boolean(ctx, false); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); + duk_push_pointer(ctx, value); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-ptr"); + duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { + duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); + + if (duk_to_boolean(ctx, -1)) { + duk_push_boolean(ctx, true); + duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); + duk_get_prop_string(ctx, 0, "\xff""\xff""js-ptr"); + delete static_cast<T *>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + } + + duk_pop(ctx); + + return 0; + }, 1); + duk_set_finalizer(ctx, -2); + } public: /** @@ -1982,7 +2023,12 @@ * @param ctx the context * @param value the value */ - static void construct(Context &ctx, T *value); + static void construct(Context &ctx, Pointer<T> value) + { + duk_push_this(ctx); + apply(ctx, value.object); + duk_pop(ctx); + } /** * Push a managed pointer as object. @@ -1990,7 +2036,13 @@ * @param ctx the context * @param value the value */ - static void push(Context &ctx, T *value); + static void push(Context &ctx, Pointer<T> value) + { + duk_push_object(ctx); + apply(ctx, value.object); + value.object->prototype(ctx); + duk_set_prototype(ctx, -2); + } /** * Get a managed pointer from the stack. @@ -2000,60 +2052,15 @@ * @return the pointer * @warning Do not store the pointer into the C++ side, the object can be deleted at any time */ - static T *get(Context &ctx, int index); -}; - -template <typename T> -void TypeInfoPointer<T>::apply(Context &ctx, T *value) -{ - duk_push_boolean(ctx, false); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); - duk_push_pointer(ctx, value); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-ptr"); - duk_push_c_function(ctx, [] (duk_context *ctx) -> duk_ret_t { - duk_get_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - - if (duk_to_boolean(ctx, -1)) { - duk_push_boolean(ctx, true); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-deleted"); - duk_get_prop_string(ctx, 0, "\xff""\xff""js-ptr"); - delete static_cast<T *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - } - + static T *get(Context &ctx, int index) + { + duk_get_prop_string(ctx, index, "\xff""\xff""js-ptr"); + T *value = static_cast<T *>(duk_to_pointer(ctx, -1)); duk_pop(ctx); - return 0; - }, 1); - duk_set_finalizer(ctx, -2); -} - -template <typename T> -void TypeInfoPointer<T>::construct(Context &ctx, T *value) -{ - duk_push_this(ctx); - apply(ctx, value); - duk_pop(ctx); -} - -template <typename T> -void TypeInfoPointer<T>::push(Context &ctx, T *value) -{ - duk_push_object(ctx); - apply(ctx, value); - TypeInfo<T *>::prototype(ctx); - duk_set_prototype(ctx, -2); -} - -template <typename T> -T *TypeInfoPointer<T>::get(Context &ctx, int index) -{ - duk_get_prop_string(ctx, index, "\xff""\xff""js-ptr"); - T *value = static_cast<T *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return value; -} + return value; + } +}; } // !js
--- a/C++/tests/Js/main.cpp Mon Oct 12 22:00:03 2015 +0200 +++ b/C++/tests/Js/main.cpp Mon Oct 12 22:18:27 2015 +0200 @@ -642,59 +642,34 @@ } /* -------------------------------------------------------- - * TypeInfo with shared and RawPointer + * Managed shared_ptr * -------------------------------------------------------- */ -class Player { +class Dog { public: - bool m_updated{false}; -}; - -namespace js { - -template <> -class TypeInfo<std::shared_ptr<Player>> : public TypeInfoShared<Player> { -public: - static void prototype(Context &ctx) + void prototype(Context &ctx) { /* Create a temporary for the test */ ctx.push(Object{}); /* "update" method */ ctx.putProperty(-1, "update", Function{[] (Context &ctx) -> int { - ctx.self<std::shared_ptr<Player>>()->m_updated = true; + ctx.self<Shared<Dog>>()->m_updated = true; return 0; }}); } + + bool m_updated{false}; }; -template <> -class TypeInfo<Player *> : public TypeInfoPointer<Player> { -public: - static void prototype(Context &ctx) - { - /* Create a temporary for the test */ - ctx.push(Object{}); - - /* "update" method */ - ctx.putProperty(-1, "update", Function{[&] (Context &ctx) -> int { - ctx.self<Player *>()->m_updated = true; - - return 0; - }}); - } -}; - -} // !js - TEST(Shared, simple) { Context ctx; - std::shared_ptr<Player> p{new Player}; + std::shared_ptr<Dog> p{new Dog}; - ctx.push(p); - std::shared_ptr<Player> p2 = ctx.get<std::shared_ptr<Player>>(-1); + ctx.push(Shared<Dog>{p}); + std::shared_ptr<Dog> p2 = ctx.get<Shared<Dog>>(-1); p2->m_updated = true; @@ -704,10 +679,10 @@ TEST(Shared, self) { Context ctx; - std::shared_ptr<Player> p{new Player}; + std::shared_ptr<Dog> p{new Dog}; try { - ctx.putGlobal("player", p); + ctx.putGlobal("player", Shared<Dog>{p}); ctx.peval(Script{"player.update();"}); ASSERT_TRUE(p->m_updated); @@ -716,14 +691,36 @@ } } -TEST(RawPointer, simple) +/* -------------------------------------------------------- + * Managed Pointer + * -------------------------------------------------------- */ + +class Cat { +public: + void prototype(Context &ctx) + { + /* Create a temporary for the test */ + ctx.push(Object{}); + + /* "update" method */ + ctx.putProperty(-1, "update", Function{[] (Context &ctx) -> int { + ctx.self<Pointer<Cat>>()->m_updated = true; + + return 0; + }}); + } + + bool m_updated{false}; +}; + +TEST(Pointer, simple) { Context context; - Player *p{new Player}; + Cat *p{new Cat}; ASSERT_EQ(0, context.top()); - context.push(p); - Player *p2 = context.get<Player *>(-1); + context.push(Pointer<Cat>{p}); + Cat *p2 = context.get<Pointer<Cat>>(-1); ASSERT_EQ(1, context.top()); p2->m_updated = true; @@ -731,13 +728,13 @@ ASSERT_TRUE(p->m_updated); } -TEST(RawPointer, self) +TEST(Pointer, self) { Context ctx; - Player *p{new Player}; + Cat *p{new Cat}; try { - ctx.putGlobal("player", p); + ctx.putGlobal("player", Pointer<Cat>{p}); ctx.peval(Script{"player.update();"}); ASSERT_TRUE(p->m_updated);