From 0339d363f2dd50ede7e6ab05e23978ec7dfa258b Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 5 Feb 2021 08:10:14 +0100 Subject: [PATCH 01/12] Add interface for access global variables --- inkcpp/include/globals.h | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index 8262eb53..615b5c84 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -11,8 +11,41 @@ namespace ink::runtime class globals_interface { public: - // No public interface yet - virtual void dummy() = 0; + + /** + * @brief Access global variable of Ink runner. + * @param name name of variable, as defined in InkScript + * @tparam T c++ type of variable + */ + template + T* get(const char* name); + virtual ~globals_interface() = default; + + protected: + virtual uint32_t* getUInt(const char* name) = 0; + virtual int32_t* getInt(const char* name) = 0; + virtual float* getFloat(const char* name) = 0; + virtual char* getStr(const char* name) = 0; }; + + template<> + uint32_t* globals_interface::get(const char* name) { + return getUInt(name); + } + + template<> + int32_t* globals_interface::get(const char* name) { + return getInt(name); + } + + template<> + float* globals_interface::get(const char* name) { + return getFloat(name); + } + + template<> + char* globals_interface::get(const char* name) { + return getStr(name); + } } From f6b386cc000aa49ca2fa24e7cf6dac7a1372ab28 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 5 Feb 2021 10:23:11 +0100 Subject: [PATCH 02/12] Add editable global variables supported: + float + int + uint missing: + string + knodes --- inkcpp/collections/restorable.h | 48 ++++++++++++++++++------------- inkcpp/globals_impl.cpp | 33 +++++++++++++++++++++- inkcpp/globals_impl.h | 19 +++++++++---- inkcpp/include/globals.h | 33 ++++++++++++++++------ inkcpp/stack.cpp | 36 ++++++++++++++++++------ inkcpp/stack.h | 3 +- inkcpp/value.h | 3 ++ inkcpp_test/CMakeLists.txt | 9 ++++++ inkcpp_test/Globals.cpp | 50 +++++++++++++++++++++++++++++++++ inkcpp_test/ink/GlobalStory.ink | 6 ++++ 10 files changed, 197 insertions(+), 43 deletions(-) create mode 100644 inkcpp_test/Globals.cpp create mode 100644 inkcpp_test/ink/GlobalStory.ink diff --git a/inkcpp/collections/restorable.h b/inkcpp/collections/restorable.h index e3a73581..173e6b5a 100644 --- a/inkcpp/collections/restorable.h +++ b/inkcpp/collections/restorable.h @@ -287,12 +287,24 @@ namespace ink::runtime::internal // Reverse find template - const ElementType* reverse_find(Predicate predicate) const + ElementType* reverse_find(Predicate predicate) { + return reverse_find_impl(predicate); + } + + template + const ElementType* reverse_find(Predicate predicate) const { + return reverse_find_impl(predicate); + } + + template + size_t size(IsNullPredicate isNull) const { if (_pos == 0) { - return nullptr; + return 0; } + size_t count = 0; + // Start at the end size_t i = _pos; do @@ -301,8 +313,8 @@ namespace ink::runtime::internal i--; // Run callback - if (predicate(_buffer[i])) - return &_buffer[i]; + if(!isNull(_buffer[i])) + count++; // Jump over saved data if (i == _save) @@ -310,18 +322,21 @@ namespace ink::runtime::internal } while (i > 0); - return nullptr; + return count; } - template - size_t size(IsNullPredicate isNull) const + protected: + // Called when we run out of space in buffer. + virtual void overflow(ElementType*& buffer, size_t& size) { throw 0; /* TODO: What to do here? Throw something more useful? */ } + + private: + template + ElementType* reverse_find_impl(Predicate predicate) const { if (_pos == 0) { - return 0; + return nullptr; } - size_t count = 0; - // Start at the end size_t i = _pos; do @@ -330,8 +345,8 @@ namespace ink::runtime::internal i--; // Run callback - if(!isNull(_buffer[i])) - count++; + if (predicate(_buffer[i])) + return &_buffer[i]; // Jump over saved data if (i == _save) @@ -339,14 +354,9 @@ namespace ink::runtime::internal } while (i > 0); - return count; + return nullptr; } - protected: - // Called when we run out of space in buffer. - virtual void overflow(ElementType*& buffer, size_t& size) { throw 0; /* TODO: What to do here? Throw something more useful? */ } - - private: // Data buffer. Collection is stored here ElementType* _buffer; @@ -360,4 +370,4 @@ namespace ink::runtime::internal size_t _jump; size_t _save; }; -} \ No newline at end of file +} diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 034f4419..10b2cff7 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -68,6 +68,37 @@ namespace ink::runtime::internal return _variables.get(name); } + value* globals_impl::get_variable(hash_t name) { + return _variables.get(name); + } + + uint32_t* globals_impl::getUInt(hash_t name) const { + value* v = _variables.get(name); + return v && v->get_data_type() == data_type::uint32 + ? v->as_uint_ptr() + : nullptr; + } + + int32_t* globals_impl::getInt(hash_t name) const { + value* v = _variables.get(name); + return v && v->get_data_type() == data_type::int32 + ? v->as_int_ptr() + : nullptr; + } + + float* globals_impl::getFloat(hash_t name) const { + value* v = _variables.get(name); + return v && v->get_data_type() == data_type::float32 + ? v->as_float_ptr() + : nullptr; + } + + char* globals_impl::getStr(hash_t name) const { + // TODO: add string support + throw ink_exception("String handling is not supported yet!"); + } + + void globals_impl::initialize_globals(runner_impl* run) { // If no way to move there, then there are no globals. @@ -118,4 +149,4 @@ namespace ink::runtime::internal _visit_counts.forget(); _variables.forget(); } -} \ No newline at end of file +} diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index ee362e48..69a94218 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -19,7 +19,14 @@ namespace ink::runtime::internal globals_impl(const story_impl*); virtual ~globals_impl() { } - virtual void dummy() override { } + protected: + uint32_t* getUInt(hash_t name) const override; + + int32_t* getInt(hash_t name) const override; + + float* getFloat(hash_t name) const override; + + char* getStr(hash_t name) const override; public: // Records a visit to a container @@ -37,6 +44,7 @@ namespace ink::runtime::internal // gets a global variable const value* get_variable(hash_t name) const; + value* get_variable(hash_t name); // checks if globals are initialized bool are_globals_initialized() const { return _globals_initialized; } @@ -46,7 +54,7 @@ namespace ink::runtime::internal // gets the allocated string table inline string_table& strings() { return _strings; } - + // run garbage collection void gc(); @@ -54,10 +62,11 @@ namespace ink::runtime::internal void save(); void restore(); void forget(); + private: // Store the number of containers. This is the length of most of our lists const uint32_t _num_containers; - + // Visit count array internal::allocated_restorable_array _visit_counts; @@ -80,7 +89,7 @@ namespace ink::runtime::internal // Global variables (TODO: Max 50?) // Implemented as a stack (slow lookup) because it has save/restore functionality. // If I could create an avl tree with save/restore, that'd be great but seems super complex. - internal::stack<50> _variables; + mutable internal::stack<50> _variables; bool _globals_initialized; }; -} \ No newline at end of file +} diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index 615b5c84..d2ae9203 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -16,36 +16,51 @@ namespace ink::runtime * @brief Access global variable of Ink runner. * @param name name of variable, as defined in InkScript * @tparam T c++ type of variable + * @return nullptr if variable with this name don't exist or + * the type differs. + * @return pointer to variable */ template - T* get(const char* name); + T* get(const char* name) { + return get_impl(hash_string(name)); + } + template + const T* get(const char* name) const { + return get_impl(hash_string(name)); + } virtual ~globals_interface() = default; protected: - virtual uint32_t* getUInt(const char* name) = 0; - virtual int32_t* getInt(const char* name) = 0; - virtual float* getFloat(const char* name) = 0; - virtual char* getStr(const char* name) = 0; + template + T* get_impl(hash_t name) const { + static_assert( + internal::always_false::value, + "Requested Type is not supported"); + } + virtual uint32_t* getUInt(hash_t name) const = 0; + virtual int32_t* getInt(hash_t name) const = 0; + virtual float* getFloat(hash_t name) const = 0; + virtual char* getStr(hash_t name) const = 0; }; template<> - uint32_t* globals_interface::get(const char* name) { + inline uint32_t* globals_interface::get_impl(hash_t name) const { return getUInt(name); } template<> - int32_t* globals_interface::get(const char* name) { + inline int32_t* globals_interface::get_impl(hash_t name) const { return getInt(name); } template<> - float* globals_interface::get(const char* name) { + inline float* globals_interface::get_impl(hash_t name) const { return getFloat(name); } template<> - char* globals_interface::get(const char* name) { + inline char* globals_interface::get_impl(hash_t name) const { return getStr(name); } } diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index 5e7b0e1b..fac96cd5 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -24,12 +24,8 @@ namespace ink::runtime::internal *existing = val; } - const value* basic_stack::get(hash_t name) const - { - // Find whatever comes first: a matching entry or a stack frame entry - thread_t skip = ~0; - uint32_t jumping = 0; - const entry* found = base::reverse_find([name, &skip, &jumping](entry& e) { + auto reverse_find_predicat_constructor(hash_t name, thread_t& skip, uint32_t& jumping) { + return [name, &skip, &jumping](entry& e) { // Jumping if (jumping > 0) { jumping--; @@ -66,7 +62,31 @@ namespace ink::runtime::internal } return e.name == name || e.name == InvalidHash; - }); + }; + } + + const value* basic_stack::get(hash_t name) const { + // Find whatever comes first: a matching entry or a stack frame entry + thread_t skip = ~0; + uint32_t jumping = 0; + const entry* found = base::reverse_find(reverse_find_predicat_constructor(name, skip, jumping)); + + // If nothing found, no value + if (found == nullptr) + return nullptr; + + // If we found something of that name, return the value + if (found->name == name) + return &found->data; + + // Otherwise, nothing in this stack frame + return nullptr; + } + value* basic_stack::get(hash_t name) { + // Find whatever comes first: a matching entry or a stack frame entry + thread_t skip = ~0; + uint32_t jumping = 0; + entry* found = base::reverse_find(reverse_find_predicat_constructor(name, skip, jumping)); // If nothing found, no value if (found == nullptr) @@ -470,4 +490,4 @@ namespace ink::runtime::internal value none = value(x); base::forget([&none](value& elem) { elem = none; }); } -} \ No newline at end of file +} diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 6108061c..29292bb9 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -36,6 +36,7 @@ namespace ink // Gets an existing value, or nullptr const value* get(hash_t name) const; + value* get(hash_t name); // pushes a new frame onto the stack void push_frame(offset_t return_to, frame_type type); @@ -134,4 +135,4 @@ namespace ink }; } } -} \ No newline at end of file +} diff --git a/inkcpp/value.h b/inkcpp/value.h index b6f087d8..31d72ca0 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -96,9 +96,12 @@ namespace ink // == Getters == int as_int() const { return _first.integer_value; } + int* as_int_ptr() { return &_first.integer_value; } float as_float() const { return _first.float_value; } + float* as_float_ptr() { return &_first.float_value; } uint32_t as_divert() const { return _first.uint_value; } uint32_t as_thread_id() const { return _first.uint_value; } + uint32_t* as_uint_ptr() { return &_first.uint_value; } // TODO: String access? template diff --git a/inkcpp_test/CMakeLists.txt b/inkcpp_test/CMakeLists.txt index 0faa9301..e8845907 100644 --- a/inkcpp_test/CMakeLists.txt +++ b/inkcpp_test/CMakeLists.txt @@ -5,8 +5,17 @@ add_executable(inkcpp_test catch.hpp Main.cpp Callstack.cpp Restorable.cpp Value.cpp + Globals.cpp ) target_link_libraries(inkcpp_test PUBLIC inkcpp) add_test(NAME UnitTests COMMAND $) +set (source "${CMAKE_CURRENT_SOURCE_DIR}/ink") +set (destination "${CMAKE_CURRENT_BINARY_DIR}/ink") +add_custom_command( + TARGET inkcpp_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink ${source} ${destination} + DEPENDS ${destination} + COMMENT "symbolic link resources folder from ${source} => ${destination}" +) diff --git a/inkcpp_test/Globals.cpp b/inkcpp_test/Globals.cpp new file mode 100644 index 00000000..c3a9da80 --- /dev/null +++ b/inkcpp_test/Globals.cpp @@ -0,0 +1,50 @@ +#include "catch.hpp" + +#include +#include +#include + +using namespace ink::runtime; + +SCENARIO("run story with global variable", "[global variables]") +{ + GIVEN ("a story with global variables") + { + story* ink = story::from_file("ink/GlobalStory.bin"); + + + WHEN( "just runs") + { + globals globStore = ink->new_globals(); + runner thread = ink->new_runner(globStore); + THEN("variables should contain values as in inkScript") + { + REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 23 years old.\n"); + REQUIRE(*globStore->get("age") == 23); + } + } + WHEN ("edit number") + { + globals globStore = ink->new_globals(); + runner thread = ink->new_runner(globStore); + *globStore->get("age") = 30; + THEN("variable should contain new value") + { + REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 30 years old.\n"); + REQUIRE(*globStore->get("age") == 30); + } + } + WHEN ("name or type not exist") + { + globals globStore = ink->new_globals(); + runner thread = ink->new_runner(globStore); + auto wrongType = globStore->get("age"); + auto notExistingName = globStore->get("foo"); + THEN("should return nullptr") + { + REQUIRE(wrongType == nullptr); + REQUIRE(notExistingName == nullptr); + } + } + } +} diff --git a/inkcpp_test/ink/GlobalStory.ink b/inkcpp_test/ink/GlobalStory.ink new file mode 100644 index 00000000..38b7814f --- /dev/null +++ b/inkcpp_test/ink/GlobalStory.ink @@ -0,0 +1,6 @@ +// from official ink tutorial + +VAR friendly_name_of_player = "Jackie" +VAR age = 23 + +My name is Jean Passepartout, but my friend's call me {friendly_name_of_player}. I'm {age} years old. From baad8872695af74a5ff52957a9099bbb3b6ad761 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 5 Feb 2021 10:45:18 +0100 Subject: [PATCH 03/12] Remove mutable --- inkcpp/globals_impl.cpp | 42 ++++++++++++++++++----------- inkcpp/globals_impl.h | 14 ++++++---- inkcpp/include/globals.h | 58 ++++++++++++++++++++++++++-------------- 3 files changed, 74 insertions(+), 40 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 10b2cff7..cb24d92f 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -72,28 +72,40 @@ namespace ink::runtime::internal return _variables.get(name); } - uint32_t* globals_impl::getUInt(hash_t name) const { - value* v = _variables.get(name); - return v && v->get_data_type() == data_type::uint32 - ? v->as_uint_ptr() + template + auto fetchVariable(auto stack, hash_t name, data_type type) { + auto v = stack.get(name); + return v && v->get_data_type() == type + ? (v->*FN)() : nullptr; } - int32_t* globals_impl::getInt(hash_t name) const { - value* v = _variables.get(name); - return v && v->get_data_type() == data_type::int32 - ? v->as_int_ptr() - : nullptr; + const uint32_t* globals_impl::getUInt(hash_t name) const { + return fetchVariable<&value::as_uint_ptr>(_variables, name, data_type::uint32); + } + uint32_t* globals_impl::getUInt(hash_t name) { + return fetchVariable<&value::as_uint_ptr>(_variables, name, data_type::uint32); } - float* globals_impl::getFloat(hash_t name) const { - value* v = _variables.get(name); - return v && v->get_data_type() == data_type::float32 - ? v->as_float_ptr() - : nullptr; + const int32_t* globals_impl::getInt(hash_t name) const { + return fetchVariable<&value::as_int_ptr>(_variables, name, data_type::int32); + } + int32_t* globals_impl::getInt(hash_t name) { + return fetchVariable<&value::as_int_ptr>(_variables, name, data_type::int32); + } + + const float* globals_impl::getFloat(hash_t name) const { + return fetchVariable<&value::as_float_ptr>(_variables, name, data_type::float32); + } + float* globals_impl::getFloat(hash_t name) { + return fetchVariable<&value::as_float_ptr>(_variables, name, data_type::float32); } - char* globals_impl::getStr(hash_t name) const { + char* globals_impl::getStr(hash_t name) { + // TODO: add string support + throw ink_exception("String handling is not supported yet!"); + } + const char* globals_impl::getStr(hash_t name) const { // TODO: add string support throw ink_exception("String handling is not supported yet!"); } diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index 69a94218..ecb2d30e 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -20,13 +20,17 @@ namespace ink::runtime::internal virtual ~globals_impl() { } protected: - uint32_t* getUInt(hash_t name) const override; + const uint32_t* getUInt(hash_t name) const override; + uint32_t* getUInt(hash_t name) override; - int32_t* getInt(hash_t name) const override; + const int32_t* getInt(hash_t name) const override; + int32_t* getInt(hash_t name) override; - float* getFloat(hash_t name) const override; + const float* getFloat(hash_t name) const override; + float* getFloat(hash_t name) override; - char* getStr(hash_t name) const override; + const char* getStr(hash_t name) const override; + char* getStr(hash_t name) override; public: // Records a visit to a container @@ -89,7 +93,7 @@ namespace ink::runtime::internal // Global variables (TODO: Max 50?) // Implemented as a stack (slow lookup) because it has save/restore functionality. // If I could create an avl tree with save/restore, that'd be great but seems super complex. - mutable internal::stack<50> _variables; + internal::stack<50> _variables; bool _globals_initialized; }; } diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index d2ae9203..c3a96f8c 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -22,45 +22,63 @@ namespace ink::runtime */ template T* get(const char* name) { - return get_impl(hash_string(name)); + static_assert( + internal::always_false::value, + "Requested Type is not supported"); } template const T* get(const char* name) const { - return get_impl(hash_string(name)); + static_assert( + internal::always_false::value, + "Requested Type is not supported"); } virtual ~globals_interface() = default; protected: - template - T* get_impl(hash_t name) const { - static_assert( - internal::always_false::value, - "Requested Type is not supported"); - } - virtual uint32_t* getUInt(hash_t name) const = 0; - virtual int32_t* getInt(hash_t name) const = 0; - virtual float* getFloat(hash_t name) const = 0; - virtual char* getStr(hash_t name) const = 0; + virtual const uint32_t* getUInt(hash_t name) const = 0; + virtual uint32_t* getUInt(hash_t name) = 0; + virtual const int32_t* getInt(hash_t name) const = 0; + virtual int32_t* getInt(hash_t name) = 0; + virtual const float* getFloat(hash_t name) const = 0; + virtual float* getFloat(hash_t name) = 0; + virtual const char* getStr(hash_t name) const = 0; + virtual char* getStr(hash_t name) = 0; }; template<> - inline uint32_t* globals_interface::get_impl(hash_t name) const { - return getUInt(name); + inline const uint32_t* globals_interface::get(const char* name) const { + return getUInt(hash_string(name)); + } + template<> + inline uint32_t* globals_interface::get(const char* name) { + return getUInt(hash_string(name)); } template<> - inline int32_t* globals_interface::get_impl(hash_t name) const { - return getInt(name); + inline const int32_t* globals_interface::get(const char* name) const { + return getInt(hash_string(name)); + } + template<> + inline int32_t* globals_interface::get(const char* name) { + return getInt(hash_string(name)); } template<> - inline float* globals_interface::get_impl(hash_t name) const { - return getFloat(name); + inline const float* globals_interface::get(const char* name) const { + return getFloat(hash_string(name)); + } + template<> + inline float* globals_interface::get(const char* name) { + return getFloat(hash_string(name)); } template<> - inline char* globals_interface::get_impl(hash_t name) const { - return getStr(name); + inline const char* globals_interface::get(const char* name) const { + return getStr(hash_string(name)); + } + template<> + inline char* globals_interface::get(const char* name) { + return getStr(hash_string(name)); } } From 9243078f2a3762af8d459c705a75a43875e07502 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sun, 7 Feb 2021 19:18:52 +0100 Subject: [PATCH 04/12] Adapt coding convention --- inkcpp/globals_impl.cpp | 30 +++++++++++++++--------------- inkcpp/globals_impl.h | 16 ++++++++-------- inkcpp/include/globals.h | 32 ++++++++++++++++---------------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index cb24d92f..51cc3574 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -73,39 +73,39 @@ namespace ink::runtime::internal } template - auto fetchVariable(auto stack, hash_t name, data_type type) { + auto fetch_variable(auto stack, hash_t name, data_type type) { auto v = stack.get(name); return v && v->get_data_type() == type ? (v->*FN)() : nullptr; } - const uint32_t* globals_impl::getUInt(hash_t name) const { - return fetchVariable<&value::as_uint_ptr>(_variables, name, data_type::uint32); + const uint32_t* globals_impl::get_uint(hash_t name) const { + return fetch_variable<&value::as_uint_ptr>(_variables, name, data_type::uint32); } - uint32_t* globals_impl::getUInt(hash_t name) { - return fetchVariable<&value::as_uint_ptr>(_variables, name, data_type::uint32); + uint32_t* globals_impl::get_uint(hash_t name) { + return fetch_variable<&value::as_uint_ptr>(_variables, name, data_type::uint32); } - const int32_t* globals_impl::getInt(hash_t name) const { - return fetchVariable<&value::as_int_ptr>(_variables, name, data_type::int32); + const int32_t* globals_impl::get_int(hash_t name) const { + return fetch_variable<&value::as_int_ptr>(_variables, name, data_type::int32); } - int32_t* globals_impl::getInt(hash_t name) { - return fetchVariable<&value::as_int_ptr>(_variables, name, data_type::int32); + int32_t* globals_impl::get_int(hash_t name) { + return fetch_variable<&value::as_int_ptr>(_variables, name, data_type::int32); } - const float* globals_impl::getFloat(hash_t name) const { - return fetchVariable<&value::as_float_ptr>(_variables, name, data_type::float32); + const float* globals_impl::get_float(hash_t name) const { + return fetch_variable<&value::as_float_ptr>(_variables, name, data_type::float32); } - float* globals_impl::getFloat(hash_t name) { - return fetchVariable<&value::as_float_ptr>(_variables, name, data_type::float32); + float* globals_impl::get_float(hash_t name) { + return fetch_variable<&value::as_float_ptr>(_variables, name, data_type::float32); } - char* globals_impl::getStr(hash_t name) { + char* globals_impl::get_str(hash_t name) { // TODO: add string support throw ink_exception("String handling is not supported yet!"); } - const char* globals_impl::getStr(hash_t name) const { + const char* globals_impl::get_str(hash_t name) const { // TODO: add string support throw ink_exception("String handling is not supported yet!"); } diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index ecb2d30e..a24a293b 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -20,17 +20,17 @@ namespace ink::runtime::internal virtual ~globals_impl() { } protected: - const uint32_t* getUInt(hash_t name) const override; - uint32_t* getUInt(hash_t name) override; + const uint32_t* get_uint(hash_t name) const override; + uint32_t* get_uint(hash_t name) override; - const int32_t* getInt(hash_t name) const override; - int32_t* getInt(hash_t name) override; + const int32_t* get_int(hash_t name) const override; + int32_t* get_int(hash_t name) override; - const float* getFloat(hash_t name) const override; - float* getFloat(hash_t name) override; + const float* get_float(hash_t name) const override; + float* get_float(hash_t name) override; - const char* getStr(hash_t name) const override; - char* getStr(hash_t name) override; + const char* get_str(hash_t name) const override; + char* get_str(hash_t name) override; public: // Records a visit to a container diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index c3a96f8c..ce82033f 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -36,49 +36,49 @@ namespace ink::runtime virtual ~globals_interface() = default; protected: - virtual const uint32_t* getUInt(hash_t name) const = 0; - virtual uint32_t* getUInt(hash_t name) = 0; - virtual const int32_t* getInt(hash_t name) const = 0; - virtual int32_t* getInt(hash_t name) = 0; - virtual const float* getFloat(hash_t name) const = 0; - virtual float* getFloat(hash_t name) = 0; - virtual const char* getStr(hash_t name) const = 0; - virtual char* getStr(hash_t name) = 0; + virtual const uint32_t* get_uint(hash_t name) const = 0; + virtual uint32_t* get_uint(hash_t name) = 0; + virtual const int32_t* get_int(hash_t name) const = 0; + virtual int32_t* get_int(hash_t name) = 0; + virtual const float* get_float(hash_t name) const = 0; + virtual float* get_float(hash_t name) = 0; + virtual const char* get_str(hash_t name) const = 0; + virtual char* get_str(hash_t name) = 0; }; template<> inline const uint32_t* globals_interface::get(const char* name) const { - return getUInt(hash_string(name)); + return get_uint(hash_string(name)); } template<> inline uint32_t* globals_interface::get(const char* name) { - return getUInt(hash_string(name)); + return get_uint(hash_string(name)); } template<> inline const int32_t* globals_interface::get(const char* name) const { - return getInt(hash_string(name)); + return get_int(hash_string(name)); } template<> inline int32_t* globals_interface::get(const char* name) { - return getInt(hash_string(name)); + return get_int(hash_string(name)); } template<> inline const float* globals_interface::get(const char* name) const { - return getFloat(hash_string(name)); + return get_float(hash_string(name)); } template<> inline float* globals_interface::get(const char* name) { - return getFloat(hash_string(name)); + return get_float(hash_string(name)); } template<> inline const char* globals_interface::get(const char* name) const { - return getStr(hash_string(name)); + return get_str(hash_string(name)); } template<> inline char* globals_interface::get(const char* name) { - return getStr(hash_string(name)); + return get_str(hash_string(name)); } } From 52660df2368b053757a765e039065c011cabf567 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 8 Feb 2021 16:38:29 +0100 Subject: [PATCH 05/12] Add string support for Globals --- inkcpp/CMakeLists.txt | 1 + inkcpp/globals.cpp | 51 +++++++++++++++++++++++++++++ inkcpp/globals_impl.cpp | 28 ++++++++++++---- inkcpp/globals_impl.h | 4 +-- inkcpp/include/globals.h | 70 ++++++++++++++++++++++++++++++++++++---- inkcpp/value.h | 2 +- inkcpp_test/Globals.cpp | 5 ++- 7 files changed, 143 insertions(+), 18 deletions(-) create mode 100644 inkcpp/globals.cpp diff --git a/inkcpp/CMakeLists.txt b/inkcpp/CMakeLists.txt index 305db42a..bb4db5ad 100644 --- a/inkcpp/CMakeLists.txt +++ b/inkcpp/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND SOURCES value.h value.cpp string_table.h string_table.cpp avl_array.h header.cpp + globals.cpp ) source_group(Collections REGULAR_EXPRESSION collections/.*) add_library(inkcpp ${SOURCES}) diff --git a/inkcpp/globals.cpp b/inkcpp/globals.cpp new file mode 100644 index 00000000..4aa22b67 --- /dev/null +++ b/inkcpp/globals.cpp @@ -0,0 +1,51 @@ +#include "globals.h" +#include "globals_impl.h" +#include "value.h" + +#include + +namespace ink::runtime { + template<> + auto global_string::operator<=>(const global_string& other) const { + const char* m = _string; + const char* o = other._string; + while(*m && *o) { + if (auto cmp = *m <=> *o; cmp != 0) { return cmp; } + ++m; + ++o; + } + if (*o) { return std::strong_ordering::less; } + if (*m) { return std::strong_ordering::greater; } + return std::strong_ordering::equal; + } + template<> + global_string::global_string(const internal::globals_impl& globals, const char* string) + : _globals{globals}, _string{string}, _size{0} + { + if (*this) { for(const char* ptr = _string; *ptr; ++ptr) { ++_size; } } + } + + global_string::global_string(internal::globals_impl& globals, const char* string, hash_t name) + : _globals{globals}, _name{name}, global_string(globals, string) + {} + + void global_string::set(const char* string) { + if (!*this) { ink_exception("try to set unbound variable!"); } + size_t new_size = 0; + char* ptr; + for(const char* i = string; *i; ++i) { ++new_size; } + internal::value* v = _globals.get_variable(_name); + char* new_string = _globals.strings().create(new_size + 1); + _globals.strings().mark_used(new_string); + ptr = new_string; + for(const char* i = string; *i; ++ptr, ++i) { + *ptr = *i; + } + internal::data d; + d.set_string(new_string, true); + *v = internal::value(d); + *ptr = 0; + global_string::_size = new_size; + global_string::_string = new_string; + } +} diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 51cc3574..5541486b 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -72,6 +72,13 @@ namespace ink::runtime::internal return _variables.get(name); } + template + auto fetch_variable(auto stack, hash_t name, data_type type) { + auto v = stack.get(name); + return v && v->get_data_type() == type + ? (v->*FN)() + : nullptr; + } template auto fetch_variable(auto stack, hash_t name, data_type type) { auto v = stack.get(name); @@ -101,13 +108,20 @@ namespace ink::runtime::internal return fetch_variable<&value::as_float_ptr>(_variables, name, data_type::float32); } - char* globals_impl::get_str(hash_t name) { - // TODO: add string support - throw ink_exception("String handling is not supported yet!"); - } - const char* globals_impl::get_str(hash_t name) const { - // TODO: add string support - throw ink_exception("String handling is not supported yet!"); + global_string globals_impl::get_str(hash_t name) { + const char* s = fetch_variable<&value::as_str>(_variables, name, data_type::string_table_pointer); + if (!s) s = fetch_variable<&value::as_str>(_variables, name, data_type::allocated_string_pointer); + return global_string( + *this, + s, + name + ); + } + global_string globals_impl::get_str(hash_t name) const { + return global_string( + *this, + fetch_variable<&value::as_str>(_variables, name, data_type::string_table_pointer) + ); } diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index a24a293b..e7aaf1d0 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -29,8 +29,8 @@ namespace ink::runtime::internal const float* get_float(hash_t name) const override; float* get_float(hash_t name) override; - const char* get_str(hash_t name) const override; - char* get_str(hash_t name) override; + global_string get_str(hash_t name) const override; + global_string get_str(hash_t name) override; public: // Records a visit to a container diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index ce82033f..7862bdb7 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -4,6 +4,62 @@ namespace ink::runtime { + class globals_interface; + namespace internal { class globals_impl;} + /** + * @brief wrapper for string in global store. + * @tparam WRITE if writing enable for this type + */ + template + class global_string { + public: + operator bool() { return _string; } + auto operator<=>(const global_string& other) const; + char operator[](size_t i) const { return _string[i]; }; // TODO: boundary check? + size_t size() const { return _size; }; + const char* data() const { return _string; } + + protected: + friend class ink::runtime::internal::globals_impl; + global_string(const internal::globals_impl& globals, const char* string); + + const internal::globals_impl& _globals; + const char* _string; + size_t _size; + }; + template<> + class global_string : public global_string{ + public: + /** + * @brief define new value for string. + * The string will be copied in internal data structure there for. + */ + void set(const char* new_string); + + using global_string::operator bool; + protected: + friend class ink::runtime::internal::globals_impl; + global_string(internal::globals_impl& globals, const char* string, hash_t name); + + internal::globals_impl& _globals; + hash_t _name; + }; + template + struct return_type { + using type = T*; + using c_type = const T*; + }; + template + using return_type_t = typename return_type::type; + template + using return_type_ct = typename return_type::c_type; + + template + struct return_type> { + using type = global_string; + using c_type = global_string; + }; + /** * Represents a global store to be shared amongst ink runners. * Stores global variable values, visit counts, turn counts, etc. @@ -21,13 +77,13 @@ namespace ink::runtime * @return pointer to variable */ template - T* get(const char* name) { + return_type_t get(const char* name) { static_assert( internal::always_false::value, "Requested Type is not supported"); } template - const T* get(const char* name) const { + return_type_ct get(const char* name) const { static_assert( internal::always_false::value, "Requested Type is not supported"); @@ -42,12 +98,12 @@ namespace ink::runtime virtual int32_t* get_int(hash_t name) = 0; virtual const float* get_float(hash_t name) const = 0; virtual float* get_float(hash_t name) = 0; - virtual const char* get_str(hash_t name) const = 0; - virtual char* get_str(hash_t name) = 0; + virtual global_string get_str(hash_t name) const = 0; + virtual global_string get_str(hash_t name) = 0; }; template<> - inline const uint32_t* globals_interface::get(const char* name) const { + inline const uint32_t* globals_interface::get(const char* name) const { return get_uint(hash_string(name)); } template<> @@ -74,11 +130,11 @@ namespace ink::runtime } template<> - inline const char* globals_interface::get(const char* name) const { + inline global_string globals_interface::get>(const char* name) const { return get_str(hash_string(name)); } template<> - inline char* globals_interface::get(const char* name) { + inline global_string globals_interface::get>(const char* name) { return get_str(hash_string(name)); } } diff --git a/inkcpp/value.h b/inkcpp/value.h index 31d72ca0..2646cf12 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -102,7 +102,7 @@ namespace ink uint32_t as_divert() const { return _first.uint_value; } uint32_t as_thread_id() const { return _first.uint_value; } uint32_t* as_uint_ptr() { return &_first.uint_value; } - // TODO: String access? + const char* as_str() const { return _first.string_val; } template T get() const { static_assert(always_false::value, "Type not supported by value class"); } diff --git a/inkcpp_test/Globals.cpp b/inkcpp_test/Globals.cpp index c3a9da80..5a5c2306 100644 --- a/inkcpp_test/Globals.cpp +++ b/inkcpp_test/Globals.cpp @@ -21,6 +21,7 @@ SCENARIO("run story with global variable", "[global variables]") { REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 23 years old.\n"); REQUIRE(*globStore->get("age") == 23); + REQUIRE(globStore->get>("friendly_name_of_player").data() == std::string{"Jackie"}); } } WHEN ("edit number") @@ -28,10 +29,12 @@ SCENARIO("run story with global variable", "[global variables]") globals globStore = ink->new_globals(); runner thread = ink->new_runner(globStore); *globStore->get("age") = 30; + globStore->get>("friendly_name_of_player").set("Freddy"); THEN("variable should contain new value") { - REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 30 years old.\n"); + REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Freddy. I'm 30 years old.\n"); REQUIRE(*globStore->get("age") == 30); + REQUIRE(globStore->get>("friendly_name_of_player").data() == std::string{"Freddy"}); } } WHEN ("name or type not exist") From bf6e9854a9ac0bfc868eec10acde19cea80e8899 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 8 Feb 2021 16:43:18 +0100 Subject: [PATCH 06/12] Allow access lower privileged string --- inkcpp/include/globals.h | 15 +++++++++++++-- inkcpp_test/Globals.cpp | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index 7862bdb7..ada269af 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -54,12 +54,18 @@ namespace ink::runtime template using return_type_ct = typename return_type::c_type; - template - struct return_type> { + template<> + struct return_type> { using type = global_string; using c_type = global_string; }; + template<> + struct return_type> { + using type = global_string; + using c_type = global_string; + }; + /** * Represents a global store to be shared amongst ink runners. * Stores global variable values, visit counts, turn counts, etc. @@ -133,6 +139,11 @@ namespace ink::runtime inline global_string globals_interface::get>(const char* name) const { return get_str(hash_string(name)); } + + template<> + inline global_string globals_interface::get>(const char* name) { + return get_str(hash_string(name)); + } template<> inline global_string globals_interface::get>(const char* name) { return get_str(hash_string(name)); diff --git a/inkcpp_test/Globals.cpp b/inkcpp_test/Globals.cpp index 5a5c2306..b5d56253 100644 --- a/inkcpp_test/Globals.cpp +++ b/inkcpp_test/Globals.cpp @@ -21,7 +21,7 @@ SCENARIO("run story with global variable", "[global variables]") { REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 23 years old.\n"); REQUIRE(*globStore->get("age") == 23); - REQUIRE(globStore->get>("friendly_name_of_player").data() == std::string{"Jackie"}); + REQUIRE(globStore->get>("friendly_name_of_player").data() == std::string{"Jackie"}); } } WHEN ("edit number") @@ -34,7 +34,7 @@ SCENARIO("run story with global variable", "[global variables]") { REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Freddy. I'm 30 years old.\n"); REQUIRE(*globStore->get("age") == 30); - REQUIRE(globStore->get>("friendly_name_of_player").data() == std::string{"Freddy"}); + REQUIRE(globStore->get>("friendly_name_of_player").data() == std::string{"Freddy"}); } } WHEN ("name or type not exist") From cf3ee1600baaa7139a9c3fdad22b1067ed049c4d Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 12 Feb 2021 18:00:48 +0100 Subject: [PATCH 07/12] Implement Idea with getter and setter for variable Avoid mess with string class, and maybe not existing global memory. Getter returns optional --- inkcpp/CMakeLists.txt | 1 - inkcpp/globals.cpp | 51 --------------- inkcpp/globals_impl.cpp | 79 ++++++++++++++---------- inkcpp/globals_impl.h | 10 +-- inkcpp/include/globals.h | 130 ++++++++++++--------------------------- inkcpp/value.h | 5 ++ inkcpp_test/Globals.cpp | 27 ++++++-- shared/public/system.h | 42 +++++++++++++ 8 files changed, 161 insertions(+), 184 deletions(-) delete mode 100644 inkcpp/globals.cpp diff --git a/inkcpp/CMakeLists.txt b/inkcpp/CMakeLists.txt index bb4db5ad..305db42a 100644 --- a/inkcpp/CMakeLists.txt +++ b/inkcpp/CMakeLists.txt @@ -18,7 +18,6 @@ list(APPEND SOURCES value.h value.cpp string_table.h string_table.cpp avl_array.h header.cpp - globals.cpp ) source_group(Collections REGULAR_EXPRESSION collections/.*) add_library(inkcpp ${SOURCES}) diff --git a/inkcpp/globals.cpp b/inkcpp/globals.cpp deleted file mode 100644 index 4aa22b67..00000000 --- a/inkcpp/globals.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "globals.h" -#include "globals_impl.h" -#include "value.h" - -#include - -namespace ink::runtime { - template<> - auto global_string::operator<=>(const global_string& other) const { - const char* m = _string; - const char* o = other._string; - while(*m && *o) { - if (auto cmp = *m <=> *o; cmp != 0) { return cmp; } - ++m; - ++o; - } - if (*o) { return std::strong_ordering::less; } - if (*m) { return std::strong_ordering::greater; } - return std::strong_ordering::equal; - } - template<> - global_string::global_string(const internal::globals_impl& globals, const char* string) - : _globals{globals}, _string{string}, _size{0} - { - if (*this) { for(const char* ptr = _string; *ptr; ++ptr) { ++_size; } } - } - - global_string::global_string(internal::globals_impl& globals, const char* string, hash_t name) - : _globals{globals}, _name{name}, global_string(globals, string) - {} - - void global_string::set(const char* string) { - if (!*this) { ink_exception("try to set unbound variable!"); } - size_t new_size = 0; - char* ptr; - for(const char* i = string; *i; ++i) { ++new_size; } - internal::value* v = _globals.get_variable(_name); - char* new_string = _globals.strings().create(new_size + 1); - _globals.strings().mark_used(new_string); - ptr = new_string; - for(const char* i = string; *i; ++ptr, ++i) { - *ptr = *i; - } - internal::data d; - d.set_string(new_string, true); - *v = internal::value(d); - *ptr = 0; - global_string::_size = new_size; - global_string::_string = new_string; - } -} diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 5541486b..eccd00e7 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -72,59 +72,76 @@ namespace ink::runtime::internal return _variables.get(name); } - template - auto fetch_variable(auto stack, hash_t name, data_type type) { - auto v = stack.get(name); - return v && v->get_data_type() == type + template + auto fetch_variable( const value* v, TYPES ... types) { + return v && ((v->get_data_type() == types) || ...) ? (v->*FN)() : nullptr; } - template - auto fetch_variable(auto stack, hash_t name, data_type type) { - auto v = stack.get(name); - return v && v->get_data_type() == type + template + auto fetch_variable(value* v, TYPES ... types) { + return v && ((v->get_data_type() == types) || ...) ? (v->*FN)() : nullptr; } const uint32_t* globals_impl::get_uint(hash_t name) const { - return fetch_variable<&value::as_uint_ptr>(_variables, name, data_type::uint32); + return fetch_variable<&value::as_uint_ptr>(get_variable(name), data_type::uint32); } - uint32_t* globals_impl::get_uint(hash_t name) { - return fetch_variable<&value::as_uint_ptr>(_variables, name, data_type::uint32); + bool globals_impl::set_uint(hash_t name, uint32_t val) { + uint32_t* p = fetch_variable<&value::as_uint_ptr>(get_variable(name), data_type::uint32); + if (p == nullptr) { return false; } + *p = val; + return true; } const int32_t* globals_impl::get_int(hash_t name) const { - return fetch_variable<&value::as_int_ptr>(_variables, name, data_type::int32); + return fetch_variable<&value::as_int_ptr>(get_variable(name), data_type::int32); } - int32_t* globals_impl::get_int(hash_t name) { - return fetch_variable<&value::as_int_ptr>(_variables, name, data_type::int32); + bool globals_impl::set_int(hash_t name, int32_t val) { + int32_t* p = fetch_variable<&value::as_int_ptr>(get_variable(name), data_type::int32); + if (p == nullptr) { return false; } + *p = val; + return true; } const float* globals_impl::get_float(hash_t name) const { - return fetch_variable<&value::as_float_ptr>(_variables, name, data_type::float32); + return fetch_variable<&value::as_float_ptr>(get_variable(name), data_type::float32); } - float* globals_impl::get_float(hash_t name) { - return fetch_variable<&value::as_float_ptr>(_variables, name, data_type::float32); + bool globals_impl::set_float(hash_t name, float val) { + float* p = fetch_variable<&value::as_float_ptr>(get_variable(name), data_type::float32); + if (p == nullptr) { return false; } + *p = val; + return true; } - global_string globals_impl::get_str(hash_t name) { - const char* s = fetch_variable<&value::as_str>(_variables, name, data_type::string_table_pointer); - if (!s) s = fetch_variable<&value::as_str>(_variables, name, data_type::allocated_string_pointer); - return global_string( - *this, - s, - name - ); + const char * const * globals_impl::get_str(hash_t name) const { + return fetch_variable<&value::as_str_ptr>( + get_variable(name), + data_type::allocated_string_pointer, + data_type::string_table_pointer); } - global_string globals_impl::get_str(hash_t name) const { - return global_string( - *this, - fetch_variable<&value::as_str>(_variables, name, data_type::string_table_pointer) - ); + bool globals_impl::set_str(hash_t name, const char* val) { + value* v = get_variable(name); + if (v->type() == value_type::string) + { + size_t size = 0; + char* ptr; + for(const char*i = val; *i; ++i) { ++size; } + char* new_string = strings().create(size + 1); + strings().mark_used(new_string); + ptr = new_string; + for(const char* i = val; *i; ++i) { + *ptr++ = *i; + } + internal::data d; + d.set_string(new_string, true); + *v = internal::value(d); + return true; + } + return false; } - void globals_impl::initialize_globals(runner_impl* run) { // If no way to move there, then there are no globals. diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index e7aaf1d0..eb14063e 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -21,16 +21,16 @@ namespace ink::runtime::internal protected: const uint32_t* get_uint(hash_t name) const override; - uint32_t* get_uint(hash_t name) override; + bool set_uint(hash_t name, uint32_t value) override; const int32_t* get_int(hash_t name) const override; - int32_t* get_int(hash_t name) override; + bool set_int(hash_t name, int32_t value) override; const float* get_float(hash_t name) const override; - float* get_float(hash_t name) override; + bool set_float(hash_t name, float value) override; - global_string get_str(hash_t name) const override; - global_string get_str(hash_t name) override; + const char * const * get_str(hash_t name) const override; + bool set_str(hash_t name, const char* value) override; public: // Records a visit to a container diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index ada269af..231fc368 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -6,65 +6,6 @@ namespace ink::runtime { class globals_interface; namespace internal { class globals_impl;} - /** - * @brief wrapper for string in global store. - * @tparam WRITE if writing enable for this type - */ - template - class global_string { - public: - operator bool() { return _string; } - auto operator<=>(const global_string& other) const; - char operator[](size_t i) const { return _string[i]; }; // TODO: boundary check? - size_t size() const { return _size; }; - const char* data() const { return _string; } - - protected: - friend class ink::runtime::internal::globals_impl; - global_string(const internal::globals_impl& globals, const char* string); - - const internal::globals_impl& _globals; - const char* _string; - size_t _size; - }; - template<> - class global_string : public global_string{ - public: - /** - * @brief define new value for string. - * The string will be copied in internal data structure there for. - */ - void set(const char* new_string); - - using global_string::operator bool; - protected: - friend class ink::runtime::internal::globals_impl; - global_string(internal::globals_impl& globals, const char* string, hash_t name); - - internal::globals_impl& _globals; - hash_t _name; - }; - template - struct return_type { - using type = T*; - using c_type = const T*; - }; - template - using return_type_t = typename return_type::type; - template - using return_type_ct = typename return_type::c_type; - - template<> - struct return_type> { - using type = global_string; - using c_type = global_string; - }; - - template<> - struct return_type> { - using type = global_string; - using c_type = global_string; - }; /** * Represents a global store to be shared amongst ink runners. @@ -78,74 +19,83 @@ namespace ink::runtime * @brief Access global variable of Ink runner. * @param name name of variable, as defined in InkScript * @tparam T c++ type of variable - * @return nullptr if variable with this name don't exist or - * the type differs. - * @return pointer to variable + * @return nullopt if variable won't exist or type won't match */ template - return_type_t get(const char* name) { + optional get(const char* name) const { static_assert( internal::always_false::value, "Requested Type is not supported"); } + + /** + * @brief Write new value in global store. + * @param name name of variable, as defined in InkScript + * @tparam T c++ type of variable + * @return true on success + */ template - return_type_ct get(const char* name) const { + bool set(const char* name, const T& val) { static_assert( internal::always_false::value, "Requested Type is not supported"); + return false; } virtual ~globals_interface() = default; protected: virtual const uint32_t* get_uint(hash_t name) const = 0; - virtual uint32_t* get_uint(hash_t name) = 0; + virtual bool set_uint(hash_t name, uint32_t val) = 0; virtual const int32_t* get_int(hash_t name) const = 0; - virtual int32_t* get_int(hash_t name) = 0; + virtual bool set_int(hash_t name, int32_t val) = 0; virtual const float* get_float(hash_t name) const = 0; - virtual float* get_float(hash_t name) = 0; - virtual global_string get_str(hash_t name) const = 0; - virtual global_string get_str(hash_t name) = 0; + virtual bool set_float(hash_t name, float val) = 0; + virtual const char* const * get_str(hash_t name) const = 0; + virtual bool set_str(hash_t name, const char* val) = 0; }; template<> - inline const uint32_t* globals_interface::get(const char* name) const { - return get_uint(hash_string(name)); + inline optional globals_interface::get(const char* name) const { + const uint32_t* p = get_uint(hash_string(name)); + if (p) { return {*p}; } + return {nullopt}; } template<> - inline uint32_t* globals_interface::get(const char* name) { - return get_uint(hash_string(name)); + inline bool globals_interface::set(const char* name, const uint32_t& val) { + return set_uint(hash_string(name), val); } template<> - inline const int32_t* globals_interface::get(const char* name) const { - return get_int(hash_string(name)); + inline optional globals_interface::get(const char* name) const { + const int32_t* p = get_int(hash_string(name)); + if (p) { return {*p}; } + return {nullopt}; } template<> - inline int32_t* globals_interface::get(const char* name) { - return get_int(hash_string(name)); + inline bool globals_interface::set(const char* name, const int32_t& val) { + return set_int(hash_string(name), val); } template<> - inline const float* globals_interface::get(const char* name) const { - return get_float(hash_string(name)); + inline optional globals_interface::get(const char* name) const { + const float* p = get_float(hash_string(name)); + if (p) { return {*p}; } + return {nullopt}; } template<> - inline float* globals_interface::get(const char* name) { - return get_float(hash_string(name)); - } - - template<> - inline global_string globals_interface::get>(const char* name) const { - return get_str(hash_string(name)); + inline bool globals_interface::set(const char* name, const float& val) { + return set_float(hash_string(name), val); } template<> - inline global_string globals_interface::get>(const char* name) { - return get_str(hash_string(name)); + inline optionalglobals_interface::get(const char* name) const { + const char * const * p = get_str(hash_string(name)); + if (p) { return {*p}; } + return {nullopt}; } template<> - inline global_string globals_interface::get>(const char* name) { - return get_str(hash_string(name)); + inline bool globals_interface::set(const char* name, const char * const & val) { + return set_str(hash_string(name), val); } } diff --git a/inkcpp/value.h b/inkcpp/value.h index 2646cf12..86637054 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -97,12 +97,17 @@ namespace ink // == Getters == int as_int() const { return _first.integer_value; } int* as_int_ptr() { return &_first.integer_value; } + const int* as_int_ptr() const { return &_first.integer_value; } float as_float() const { return _first.float_value; } float* as_float_ptr() { return &_first.float_value; } + const float* as_float_ptr() const { return &_first.float_value; } uint32_t as_divert() const { return _first.uint_value; } uint32_t as_thread_id() const { return _first.uint_value; } uint32_t* as_uint_ptr() { return &_first.uint_value; } + const uint32_t* as_uint_ptr() const { return &_first.uint_value; } + // FIXME: we should compress the value before const char* as_str() const { return _first.string_val; } + const char* const * as_str_ptr() const { return &_first.string_val; } template T get() const { static_assert(always_false::value, "Type not supported by value class"); } diff --git a/inkcpp_test/Globals.cpp b/inkcpp_test/Globals.cpp index b5d56253..5c968aaa 100644 --- a/inkcpp_test/Globals.cpp +++ b/inkcpp_test/Globals.cpp @@ -21,20 +21,27 @@ SCENARIO("run story with global variable", "[global variables]") { REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 23 years old.\n"); REQUIRE(*globStore->get("age") == 23); - REQUIRE(globStore->get>("friendly_name_of_player").data() == std::string{"Jackie"}); + REQUIRE(*globStore->get("friendly_name_of_player") == std::string{"Jackie"}); } } WHEN ("edit number") { globals globStore = ink->new_globals(); runner thread = ink->new_runner(globStore); - *globStore->get("age") = 30; - globStore->get>("friendly_name_of_player").set("Freddy"); + bool resi + = globStore->set("age", 30); + bool resc + = globStore->set("friendly_name_of_player", "Freddy"); + THEN("execution should success") + { + REQUIRE(resi == true); + REQUIRE(resc == true); + } THEN("variable should contain new value") { REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Freddy. I'm 30 years old.\n"); REQUIRE(*globStore->get("age") == 30); - REQUIRE(globStore->get>("friendly_name_of_player").data() == std::string{"Freddy"}); + REQUIRE(*globStore->get("friendly_name_of_player") == std::string{"Freddy"}); } } WHEN ("name or type not exist") @@ -45,8 +52,16 @@ SCENARIO("run story with global variable", "[global variables]") auto notExistingName = globStore->get("foo"); THEN("should return nullptr") { - REQUIRE(wrongType == nullptr); - REQUIRE(notExistingName == nullptr); + REQUIRE(wrongType.has_value() == false); + REQUIRE(notExistingName.has_value() == false); + } + + bool rest = globStore->set("age", 3); + bool resn = globStore->set("foo", 3); + THEN("should return false") + { + REQUIRE(rest == false); + REQUIRE(resn == false); } } } diff --git a/shared/public/system.h b/shared/public/system.h index 20197f22..0a7d6a5c 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -11,6 +11,7 @@ #ifdef INK_ENABLE_STL #include #include +#include #endif namespace ink @@ -121,6 +122,47 @@ namespace ink template struct always_false { static constexpr bool value = false; }; } + +#ifdef INK_ENABLE_STL + template + using optional = std::optional; + constexpr std::nullopt_t nullopt = std::nullopt; +#else + struct nullopt_t{}; + constexpr nullopt_t nullopt; + + template + class optional { + public: + optional() {} + optional(nullopt_t) {} + optional(T&& val) _has_value{true}, _value{std::forward(val)}{} + optional(const T& val) _has_value{true}, _value{val}{} + + const T& operator*() const { return _value; } + T& operator*() { return _value; } + const T* operator->() const { return &_value; } + T* operator->() { return &_value; } + + constexpr bool has_value() const { return _has_value; } + constexpr T& value() { check(); return _value; } + constexpr const T& value() const { check(); return _value; } + constexpr operator bool() const { return has_value(); } + template + constexpr T value_or(U&& u) const { + return _has_value ? _value : static_cast(std::forward(u)); + } + private: + void check() const { + if ( ! _has_value) { + throw ink_exception("Can't access empty optional!"); + } + } + + bool _has_value = false; + T _value; + }; +#endif } // Platform specific defines // From d979f9b43df86a4668087b3b0f97e72b06038653 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 12 Feb 2021 20:22:34 +0100 Subject: [PATCH 08/12] Fix Microsoft Compiler error + replace auto with explicit type --- inkcpp/globals_impl.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index eccd00e7..f6907df6 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -72,13 +72,13 @@ namespace ink::runtime::internal return _variables.get(name); } - template + template auto fetch_variable( const value* v, TYPES ... types) { return v && ((v->get_data_type() == types) || ...) ? (v->*FN)() : nullptr; } - template + template auto fetch_variable(value* v, TYPES ... types) { return v && ((v->get_data_type() == types) || ...) ? (v->*FN)() @@ -86,37 +86,37 @@ namespace ink::runtime::internal } const uint32_t* globals_impl::get_uint(hash_t name) const { - return fetch_variable<&value::as_uint_ptr>(get_variable(name), data_type::uint32); + return fetch_variable(get_variable(name), data_type::uint32); } bool globals_impl::set_uint(hash_t name, uint32_t val) { - uint32_t* p = fetch_variable<&value::as_uint_ptr>(get_variable(name), data_type::uint32); + uint32_t* p = fetch_variable(get_variable(name), data_type::uint32); if (p == nullptr) { return false; } *p = val; return true; } const int32_t* globals_impl::get_int(hash_t name) const { - return fetch_variable<&value::as_int_ptr>(get_variable(name), data_type::int32); + return fetch_variable(get_variable(name), data_type::int32); } bool globals_impl::set_int(hash_t name, int32_t val) { - int32_t* p = fetch_variable<&value::as_int_ptr>(get_variable(name), data_type::int32); + int32_t* p = fetch_variable(get_variable(name), data_type::int32); if (p == nullptr) { return false; } *p = val; return true; } const float* globals_impl::get_float(hash_t name) const { - return fetch_variable<&value::as_float_ptr>(get_variable(name), data_type::float32); + return fetch_variable(get_variable(name), data_type::float32); } bool globals_impl::set_float(hash_t name, float val) { - float* p = fetch_variable<&value::as_float_ptr>(get_variable(name), data_type::float32); + float* p = fetch_variable(get_variable(name), data_type::float32); if (p == nullptr) { return false; } *p = val; return true; } const char * const * globals_impl::get_str(hash_t name) const { - return fetch_variable<&value::as_str_ptr>( + return fetch_variable( get_variable(name), data_type::allocated_string_pointer, data_type::string_table_pointer); From cd265019f336aaf94b69dfafa8faafda24503414 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sun, 14 Feb 2021 12:07:07 +0100 Subject: [PATCH 09/12] Compile inkBin to test time from ink --- inkcpp_test/CMakeLists.txt | 2 +- inkcpp_test/Globals.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/inkcpp_test/CMakeLists.txt b/inkcpp_test/CMakeLists.txt index e8845907..27a01340 100644 --- a/inkcpp_test/CMakeLists.txt +++ b/inkcpp_test/CMakeLists.txt @@ -8,7 +8,7 @@ add_executable(inkcpp_test catch.hpp Main.cpp Globals.cpp ) -target_link_libraries(inkcpp_test PUBLIC inkcpp) +target_link_libraries(inkcpp_test PUBLIC inkcpp inkcpp_compiler) add_test(NAME UnitTests COMMAND $) set (source "${CMAKE_CURRENT_SOURCE_DIR}/ink") diff --git a/inkcpp_test/Globals.cpp b/inkcpp_test/Globals.cpp index 5c968aaa..156a00a4 100644 --- a/inkcpp_test/Globals.cpp +++ b/inkcpp_test/Globals.cpp @@ -1,8 +1,10 @@ #include "catch.hpp" +#include "../inkcpp_cl/test.cpp" #include #include #include +#include using namespace ink::runtime; @@ -10,8 +12,9 @@ SCENARIO("run story with global variable", "[global variables]") { GIVEN ("a story with global variables") { - story* ink = story::from_file("ink/GlobalStory.bin"); - + inklecate("ink/GlobalStory.ink", "GlobalsStory.tmp"); + ink::compiler::run("GlobalsStory.tmp", "GlobalsStory.bin"); + auto ink = story::from_file("GlobalsStory.bin"); WHEN( "just runs") { From b78a98a3d4246e49c13b3cedae498ad40b32d41c Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sun, 14 Feb 2021 12:56:45 +0100 Subject: [PATCH 10/12] Add test case for contacted string value --- inkcpp_test/Globals.cpp | 12 ++++++++++-- inkcpp_test/ink/GlobalStory.ink | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/inkcpp_test/Globals.cpp b/inkcpp_test/Globals.cpp index 156a00a4..841ddf19 100644 --- a/inkcpp_test/Globals.cpp +++ b/inkcpp_test/Globals.cpp @@ -22,7 +22,7 @@ SCENARIO("run story with global variable", "[global variables]") runner thread = ink->new_runner(globStore); THEN("variables should contain values as in inkScript") { - REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 23 years old.\n"); + REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 23 years old.\nFoo:23\n"); REQUIRE(*globStore->get("age") == 23); REQUIRE(*globStore->get("friendly_name_of_player") == std::string{"Jackie"}); } @@ -42,10 +42,18 @@ SCENARIO("run story with global variable", "[global variables]") } THEN("variable should contain new value") { - REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Freddy. I'm 30 years old.\n"); + REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Freddy. I'm 30 years old.\nFoo:30\n"); REQUIRE(*globStore->get("age") == 30); REQUIRE(*globStore->get("friendly_name_of_player") == std::string{"Freddy"}); } + WHEN ("something added to string") + { + // concat in GlobalsStory.ink + THEN("get should return the whole string") + { + REQUIRE(*globStore->get("concat") == std::string{"Foo:30"}); + } + } } WHEN ("name or type not exist") { diff --git a/inkcpp_test/ink/GlobalStory.ink b/inkcpp_test/ink/GlobalStory.ink index 38b7814f..ace5657e 100644 --- a/inkcpp_test/ink/GlobalStory.ink +++ b/inkcpp_test/ink/GlobalStory.ink @@ -2,5 +2,8 @@ VAR friendly_name_of_player = "Jackie" VAR age = 23 +VAR concat = "Foo:" My name is Jean Passepartout, but my friend's call me {friendly_name_of_player}. I'm {age} years old. +~ concat += age +{concat} From f9dba1c48990c0533d8653055584854d889a8301 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sun, 14 Feb 2021 17:09:23 +0100 Subject: [PATCH 11/12] Add functionality to finalize string values --- inkcpp/globals_impl.cpp | 7 ++--- inkcpp/globals_impl.h | 2 +- inkcpp/output.cpp | 49 +++++------------------------ inkcpp/string_utils.h | 68 +++++++++++++++++++++++++++++++++++++++++ inkcpp/value.cpp | 55 ++++++++++++++++++++++++++++++++- inkcpp/value.h | 18 ++++++----- inkcpp_test/Globals.cpp | 9 ++---- 7 files changed, 147 insertions(+), 61 deletions(-) create mode 100644 inkcpp/string_utils.h diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index f6907df6..c7dc5920 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -116,10 +116,9 @@ namespace ink::runtime::internal } const char * const * globals_impl::get_str(hash_t name) const { - return fetch_variable( - get_variable(name), - data_type::allocated_string_pointer, - data_type::string_table_pointer); + const value* v = get_variable(name); + if (v->type() != value_type::string) { return nullptr; } + return v->as_str_ptr(_strings); } bool globals_impl::set_str(hash_t name, const char* val) { value* v = get_variable(name); diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index eb14063e..bf6ce785 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -88,7 +88,7 @@ namespace ink::runtime::internal runner_entry* _runners_start; // Allocated string table (shared by all runners using this global store) - string_table _strings; + mutable string_table _strings; // Global variables (TODO: Max 50?) // Implemented as a stack (slow lookup) because it has save/restore functionality. diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 4dd02de1..54742edc 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -1,56 +1,21 @@ #include "output.h" #include "string_table.h" +#include +#include "string_utils.h" #ifdef INK_ENABLE_STL #include #endif -#include -#include -#include - namespace ink { namespace runtime { namespace internal { - template - int toStr(char * buffer, size_t size, T value) { - static_assert(!std::is_same::value, "Type not supported for conversion!"); - return EINVAL; - } - - // error behavior from: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/itoa-s-itow-s?view=msvc-160 - template<> - int toStr(char * buffer, size_t size, int value) { -#ifdef WIN32 - return _itoa_s(value, buffer, size, 10); -#else - if ( buffer == nullptr || size < 1 ){ return EINVAL; } - int res = snprintf(buffer, size, "%d", value); - if (res > 0 && res < size) { return 0; } - return EINVAL; -#endif - } - - // error behavior from: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/gcvt-s?view=msvc-160 - template<> - int toStr(char * buffer, size_t size, float value) { -#ifdef WIN32 - return _gcvt_s(buffer, size, value, 11); -#else - if ( buffer == nullptr || size < 1 ) { return EINVAL; } - int res = snprintf(buffer, size, "%f.10", value); - if (res > 0 && res < size) { return 0; } - return EINVAL; -#endif - } basic_stream::basic_stream(data* buffer, size_t len) : _data(buffer), _max(len), _size(0), _save(~0) - { - - } + {} void basic_stream::append(const data& in) { @@ -62,7 +27,7 @@ namespace ink if (_data[_size - 1].type == data_type::func_start) return; } - + // Ignore leading newlines if (in.type == data_type::newline && _size == 0) return; @@ -114,7 +79,7 @@ namespace ink template inline void write_char(OUT& output, char c) { - static_assert(! std::is_same::value, "Invalid output type"); + static_assert(always_false::value, "Invalid output type"); } template<> @@ -421,10 +386,10 @@ namespace ink switch (_data[i].type) { case data_type::int32: - length += 11; + length += decimal_digits(_data[i].integer_value); break; case data_type::float32: - length += 11; // ??? + length += decimal_digits(_data[i].float_value); break; case data_type::string_table_pointer: case data_type::allocated_string_pointer: diff --git a/inkcpp/string_utils.h b/inkcpp/string_utils.h new file mode 100644 index 00000000..cd6956fc --- /dev/null +++ b/inkcpp/string_utils.h @@ -0,0 +1,68 @@ +#pragma once + +#include "system.h" + +#include + +namespace ink::runtime::internal { + + // error behavior from: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/itoa-s-itow-s?view=msvc-160 + inline int toStr(char * buffer, size_t size, uint32_t value) { +#ifdef WIN32 + return _itoa_s(value, buffer, size, 10); +#else + if ( buffer == nullptr || size < 1 ){ return EINVAL; } + int res = snprintf(buffer, size, "%d", value); + if (res > 0 && res < size) { return 0; } + return EINVAL; +#endif + } + + // error behavior from: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/itoa-s-itow-s?view=msvc-160 + inline int toStr(char * buffer, size_t size, int32_t value) { +#ifdef WIN32 + return _itoa_s(value, buffer, size, 10); +#else + if ( buffer == nullptr || size < 1 ){ return EINVAL; } + int res = snprintf(buffer, size, "%d", value); + if (res > 0 && res < size) { return 0; } + return EINVAL; +#endif + } + + // error behavior from: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/gcvt-s?view=msvc-160 + inline int toStr(char * buffer, size_t size, float value) { +#ifdef WIN32 + return _gcvt_s(buffer, size, value, 7); // number of significant digits +#else + if ( buffer == nullptr || size < 1 ) { return EINVAL; } + int res = snprintf(buffer, size, "%f.7", value); + if (res > 0 && res < size) { return 0; } + return EINVAL; +#endif + } + inline size_t strlen(const char* str) { + size_t len = 0; + for(const char* c = str; *c; ++c) { + ++len; + } + return len; + } + + // return a upper bound for the string representation of the number + inline constexpr size_t decimal_digits(uint32_t number) { + size_t length = 1; + while(number /= 10) { ++length; } + return length; + } + + inline constexpr size_t decimal_digits(int32_t number) { + size_t length = number < 0 ? 2 : 1; + while(number /= 10) { ++length; } + return length; + } + + inline constexpr size_t decimal_digits(float number) { + return 16; + } +} diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index ba8099ba..6da899c5 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -1,6 +1,7 @@ #include "value.h" #include "output.h" #include "string_table.h" +#include "string_utils.h" namespace ink { @@ -324,7 +325,59 @@ namespace ink } } - + void value::finalize_string(string_table& table) const { + constexpr size_t MaxSize = 256; // max size for no string element + char buffer[VALUE_DATA_LENGTH][MaxSize]; + const char* strs[VALUE_DATA_LENGTH]; + char null = 0; + + size_t len = 0; + for (int i = 0; i < VALUE_DATA_LENGTH; ++i) { + switch(_data[i].type) { + case data_type::float32: + strs[i] = buffer[i]; + toStr(buffer[i], MaxSize, _data[i].float_value); + break; + case data_type::uint32: + strs[i] = buffer[i]; + toStr(buffer[i], MaxSize, _data[i].uint_value); + break; + case data_type::int32: + strs[i] = buffer[i]; + toStr(buffer[i], MaxSize, _data[i].integer_value); + break; + case data_type::string_table_pointer: + case data_type::allocated_string_pointer: + strs[i] = _data[i].string_val; + break; + default: strs[i] = &null; + } + _data[i].set_none(); + len += strlen(strs[i]); + } + char* str = table.create(len+1); + table.mark_used(str); + + char* ptr = str; + for (int i = 0; i < VALUE_DATA_LENGTH; ++i) { + for(const char* c = strs[i]; *c; ++c){ + *ptr++ = *c; + } + } + *ptr = 0; + _data[0].set_string(str, true); + } + + const char* value::as_str(string_table& table) const { + finalize_string(table); + return _first.string_val; + } + + const char * const * value::as_str_ptr(string_table& table) const { + finalize_string(table); + return &_first.string_val; + } + bool value::compare_string(const value& left, const value& right) { // convert fields to string representation and start comparison // when the end of one field is reached, the other still has diff --git a/inkcpp/value.h b/inkcpp/value.h index 86637054..bad80088 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -106,8 +106,8 @@ namespace ink uint32_t* as_uint_ptr() { return &_first.uint_value; } const uint32_t* as_uint_ptr() const { return &_first.uint_value; } // FIXME: we should compress the value before - const char* as_str() const { return _first.string_val; } - const char* const * as_str_ptr() const { return &_first.string_val; } + const char* as_str(string_table&) const; + const char* const * as_str_ptr(string_table&) const; template T get() const { static_assert(always_false::value, "Type not supported by value class"); } @@ -153,6 +153,10 @@ namespace ink * @brief compare if the string representation of values are equal. */ static bool compare_string(const value& left, const value& right); + /** + * @brief compress string values to one data field + */ + void finalize_string(string_table&) const; private: // Maximum sequential data a value can have @@ -161,16 +165,16 @@ namespace ink union { // Quick access struct - struct + struct { - data _first; + data _first; data _second; }; - + // Data array - data _data[VALUE_DATA_LENGTH]; + mutable data _data[VALUE_DATA_LENGTH]; }; - + }; // == Binary Operators == diff --git a/inkcpp_test/Globals.cpp b/inkcpp_test/Globals.cpp index 841ddf19..15eb86d7 100644 --- a/inkcpp_test/Globals.cpp +++ b/inkcpp_test/Globals.cpp @@ -15,11 +15,11 @@ SCENARIO("run story with global variable", "[global variables]") inklecate("ink/GlobalStory.ink", "GlobalsStory.tmp"); ink::compiler::run("GlobalsStory.tmp", "GlobalsStory.bin"); auto ink = story::from_file("GlobalsStory.bin"); + globals globStore = ink->new_globals(); + runner thread = ink->new_runner(globStore); WHEN( "just runs") { - globals globStore = ink->new_globals(); - runner thread = ink->new_runner(globStore); THEN("variables should contain values as in inkScript") { REQUIRE(thread->getall() == "My name is Jean Passepartout, but my friend's call me Jackie. I'm 23 years old.\nFoo:23\n"); @@ -29,8 +29,6 @@ SCENARIO("run story with global variable", "[global variables]") } WHEN ("edit number") { - globals globStore = ink->new_globals(); - runner thread = ink->new_runner(globStore); bool resi = globStore->set("age", 30); bool resc @@ -49,6 +47,7 @@ SCENARIO("run story with global variable", "[global variables]") WHEN ("something added to string") { // concat in GlobalsStory.ink + thread->getall(); THEN("get should return the whole string") { REQUIRE(*globStore->get("concat") == std::string{"Foo:30"}); @@ -57,8 +56,6 @@ SCENARIO("run story with global variable", "[global variables]") } WHEN ("name or type not exist") { - globals globStore = ink->new_globals(); - runner thread = ink->new_runner(globStore); auto wrongType = globStore->get("age"); auto notExistingName = globStore->get("foo"); THEN("should return nullptr") From f0fced36144786f26363591743694432e9d56875 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 17 Feb 2021 11:14:54 +0100 Subject: [PATCH 12/12] Remove outdated FIXME --- inkcpp/value.h | 1 - 1 file changed, 1 deletion(-) diff --git a/inkcpp/value.h b/inkcpp/value.h index bad80088..0bc32ae0 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -105,7 +105,6 @@ namespace ink uint32_t as_thread_id() const { return _first.uint_value; } uint32_t* as_uint_ptr() { return &_first.uint_value; } const uint32_t* as_uint_ptr() const { return &_first.uint_value; } - // FIXME: we should compress the value before const char* as_str(string_table&) const; const char* const * as_str_ptr(string_table&) const;