changeset 401:ca5e4360f79a

Js: - Add support of constants map (js::Map<T>) - Add supports for vectors (std::vector<T>) - Add index based getProperty/putProperty
author David Demelier <markand@malikania.fr>
date Sat, 03 Oct 2015 11:27:49 +0200
parents c118df15d354
children c21f37c9cd4a
files C++/modules/Js/Js.cpp C++/modules/Js/Js.h C++/tests/Js/main.cpp
diffstat 3 files changed, 139 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/C++/modules/Js/Js.cpp	Thu Oct 01 21:30:57 2015 +0200
+++ b/C++/modules/Js/Js.cpp	Sat Oct 03 11:27:49 2015 +0200
@@ -1,7 +1,7 @@
 /*
- * Js.cpp -- JS API for irccd and Duktape helpers
+ * Js.cpp -- JavaScript wrapper for Duktape
  *
- * Copyright (c) 2013, 2014, 2015 David Demelier <markand@malikania.fr>
+ * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
--- a/C++/modules/Js/Js.h	Thu Oct 01 21:30:57 2015 +0200
+++ b/C++/modules/Js/Js.h	Sat Oct 03 11:27:49 2015 +0200
@@ -1,7 +1,7 @@
 /*
  * Js.h -- JavaScript wrapper for Duktape
  *
- * Copyright (c) 2013, 2014, 2015 David Demelier <markand@malikania.fr>
+ * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -35,6 +35,7 @@
 #include <string>
 #include <type_traits>
 #include <unordered_map>
+#include <vector>
 
 #include <duktape.h>
 
@@ -105,6 +106,12 @@
 using FunctionMap = std::unordered_map<std::string, Function>;
 
 /**
+ * Map of string to type constant.
+ */
+template <typename Type>
+using Map = std::unordered_map<std::string, Type>;
+
+/**
  * @class ErrorInfo
  * @brief Error description.
  *
@@ -310,7 +317,7 @@
 	// TODO: add optional
 
 	/* --------------------------------------------------------
-	 * Get properties (for objects)
+	 * Get properties (for objects and arrays)
 	 * -------------------------------------------------------- */
 
 	/**
@@ -331,6 +338,16 @@
 		return value;
 	}
 
+	template <typename Type>
+	inline auto getProperty(int index, int position, std::enable_if_t<!std::is_void<Type>::value> * = nullptr) -> decltype(get<Type>(0))
+	{
+		duk_get_prop_index(m_handle.get(), index, position);
+		decltype(get<Type>(0)) value = get<Type>(-1);
+		duk_pop(m_handle.get());
+
+		return value;
+	}
+
 	/**
 	 * Get the property `name' and push it to the stack from the object at the specified index.
 	 *
@@ -344,6 +361,12 @@
 		duk_get_prop_string(m_handle.get(), index, name.c_str());
 	}
 
+	template <typename Type>
+	inline void getProperty(int index, int position, std::enable_if_t<std::is_void<Type>::value> * = nullptr)
+	{
+		duk_get_prop_index(m_handle.get(), index, position);
+	}
+
 	/* --------------------------------------------------------
 	 * Put properties functions (for object)
 	 * -------------------------------------------------------- */
@@ -357,7 +380,7 @@
 	 * @note The stack is unchanged
 	 */
 	template <typename Type>
-	void putProperty(int index, const std::string &name, Type&& value)
+	void putProperty(int index, const std::string &name, Type &&value)
 	{
 		index = duk_normalize_index(m_handle.get(), index);
 
@@ -365,6 +388,15 @@
 		duk_put_prop_string(m_handle.get(), index, name.c_str());
 	}
 
+	template <typename Type>
+	void putProperty(int index, int position, Type &&value)
+	{
+		index = duk_normalize_index(m_handle.get(), index);
+
+		push(std::forward<Type>(value));
+		duk_put_prop_index(m_handle.get(), index, position);
+	}
+
 	/**
 	 * Put the value that is at the top of the stack as property to the object.
 	 *
@@ -376,6 +408,10 @@
 		duk_put_prop_string(m_handle.get(), index, name.c_str());
 	}
 
+	inline void putProperty(int index, int position)
+	{
+		duk_put_prop_index(m_handle.get(), index, position);
+	}
 
 	/* --------------------------------------------------------
 	 * Basic functions
@@ -1074,6 +1110,66 @@
 	}
 };
 
+/**
+ * @class TypeInfo<Map<T>>
+ * @brief Push a map of key-value pair as objects.
+ *
+ * Provides: push
+ *
+ * This class is convenient for settings constants such as enums, string and such.
+ */
+template <typename T>
+class TypeInfo<Map<T>> {
+public:
+	static void push(Context &ctx, const Map<T> &map)
+	{
+		duk_push_object(ctx);
+
+		for (const auto &pair : map) {
+			TypeInfo<T>::push(ctx, pair.second);
+			duk_put_prop_string(ctx, -2, pair.first.c_str());
+		}
+	}
+};
+
+/**
+ * @class TypeInfo<std::vector<T>>
+ * @brief Push or get vectors as JavaScript arrays.
+ *
+ * Provides: push
+ */
+template <typename T>
+class TypeInfo<std::vector<T>> {
+public:
+	static void push(Context &ctx, const std::vector<T> &array)
+	{
+		duk_push_array(ctx);
+
+		int i = 0;
+		for (const auto &v : array) {
+			TypeInfo<T>::push(ctx, v);
+			duk_put_prop_index(ctx, -2, i++);
+		}
+	}
+
+	static std::vector<T> get(Context &ctx, int index)
+	{
+		std::vector<T> result;
+
+		if (!duk_is_array(ctx, -1)) {
+			return result;
+		}
+
+		/* XXX: something else than length property? */
+		int total = ctx.getProperty<int>(index, "length");
+		for (int i = 0; i < total; ++i) {
+			result.push_back(ctx.getProperty<T>(index, i));
+		}
+
+		return result;
+	}
+};
+
 /* ------------------------------------------------------------------
  * Helpers for pointers and std::shared_ptr
  * ------------------------------------------------------------------ */
--- a/C++/tests/Js/main.cpp	Thu Oct 01 21:30:57 2015 +0200
+++ b/C++/tests/Js/main.cpp	Sat Oct 03 11:27:49 2015 +0200
@@ -80,6 +80,36 @@
 	ASSERT_EQ(DUK_TYPE_NULL, context.type(-1));
 }
 
+TEST(PushAndGet, map)
+{
+	Context context;
+	Map<int> map{
+		{ "one",	1 },
+		{ "two",	2 }
+	};
+
+	context.push(map);
+
+	ASSERT_EQ(DUK_TYPE_OBJECT, context.type(-1));
+	ASSERT_EQ(1, context.getProperty<int>(-1, "one"));
+	ASSERT_EQ(2, context.getProperty<int>(-1, "two"));
+}
+
+TEST(PushAndGet, vector)
+{
+	Context context;
+
+	context.push(std::vector<int>{1, 2, 3});
+
+	ASSERT_EQ(DUK_TYPE_OBJECT, context.type(-1));
+
+	auto array = context.get<std::vector<int>>(-1);
+	ASSERT_EQ(3U, array.size());
+	ASSERT_EQ(1, array[0]);
+	ASSERT_EQ(2, array[1]);
+	ASSERT_EQ(3, array[2]);
+}
+
 /* --------------------------------------------------------
  * Is
  * -------------------------------------------------------- */
@@ -165,11 +195,11 @@
 	Context context;
 
 	// boolean
-	context.setGlobal("valueBoolean", true);
+	context.putGlobal("valueBoolean", true);
 	ASSERT_TRUE(context.getGlobal<bool>("valueBoolean"));
 
 	// integer
-	context.setGlobal("valueInteger", 123);
+	context.putGlobal("valueInteger", 123);
 	ASSERT_EQ(123, context.getGlobal<int>("valueInteger"));
 }
 
@@ -235,7 +265,7 @@
 	Context context;
 
 	context.push(Function{[] (Context &ctx) -> int {
-		ctx.setGlobal("x", 123);
+		ctx.putGlobal("x", 123);
 
 		return 0;
 	}});
@@ -268,8 +298,8 @@
 {
 	Context context;
 
-	context.setGlobal("f", Function{[] (Context &ctx) -> int {
-		ctx.setGlobal("x", 123);
+	context.putGlobal("f", Function{[] (Context &ctx) -> int {
+		ctx.putGlobal("x", 123);
 
 		return 0;
 	}});
@@ -313,7 +343,7 @@
 {
 	Context context;
 
-	context.setGlobal("f", Function{[] (Context &ctx) -> duk_idx_t {
+	context.putGlobal("f", Function{[] (Context &ctx) -> duk_idx_t {
 		ctx.raise(Error{"error thrown"});
 
 		return 0;
@@ -399,7 +429,7 @@
 	std::shared_ptr<Player> p{new Player};
 
 	try {
-		ctx.setGlobal("player", p);
+		ctx.putGlobal("player", p);
 		ctx.peval(Script{"player.update();"});
 
 		ASSERT_TRUE(p->m_updated);
@@ -427,7 +457,7 @@
 	Player *p{new Player};
 
 	try {
-		ctx.setGlobal("player", p);
+		ctx.putGlobal("player", p);
 		ctx.peval(Script{"player.update();"});
 
 		ASSERT_TRUE(p->m_updated);