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);