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