# HG changeset patch # User David Demelier # Date 1444681107 -7200 # Node ID bb550fbd85e9bc701955e545a8fe40ea8c447e7b # Parent 0dd7d9dd554a3436f3a3aa137b53a5dfb4064c50 Js: use Shared and Pointer instead of TypeInfoShared/TypeInfoPointer diff -r 0dd7d9dd554a -r bb550fbd85e9 C++/modules/Js/Js.h --- 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 +class Shared { +public: + /** + * The shared object. + */ + std::shared_ptr 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 +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 and inherits from this class to implement constrct, push and get function automatically. + * @brief Implementation of managed shared_ptr + * @see Shared */ template -class TypeInfoShared { +class TypeInfo> { private: - static void apply(Context &ctx, std::shared_ptr value); + static void apply(Context &ctx, std::shared_ptr value) + { + duk_push_boolean(ctx, false); + duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); + duk_push_pointer(ctx, new std::shared_ptr(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 *>(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 value); + static void construct(Context &ctx, Shared 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 value); + static void push(Context &ctx, Shared 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 get(Context &ctx, int index); -}; - -template -void TypeInfoShared::apply(Context &ctx, std::shared_ptr value) -{ - duk_push_boolean(ctx, false); - duk_put_prop_string(ctx, -2, "\xff""\xff""js-deleted"); - duk_push_pointer(ctx, new std::shared_ptr(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 *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - } - + static std::shared_ptr get(Context &ctx, int index) + { + duk_get_prop_string(ctx, index, "\xff""\xff""js-shared-ptr"); + std::shared_ptr value = *static_cast *>(duk_to_pointer(ctx, -1)); duk_pop(ctx); - return 0; - }, 1); - duk_set_finalizer(ctx, -2); -} - -template -void TypeInfoShared::construct(Context &ctx, std::shared_ptr value) -{ - duk_push_this(ctx); - apply(ctx, std::move(value)); - duk_pop(ctx); -} - -template -void TypeInfoShared::push(Context &ctx, std::shared_ptr value) -{ - duk_push_object(ctx); - apply(ctx, std::move(value)); - TypeInfo>::prototype(ctx); - duk_set_prototype(ctx, -2); -} - -template -std::shared_ptr TypeInfoShared::get(Context &ctx, int index) -{ - duk_get_prop_string(ctx, index, "\xff""\xff""js-shared-ptr"); - std::shared_ptr value = *static_cast *>(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 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 -class TypeInfoPointer { +class TypeInfo> { 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(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 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 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 -void TypeInfoPointer::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(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(duk_to_pointer(ctx, -1)); duk_pop(ctx); - return 0; - }, 1); - duk_set_finalizer(ctx, -2); -} - -template -void TypeInfoPointer::construct(Context &ctx, T *value) -{ - duk_push_this(ctx); - apply(ctx, value); - duk_pop(ctx); -} - -template -void TypeInfoPointer::push(Context &ctx, T *value) -{ - duk_push_object(ctx); - apply(ctx, value); - TypeInfo::prototype(ctx); - duk_set_prototype(ctx, -2); -} - -template -T *TypeInfoPointer::get(Context &ctx, int index) -{ - duk_get_prop_string(ctx, index, "\xff""\xff""js-ptr"); - T *value = static_cast(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return value; -} + return value; + } +}; } // !js diff -r 0dd7d9dd554a -r bb550fbd85e9 C++/tests/Js/main.cpp --- 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> : public TypeInfoShared { -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>()->m_updated = true; + ctx.self>()->m_updated = true; return 0; }}); } + + bool m_updated{false}; }; -template <> -class TypeInfo : public TypeInfoPointer { -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()->m_updated = true; - - return 0; - }}); - } -}; - -} // !js - TEST(Shared, simple) { Context ctx; - std::shared_ptr p{new Player}; + std::shared_ptr p{new Dog}; - ctx.push(p); - std::shared_ptr p2 = ctx.get>(-1); + ctx.push(Shared{p}); + std::shared_ptr p2 = ctx.get>(-1); p2->m_updated = true; @@ -704,10 +679,10 @@ TEST(Shared, self) { Context ctx; - std::shared_ptr p{new Player}; + std::shared_ptr p{new Dog}; try { - ctx.putGlobal("player", p); + ctx.putGlobal("player", Shared{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>()->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(-1); + context.push(Pointer{p}); + Cat *p2 = context.get>(-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{p}); ctx.peval(Script{"player.update();"}); ASSERT_TRUE(p->m_updated);