From 0d9756f9407f717b9c0bcecb081b66176db79eda Mon Sep 17 00:00:00 2001 From: JBenda Date: Sun, 17 Jul 2022 17:22:48 +0200 Subject: [PATCH 01/67] Make Unreal plugin compiable --- inkcpp/CMakeLists.txt | 17 +++- inkcpp/array.h | 4 +- inkcpp/avl_array.h | 4 +- inkcpp/collections/restorable.h | 2 +- inkcpp/container_operations.cpp | 14 ++-- inkcpp/functional.cpp | 24 ++++-- inkcpp/include/functional.h | 8 +- inkcpp/include/runner.h | 2 + inkcpp/list_operations.cpp | 32 ++++---- inkcpp/list_table.cpp | 26 +++--- inkcpp/list_table.h | 4 +- inkcpp/numeric_operations.cpp | 4 +- inkcpp/numeric_operations.h | 7 +- inkcpp/operations.h | 2 +- inkcpp/output.cpp | 79 ++++--------------- inkcpp/output.h | 12 +-- inkcpp/random.h | 2 +- inkcpp/runner_impl.cpp | 31 ++++---- inkcpp/simple_restorable_stack.h | 2 + inkcpp/stack.cpp | 2 +- inkcpp/stack.h | 4 + inkcpp/string_utils.h | 2 +- inkcpp/tuple.hpp | 2 +- inkcpp/value.h | 6 +- inkcpp_compiler/binary_emitter.cpp | 4 +- inkcpp_compiler/json_compiler.cpp | 12 +-- inkcpp_compiler/list_data.cpp | 3 +- inkcpp_compiler/list_data.h | 6 +- shared/public/system.h | 13 +-- .../inkcpp/Source/inkcpp/Private/Choice.cpp | 3 +- .../inkcpp/Source/inkcpp/Private/InkAsset.cpp | 2 +- .../inkcpp/Source/inkcpp/Private/InkVar.cpp | 2 +- unreal/inkcpp/Source/inkcpp/Public/Choice.h | 6 +- unreal/inkcpp/Source/inkcpp/Public/InkAsset.h | 2 +- .../inkcpp/Source/inkcpp/Public/InkRuntime.h | 14 ++-- .../inkcpp/Source/inkcpp/Public/InkThread.h | 28 +++---- unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs | 4 +- 37 files changed, 191 insertions(+), 200 deletions(-) diff --git a/inkcpp/CMakeLists.txt b/inkcpp/CMakeLists.txt index 876b6b18..eec79406 100644 --- a/inkcpp/CMakeLists.txt +++ b/inkcpp/CMakeLists.txt @@ -1,8 +1,6 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) list(APPEND SOURCES - collections/restorable.h - collections/restorable.cpp array.h choice.cpp functional.cpp @@ -16,16 +14,28 @@ list(APPEND SOURCES story_ptr.cpp system.cpp value.h value.cpp + tuple.hpp string_table.h string_table.cpp avl_array.h list_table.h list_table.cpp + operations.h operation_bases.h list_operations.h list_operations.cpp container_operations.h container_operations.cpp + numeric_operations.h numeric_operations.cpp + string_operations.h string_operations.cpp string_operations.cpp numeric_operations.cpp + casting.h + executioner.h + string_utils.h header.cpp + random.h +) +list(APPEND COLLECTION_SOURCES + collections/restorable.h + collections/restorable.cpp ) source_group(Collections REGULAR_EXPRESSION collections/.*) -add_library(inkcpp ${SOURCES}) +add_library(inkcpp ${SOURCES} ${COLLECTION_SOURCES}) target_include_directories(inkcpp PUBLIC $ $ @@ -42,3 +52,4 @@ target_compile_features(inkcpp PUBLIC cxx_std_17) # Unreal installation install(DIRECTORY "include/" DESTINATION "inkcpp/Source/inkcpp/Public/ink/" COMPONENT unreal EXCLUDE_FROM_ALL) install(FILES ${SOURCES} DESTINATION "inkcpp/Source/inkcpp/Private/ink/" COMPONENT unreal EXCLUDE_FROM_ALL) +install(FILES ${COLLECTION_SOURCES} DESTINATION "inkcpp/Source/inkcpp/Private/ink/collections/" COMPONENT unreal EXCLUDE_FROM_ALL) diff --git a/inkcpp/array.h b/inkcpp/array.h index 9ad97afb..499d3816 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -42,13 +42,13 @@ namespace ink::runtime::internal if constexpr (dynamic) { if (_size == _capacity) { extend(); } } else { - ink_assert(_size <= _capacity, "Stack Overflow!"); + inkAssert(_size <= _capacity, "Stack Overflow!"); } return data()[_size++]; } void clear() { _size = 0; } void resize(size_t size) { - ink_assert(size <= _size, "Only allow to reduce size"); + inkAssert(size <= _size, "Only allow to reduce size"); _size = size; } diff --git a/inkcpp/avl_array.h b/inkcpp/avl_array.h index 9690149a..17638112 100644 --- a/inkcpp/avl_array.h +++ b/inkcpp/avl_array.h @@ -481,10 +481,10 @@ class avl_array /** - * Integrity (self) check + * Integrity (self) test * \return True if the tree intergity is correct, false if error (should not happen normally) */ - bool check() const + bool test() const { // check root if (empty() && (root_ != INVALID_IDX)) { diff --git a/inkcpp/collections/restorable.h b/inkcpp/collections/restorable.h index ad64175e..598a02c4 100644 --- a/inkcpp/collections/restorable.h +++ b/inkcpp/collections/restorable.h @@ -72,7 +72,7 @@ namespace ink::runtime::internal { public: restorable(ElementType* buffer, size_t size) - : _buffer(buffer), _size(size), _pos(0), _save(~0), _jump(~0) + : _buffer(buffer), _size(size), _pos(0), _jump(~0), _save(~0) { } // Checks if we have a save state diff --git a/inkcpp/container_operations.cpp b/inkcpp/container_operations.cpp index c34b681c..79de3f93 100644 --- a/inkcpp/container_operations.cpp +++ b/inkcpp/container_operations.cpp @@ -13,9 +13,10 @@ namespace ink::runtime::internal { basic_eval_stack& stack, value* vals) { container_t id; - inkAssert(_story.get_container_id( - _story.instructions() + vals[0].get(), - id), "failed to find container to read visit count!"); + bool success = _story.get_container_id( + _story.instructions() + vals[0].get(), + id); + inkAssert(success, "failed to find container to read visit count!"); stack.push(value{}.set( static_cast(_visit_counts.visits( id ) ))); @@ -25,9 +26,10 @@ namespace ink::runtime::internal { basic_eval_stack& stack, value* vals) { container_t id; - inkAssert(_story.get_container_id( - _story.instructions() + vals[0].get(), - id), "failed to find container to read turn count!"); + bool success = _story.get_container_id( + _story.instructions() + vals[0].get(), + id); + inkAssert(success, "failed to find container to read turn count!"); stack.push(value{}.set( static_cast(_visit_counts.turns(id) ))); diff --git a/inkcpp/functional.cpp b/inkcpp/functional.cpp index 9c0cad62..7deec6fa 100644 --- a/inkcpp/functional.cpp +++ b/inkcpp/functional.cpp @@ -51,7 +51,10 @@ namespace ink::runtime::internal } #endif #ifdef INK_ENABLE_UNREAL - SUPPORT_TYPE_PARAMETER_ONLY(FString); + template<> + FString function_base::pop(basic_eval_stack* stack) { + return FString(pop(stack)); + } template<> FInkVar function_base::pop(basic_eval_stack* stack) @@ -63,12 +66,17 @@ namespace ink::runtime::internal case value_type::divert: inkFail("Trying to pass null or divert as ink parameter to external function"); break; - case value_type::integer: - return FInkVar(v.get()); - case value_type::decimal: - return FInkVar(v.get()); + case value_type::int32: + return FInkVar(v.get()); + case value_type::uint32: { + uint32_t n = v.get(); + inkAssert(n < (~(1<<31)), "Value to large to cast without overlfow to int!"); + return FInkVar(static_cast(n)); + } + case value_type::float32: + return FInkVar(v.get()); case value_type::string: - return FInkVar(v.get()); + return FInkVar(FString(v.get().str)); } return FInkVar(); @@ -86,10 +94,10 @@ namespace ink::runtime::internal } break; case EInkVarType::Int: - stack->push(value.intVar); + stack->push(internal::value{}.set(value.intVar)); break; case EInkVarType::Float: - stack->push(value.floatVar); + stack->push(internal::value{}.set(value.floatVar)); break; case EInkVarType::String: inkFail("NOT IMPLEMENTED"); // TODO: String support diff --git a/inkcpp/include/functional.h b/inkcpp/include/functional.h index a5461b9b..a0648993 100644 --- a/inkcpp/include/functional.h +++ b/inkcpp/include/functional.h @@ -15,7 +15,11 @@ namespace ink::runtime::internal virtual ~function_base() { } // calls the underlying function object taking parameters from a stack - virtual void call(basic_eval_stack* stack, ink::size_t length, string_table& strings) = 0; +#ifdef INK_ENABLE_UNREAL + virtual void call(basic_eval_stack* stack, size_t length, string_table& strings) = 0; +#else + virtual void call(basic_eval_stack* stack, size_t length, string_table& strings) = 0; +#endif protected: // used to hide basic_eval_stack and value definitions @@ -122,7 +126,7 @@ namespace ink::runtime::internal function_array_delegate(const D& del) : invocableDelegate(del) { } // calls the underlying delegate using arguments on the stack - virtual void call(basic_eval_stack* stack, size_t length) override + virtual void call(basic_eval_stack* stack, size_t length, string_table&) override { // Create variable array TArray variables; diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index 737553de..a02da364 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -56,6 +56,7 @@ namespace ink::runtime */ virtual bool can_continue() const = 0; +#ifdef INK_ENABLE_CSTD /** * Continue execution until the next newline, then allocate a c-style * string with the output. This allocated string is now the callers @@ -64,6 +65,7 @@ namespace ink::runtime * @return allocated c-style string with the output of a single line of execution */ virtual char* getline_alloc() = 0; +#endif #ifdef INK_ENABLE_STL /** diff --git a/inkcpp/list_operations.cpp b/inkcpp/list_operations.cpp index 252a7244..eac2bbb8 100644 --- a/inkcpp/list_operations.cpp +++ b/inkcpp/list_operations.cpp @@ -23,7 +23,7 @@ ) \ )); \ } else {\ - inkAssert(vals[1].type()==value_type::list_flag);\ + inkAssert(vals[1].type()==value_type::list_flag, "list operation was called but second argument is not a list or list_flag");\ stack.push(value{}.set( \ _list_table.FUN( \ vals[0].get(), \ @@ -32,7 +32,7 @@ )); \ } \ } else { \ - inkAssert(vals[0].type() == value_type::list); \ + inkAssert(vals[0].type() == value_type::list, "list operation was called but first argument is not a list or a list_flag!"); \ if(vals[1].type() == value_type::list) { \ stack.push(value{}.set( \ _list_table.FUN( \ @@ -41,7 +41,7 @@ ) \ )); \ } else {\ - inkAssert(vals[1].type()==value_type::list_flag);\ + inkAssert(vals[1].type()==value_type::list_flag, "list operation was called but second argument ist not a list or list_flag!");\ stack.push(value{}.set( \ _list_table.FUN( \ vals[0].get(), \ @@ -76,8 +76,8 @@ namespace ink::runtime::internal { { int i = vals[1].type() == value_type::int32 ? vals[1].get() - : vals[1].get(); - inkAssert(vals[0].type() == value_type::list); + : static_cast(vals[1].get()); + inkAssert(vals[0].type() == value_type::list, "try to use list add function but value is not of type list"); stack.push(value{}.set( _list_table.add(vals[0].get(), i) )); @@ -88,12 +88,13 @@ namespace ink::runtime::internal { void operation::operator()( basic_eval_stack& stack, value* vals) { - inkAssert(vals[0].type() == value_type::list_flag); + inkAssert(vals[0].type() == value_type::list_flag, "try to use add function with list_flag results but first argument is not a list_flag!"); inkAssert(vals[1].type() == value_type::int32 - || vals[1].type() == value_type::uint32); + || vals[1].type() == value_type::uint32, + "try modify a list flag with a non intiger type!"); int i = vals[1].type() == value_type::int32 ? vals[1].get() - : vals[1].get(); + : static_cast(vals[1].get()); stack.push(value{}.set( _list_table.add(vals[0].get(), i) )); @@ -108,7 +109,7 @@ namespace ink::runtime::internal { int i = vals[1].type() == value_type::int32 ? vals[1].get() : vals[1].get(); - inkAssert(vals[0].type() == value_type::list); + inkAssert(vals[0].type() == value_type::list, "A in list resulting subtraction needs at leas one list as argument!"); stack.push(value{}.set( _list_table.sub(vals[0].get(), i) )); @@ -119,9 +120,10 @@ namespace ink::runtime::internal { void operation::operator()( basic_eval_stack& stack, value* vals) { - inkAssert(vals[0].type() == value_type::list_flag); + inkAssert(vals[0].type() == value_type::list_flag, "subtraction resulting in list_flag needs a list_flag as first arguments!"); inkAssert(vals[1].type() == value_type::int32 - || vals[1].type() == value_type::uint32); + || vals[1].type() == value_type::uint32, + "Try to subtract non integer value from list_flag."); int i = vals[1].type() == value_type::int32 ? vals[1].get() : vals[1].get(); @@ -170,8 +172,8 @@ namespace ink::runtime::internal { void operation::operator()( basic_eval_stack& stack, value* vals) { - inkAssert(vals[0].type() == value_type::string); - inkAssert(vals[1].type() == value_type::int32); + inkAssert(vals[0].type() == value_type::string, "list_flag construction needs the list name as string as first argument!"); + inkAssert(vals[1].type() == value_type::int32, "list_flag construction needs the flag numeric value as second argument!"); list_flag entry = _list_table.get_list_id(vals[0].get()); entry.flag = vals[1].get() - 1; stack.push(value{}.set(entry)); @@ -181,14 +183,14 @@ namespace ink::runtime::internal { if(val.type() == value_type::int32) { return val.get() - 1; } else { - inkAssert(val.type() == value_type::list_flag); + inkAssert(val.type() == value_type::list_flag, "flag value must be a integer or a list_flag"); return val.get().flag; } } void operation::operator()( basic_eval_stack& stack, value* vals) { - inkAssert(vals[0].type() == value_type::list); + inkAssert(vals[0].type() == value_type::list, "Can't get range of non list type!"); stack.push(value{}.set(_list_table.range( vals[0].get(), get_limit(vals[1]), diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index 06b57aa8..97e80d38 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -52,7 +52,7 @@ namespace ink::runtime::internal list_table::list list_table::create() { - for(int i = 0; i < _entry_state.size(); ++i) { + for(size_t i = 0; i < _entry_state.size(); ++i) { if (_entry_state[i] == state::empty) { _entry_state[i] = state::used; return list(i); @@ -82,7 +82,7 @@ namespace ink::runtime::internal } void list_table::gc() { - for(int i = 0; i < _entry_state.size(); ++i) { + for(size_t i = 0; i < _entry_state.size(); ++i) { if (_entry_state[i] == state::unused) { _entry_state[i] = state::empty; data_t* entry = getPtr(i); @@ -299,10 +299,10 @@ namespace ink::runtime::internal } - list_table::list list_table::add(list arg, int i) { + list_table::list list_table::add(list arg, int n) { // TODO: handle i == 0 (for performance only) - if (i < 0) { - return sub(arg, -i); + if (n < 0) { + return sub(arg, -n); } list res = create(); data_t* l = getPtr(arg.lid); @@ -314,7 +314,7 @@ namespace ink::runtime::internal for(int j = listBegin(i); j < _list_end[i] - i;++j) { if(hasFlag(l, j)) { - setFlag(o,j+i); + setFlag(o,j+n); has_flag = true; } } @@ -339,10 +339,10 @@ namespace ink::runtime::internal return arg; } - list_table::list list_table::sub(list arg, int i) { + list_table::list list_table::sub(list arg, int n) { // TODO: handle i == 0 (for perofrgmance only) - if(i < 0) { - return add(arg, -i); + if(n < 0) { + return add(arg, -n); } list res = create(); data_t* l = getPtr(arg.lid); @@ -354,7 +354,7 @@ namespace ink::runtime::internal for(int j = listBegin(i) + i; j < _list_end[i]; ++j) { if(hasFlag(l,j)) { - setFlag(o,j-i); + setFlag(o,j-n); has_flag = true; } } @@ -554,14 +554,14 @@ namespace ink::runtime::internal list_flag list_table::lrnd(list lh, prng& rng) const { const data_t* l = getPtr(lh.lid); - int i = count(lh); - rng.rand(i); + int n = count(lh); + n = rng.rand(n); int count = 0; for(int i = 0; i < numLists(); ++i) { if(hasList(l, i)) { for(int j = listBegin(i); j < _list_end[i]; ++j) { if(hasFlag(l,j)) { - if(count++ == i) { + if(count++ == n) { return list_flag{ static_cast(i), static_cast( j - listBegin(i) ) diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index 3ebd5497..2e97135b 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -8,7 +8,7 @@ #endif namespace ink::internal { - class header; + struct header; } namespace ink::runtime::internal { @@ -215,7 +215,7 @@ namespace ink::runtime::internal abs(config::maxListTypes) + abs(config::maxFlags), sizeof(data_t) - ) * abs(config::maxLists); + ) * static_cast(abs(config::maxLists)); int _entrySize; ///< entry size in data_t // entries (created lists) diff --git a/inkcpp/numeric_operations.cpp b/inkcpp/numeric_operations.cpp index 16f67ae0..6d17c57c 100644 --- a/inkcpp/numeric_operations.cpp +++ b/inkcpp/numeric_operations.cpp @@ -19,7 +19,7 @@ namespace ink::runtime::internal { void operation::operator()( basic_eval_stack& stack, value* vals) { - inkAssert(vals[0].type() == value_type::float32); + inkAssert(vals[0].type() == value_type::float32, "Expected floating point number to floor."); stack.push(value{}.set( floor(vals->get()))); } @@ -27,7 +27,7 @@ namespace ink::runtime::internal { void operation::operator()( basic_eval_stack& stack, value* vals) { - inkAssert(vals[0].type() == value_type::float32); + inkAssert(vals[0].type() == value_type::float32, "Expected floating point number to ceil."); stack.push(value{}.set( ceil(vals->get()))); } diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index 50c5f05a..9d87fa29 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -14,6 +14,11 @@ namespace ink::runtime::internal { || ty == value_type::int32 || ty == value_type::uint32 || ty == value_type::float32, void>::type; + + template + using is_signed_numeric_t = typename enable_if< + ty == value_type::int32 + || ty == value_type::float32, void>::type; /// list of internal value types /// produces a SFINAE error if type is not part of list @@ -369,7 +374,7 @@ namespace ink::runtime::internal { }; template - class operation> : public operation_base { + class operation> : public operation_base { public: using operation_base::operation_base; void operator()(basic_eval_stack& stack, value* vals) { diff --git a/inkcpp/operations.h b/inkcpp/operations.h index 98339ffe..9e79a711 100644 --- a/inkcpp/operations.h +++ b/inkcpp/operations.h @@ -2,7 +2,7 @@ /// Define base constructs to specify by operation headers. -#include "../shared/private/command.h" +#include "command.h" namespace ink::runtime::internal { diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 559de9ff..21f75bd9 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -77,10 +77,10 @@ namespace ink append(in[i]); } - template - inline void write_char(OUT& output, char c) + template + inline void write_char(T& output, char c) { - static_assert(always_false::value, "Invalid output type"); + static_assert(always_false::value, "Invalid output type"); } template<> @@ -89,11 +89,13 @@ namespace ink (*output++) = c; } +#ifdef INK_ENABLE_STL template<> inline void write_char(std::stringstream& output, char c) { output.put(c); } +#endif inline bool get_next(const value* list, size_t i, size_t size, const value** next) { @@ -108,8 +110,8 @@ namespace ink return false; } - template - void basic_stream::copy_string(const char* str, size_t& dataIter, OUT& output) + template + void basic_stream::copy_string(const char* str, size_t& dataIter, T& output) { while(*str != 0) { write_char(output, *str++); @@ -140,55 +142,12 @@ namespace ink // Return processed string // remove mulitple accourencies of ' ' std::string result = str.str(); - auto end = clean_string(result.begin(), result.end()); + auto end = clean_string(result.begin(), result.end()); _last_char = *(end-1); result.resize(end - result.begin() - (_last_char == ' ' ? 1 : 0)); return result; } #endif -#ifdef INK_ENABLE_UNREAL - FString basic_stream::get() - { - size_t start = find_start(); - - // TODO: Slow! FString concatonation. - // Is there really no equivilent of stringstream in Unreal? Some kind of String Builder? - - // Move up from marker - bool hasGlue = false; - FString str; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue)) - continue; - - switch (_data[i].type) - { - case value_type::int32: - str += FString::Printf(TEXT("%d"), _data[i].integer_value); - break; - case value_type::float32: - // TODO: Whitespace cleaning - str += FString::Printf(TEXT("%f"), _data[i].float_value); - break; - case value_type::string: - str += _data[i].string_val; - break; - case data_type::newline: - str += "\n"; - break; - default: - break; - } - } - - // Reset stream size to where we last held the marker - _size = start; - - // Return processed string - return str; - } -#endif int basic_stream::queued() const { @@ -367,14 +326,13 @@ namespace ink _size = start; // Return processed string - { - auto end = clean_string(buffer, buffer+length); - *end = 0; - _last_char = end[-1]; - if constexpr (RemoveTail) { - if (_last_char == ' ') { end[-1] = 0; } - } + end = clean_string(buffer, buffer+length); + *end = 0; + _last_char = end[-1]; + if constexpr (RemoveTail) { + if (_last_char == ' ') { end[-1] = 0; } } + return buffer; } @@ -469,7 +427,7 @@ namespace ink void basic_stream::mark_strings(string_table& strings) const { // Find all allocated strings and mark them as used - for (int i = 0; i < _size; i++) + for (size_t i = 0; i < _size; i++) { if (_data[i].type() == value_type::string) { string_type str = _data[i].get(); @@ -493,13 +451,6 @@ namespace ink return in; } #endif -#ifdef INK_ENABLE_UNREAL - basic_stream& operator>>(basic_stream& in, FString& out) - { - out = in.get(); - return in; - } -#endif } diff --git a/inkcpp/output.h b/inkcpp/output.h index 60663f8e..3938763b 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -55,10 +55,6 @@ namespace ink std::string get(); #else // will conflict with stl definition -# ifdef INK_ENABLE_UNREAL - // Extract into a string - FString get(); -# endif #endif // Check if the stream is empty @@ -103,8 +99,8 @@ namespace ink size_t find_start() const; bool should_skip(size_t iter, bool& hasGlue, bool& lastNewline) const; - template - void copy_string(const char* str, size_t& dataIter, OUT& output); + template + void copy_string(const char* str, size_t& dataIter, T& output); private: char _last_char; @@ -126,9 +122,7 @@ namespace ink std::ostream& operator <<(std::ostream&, basic_stream&); basic_stream& operator >>(basic_stream&, std::string&); #endif -#ifdef INK_ENABLE_UNREAL - basic_stream& operator >>(basic_stream&, FString&); -#endif + template class stream : public basic_stream diff --git a/inkcpp/random.h b/inkcpp/random.h index 995b7947..ab6aff18 100644 --- a/inkcpp/random.h +++ b/inkcpp/random.h @@ -1,6 +1,6 @@ #pragma once -#include "../shared/public/system.h" +#include "system.h" namespace ink::runtime::internal { /** diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index ee51511a..4b2a665a 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -246,13 +246,13 @@ namespace ink::runtime::internal // Iterate over the container stack marking any _new_ entries as "visited" if (record_visits) { - const container_t* iter; + const container_t* con_iter; size_t num_new = _container.size() - pos; - while (_container.iter(iter)) + while (_container.iter(con_iter)) { if (num_new <= 0) break; - _globals->visit(*iter); + _globals->visit(*con_iter); --num_new; } } @@ -316,7 +316,7 @@ namespace ink::runtime::internal } runner_impl::runner_impl(const story_impl* data, globals global) - : _story(data), _globals(global.cast()), _container(~0), + : _story(data), _globals(global.cast()), _operations( global.cast()->strings(), global.cast()->lists(), @@ -324,7 +324,7 @@ namespace ink::runtime::internal *global.cast(), *data, static_cast(*this)), - _backup(nullptr), _done(nullptr), _choices() + _backup(nullptr), _done(nullptr), _choices(), _container(~0) { _ptr = _story->instructions(); bEvaluationMode = false; @@ -430,12 +430,9 @@ namespace ink::runtime::internal FString runner_impl::getline() { inkAssert(false, "Fix (see getline for std)"); - // Advance interpreter one line - advance_line(); // Read line into std::string FString result; - _output >> result; // Return result inkAssert(_output.is_empty(), "Output should be empty after getline!"); @@ -527,12 +524,12 @@ namespace ink::runtime::internal #ifdef INK_ENABLE_CSTD char* runner_impl::getline_alloc() - { - // TODO + { + /// TODO + inkAssert(false, "Not implemented yet!"); return nullptr; - -#endif } +#endif bool runner_impl::move_to(hash_t path) { @@ -641,7 +638,9 @@ namespace ink::runtime::internal void runner_impl::step() { +#ifndef INK_ENABLE_UNREAL try +#endif { inkAssert(_ptr != nullptr, "Can not step! Do not have a valid pointer"); @@ -830,7 +829,7 @@ namespace ink::runtime::internal if(flag & CommandFlag::TUNNEL_TO_VARIABLE) { hash_t var_name = read(); const value* val = get_var(var_name); - inkAssert(val != nullptr); + inkAssert(val != nullptr, "Variable containing tunnel target could not be found!"); target = val->get(); } else { target = read(); @@ -845,7 +844,7 @@ namespace ink::runtime::internal if(flag & CommandFlag::FUNCTION_TO_VARIABLE) { hash_t var_name = read(); const value* val = get_var(var_name); - inkAssert(val != nullptr); + inkAssert(val != nullptr, "Varibale containing function could not be found!"); target = val->get(); } else { target = read(); @@ -1131,12 +1130,14 @@ namespace ink::runtime::internal break; } } +#ifndef INK_ENABLE_UNREAL catch (...) { // Reset our whole state as it's probably corrupt reset(); throw; } +#endif } void runner_impl::on_done(bool setDone) @@ -1201,7 +1202,7 @@ namespace ink::runtime::internal _eval.mark_strings(strings); // Take into account choice text - for (int i = 0; i < _choices.size(); i++) + for (size_t i = 0; i < _choices.size(); i++) strings.mark_used(_choices[i]._text); } diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index bf09d633..b38a0e4c 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -11,6 +11,8 @@ namespace ink::runtime::internal public: simple_restorable_stack(T* buffer, size_t size, const T& null) : _buffer(buffer), _size(size), _null(null) { } + virtual ~simple_restorable_stack() = default; + void push(const T& value); T pop(); diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index 40c73127..44d531c8 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -76,7 +76,7 @@ namespace ink::runtime::internal }; class reverse_find_from_frame_predicat_operator { public: - reverse_find_from_frame_predicat_operator(int ci, hash_t name) : _name{name}, _ci{ci} { + reverse_find_from_frame_predicat_operator(int ci, hash_t name) : _ci{ci}, _name{name} { inkAssert(ci == -1 || ci == 0, "only support ci == -1, for now!"); } bool operator()(entry& e) { diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 147e4c54..f16533c2 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -91,6 +91,10 @@ namespace ink static const hash_t NulledHashId = ~0; }; + + template<> void basic_stack::push_frame(offset_t return_to, bool eval); + template<> void basic_stack::push_frame(offset_t return_to, bool eval); + template<> void basic_stack::push_frame(offset_t return_to, bool eval); /** * @brief stack for call history and temporary variables diff --git a/inkcpp/string_utils.h b/inkcpp/string_utils.h index 55d9fac4..db4f0b0a 100644 --- a/inkcpp/string_utils.h +++ b/inkcpp/string_utils.h @@ -126,7 +126,7 @@ namespace ink::runtime::internal { inline constexpr ITR clean_string(ITR begin, ITR end) { auto dst = begin; for(auto src = begin; src != end; ++src){ - if (src == begin) { + if (dst == begin) { if (LEADING_SPACES && (src[0] == ' ' || src[0] == '\n')) { continue; } } else if(src[-1] == '\n' && (src[0] == ' ' || src[0] == '\n')) { continue;} diff --git a/inkcpp/tuple.hpp b/inkcpp/tuple.hpp index 528c8d47..eb4f99a5 100644 --- a/inkcpp/tuple.hpp +++ b/inkcpp/tuple.hpp @@ -2,7 +2,7 @@ /// very basic flat tuple implementation, only use for trivial data types. -#include "./include/traits.h" +#include "traits.h" namespace ink::runtime::internal { namespace tuple_internal { diff --git a/inkcpp/value.h b/inkcpp/value.h index 88382f5a..7ae99ce7 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -6,7 +6,7 @@ /// define different value_types, and the mapping between type and data. #include "system.h" -#include "../shared/private/command.h" +#include "command.h" #include "list_table.h" #include "tuple.hpp" @@ -87,7 +87,7 @@ namespace ink::runtime::internal { /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; - constexpr value() : _type{value_type::none}, uint32_value{0}{} + constexpr value() : uint32_value{0}, _type{value_type::none}{} /// get value of the type (if possible) template @@ -122,7 +122,7 @@ namespace ink::runtime::internal { /// this new type template value redefine(const value& oth, T& ... env) const { - inkAssert(type() == oth.type()); + inkAssert(type() == oth.type(), "try to redefine value of other type"); return redefine(oth, {&env...}); } diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index dd163e63..fdc89240 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -171,8 +171,8 @@ namespace ink::compiler::internal void binary_emitter::write_list(Command command, CommandFlag flag, const std::vector& entries) { uint32_t id = _list_count++; - for(const list_flag& flag : entries) { - _lists.write(flag); + for(const list_flag& entry : entries) { + _lists.write(entry); } _lists.write(null_flag); write(command, id, flag); diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index d5ebe17f..87388121 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -90,14 +90,14 @@ namespace ink::compiler::internal container_t myIndex = _next_container_index++; // Make appropriate flags - CommandFlag flags = CommandFlag::NO_FLAGS; + CommandFlag cmd_flags = CommandFlag::NO_FLAGS; if (visits) - flags |= CommandFlag::CONTAINER_MARKER_TRACK_VISITS; + cmd_flags |= CommandFlag::CONTAINER_MARKER_TRACK_VISITS; if (turns) - flags |= CommandFlag::CONTAINER_MARKER_TRACK_TURNS; + cmd_flags |= CommandFlag::CONTAINER_MARKER_TRACK_TURNS; // Write command out at this position - _emitter->write(Command::START_CONTAINER_MARKER, myIndex, flags); + _emitter->write(Command::START_CONTAINER_MARKER, myIndex, cmd_flags); data.indexToReturn = myIndex; @@ -196,8 +196,8 @@ namespace ink::compiler::internal std::vector divert_positions; // Write empty divert to be patched later - uint32_t position = _emitter->fallthrough_divert(); - divert_positions.push_back(position); + uint32_t divert_position = _emitter->fallthrough_divert(); + divert_positions.push_back(divert_position); // (2) Write deffered containers for (auto& t : meta.deferred) diff --git a/inkcpp_compiler/list_data.cpp b/inkcpp_compiler/list_data.cpp index 6590932e..b4c76ca1 100644 --- a/inkcpp_compiler/list_data.cpp +++ b/inkcpp_compiler/list_data.cpp @@ -13,7 +13,7 @@ namespace ink::compiler::internal _list_end.push_back(current_back); } - void list_data::new_flag(const std::string& flag_name, int value) + void list_data::new_flag(const std::string& flag_name, size_t value) { while(_flag_names.size() < _current_list_start + value) { _flag_names.push_back(""); @@ -23,6 +23,7 @@ namespace ink::compiler::internal } _flag_names[_current_list_start + value - 1] = flag_name; } + std::vector list_data::get_flags() const { std::vector result{}; size_t begin = 0; diff --git a/inkcpp_compiler/list_data.h b/inkcpp_compiler/list_data.h index 01bbc5e7..2581320f 100644 --- a/inkcpp_compiler/list_data.h +++ b/inkcpp_compiler/list_data.h @@ -19,7 +19,7 @@ namespace ink::compiler::internal void new_list(const std::string& list_name); // add flag to current list - void new_flag(const std::string& flag_name, int value); + void new_flag(const std::string& flag_name, size_t value); lid_t get_lid(const std::string_view& list_name) { auto itr = _lists.find(list_name); @@ -37,9 +37,9 @@ namespace ink::compiler::internal } private: std::map> _lists; - std::vector _list_end; + std::vector _list_end; std::vector _list_name; - int _current_list_start = 0; + size_t _current_list_start = 0; std::vector _flag_names; }; } diff --git a/shared/public/system.h b/shared/public/system.h index 0779ac2c..184dec43 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -7,6 +7,7 @@ #include "Misc/CString.h" #include "HAL/UnrealMemory.h" #include "Hash/CityHash.h" + #endif #ifdef INK_ENABLE_STL #include @@ -175,8 +176,8 @@ namespace ink public: optional() {} optional(nullopt_t) {} - optional(T&& val) _has_value{true}, _value{std::forward(val)}{} - optional(const T& val) _has_value{true}, _value{val}{} + 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; } @@ -184,15 +185,15 @@ namespace ink 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 T& value() { test_value(); return _value; } + constexpr const T& value() const { test_value(); 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)); + return _has_value ? _value : static_cast(std::forward(u)); } private: - void check() const { + void test_value() const { if ( ! _has_value) { throw ink_exception("Can't access empty optional!"); } diff --git a/unreal/inkcpp/Source/inkcpp/Private/Choice.cpp b/unreal/inkcpp/Source/inkcpp/Private/Choice.cpp index eed7467b..43214c87 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/Choice.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/Choice.cpp @@ -1,4 +1,5 @@ -#include "Choice.h" +// name conflict with unreal include +#include "../Public/Choice.h" #include "ink/choice.h" diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkAsset.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkAsset.cpp index 53c8c291..98ee625c 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkAsset.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkAsset.cpp @@ -1,6 +1,6 @@ #include "InkAsset.h" -#include "FileHelper.h" +#include "Misc/FileHelper.h" #if WITH_EDITORONLY_DATA #include "EditorFramework/AssetImportData.h" #endif diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp index 7debdb47..5229e1f7 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp @@ -1,6 +1,6 @@ #include "InkVar.h" -#include "AssertionMacros.h" +#include "Misc/AssertionMacros.h" FString UInkVarLibrary::Conv_InkVarString(const FInkVar& InkVar) { diff --git a/unreal/inkcpp/Source/inkcpp/Public/Choice.h b/unreal/inkcpp/Source/inkcpp/Public/Choice.h index a2898b4c..b5428aba 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/Choice.h +++ b/unreal/inkcpp/Source/inkcpp/Public/Choice.h @@ -1,6 +1,6 @@ #pragma once -#include "Object.h" +#include "UObject/Object.h" #include "Choice.generated.h" @@ -11,10 +11,10 @@ class UChoice : public UObject { GENERATED_BODY() public: - UFUNCTION(BlueprintPure) + UFUNCTION(BlueprintPure, Category="Read") FString GetText() const; - UFUNCTION(BlueprintPure) + UFUNCTION(BlueprintPure, Category="Read") int GetIndex() const; protected: diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkAsset.h b/unreal/inkcpp/Source/inkcpp/Public/InkAsset.h index 812da6d6..59b272d3 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkAsset.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkAsset.h @@ -1,6 +1,6 @@ #pragma once -#include "Object.h" +#include "UObject/Object.h" #include "InkAsset.generated.h" diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index c18c212f..97f130a8 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -23,24 +23,24 @@ class INKCPP_API AInkRuntime : public AActor AInkRuntime(); ~AInkRuntime(); - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Start") UInkThread* Start(TSubclassOf type, FString path, bool runImmediately = true); - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Start") UInkThread* StartExisting(UInkThread* thread, FString path, bool runImmediately = true); - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Tags") void RegisterGlobalTagFunction(FName FunctionName, const FGlobalTagFunctionDelegate& Function); // Called from UInkThread void HandleTagFunction(UInkThread* Caller, const TArray& Params); // Marks a thread as "exclusive". As long as it is running, no other threads will update. - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Exclusive Thread") void PushExclusiveThread(UInkThread* Thread); // Removes a thread from the exclusive stack. See PushExclusiveThread. - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Exclusive Thread") void PopExclusiveThread(UInkThread* Thread); protected: @@ -52,14 +52,14 @@ class INKCPP_API AInkRuntime : public AActor virtual void Tick(float DeltaTime) override; // Story asset used in this level - UPROPERTY(EditAnywhere) + UPROPERTY(EditAnywhere, Category="Put in correct category") class UInkAsset* InkAsset; // Called by threads when they want to register an external function void ExternalFunctionRegistered(FString functionName); /* Returns the tags at the specified knot */ - UFUNCTION(BlueprintPure) + UFUNCTION(BlueprintPure, Category="Tags") UTagList* GetTagsAtPath(FString Path); private: diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h index aab55806..333a87d3 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h @@ -35,56 +35,56 @@ class INKCPP_API UInkThread : public UObject UInkThread(); // Yields the thread immediately. Will wait until Resume(). - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Controle") void Yield(); - UFUNCTION(BlueprintPure) + UFUNCTION(BlueprintPure, Category="Controle") bool IsYielding(); // Causes the thread to resume if yielded. - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Controle") void Resume(); // Kills the thread, regardless of state - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Controle") void Stop(); // Returns the runtime which owns this thread. - UFUNCTION(BlueprintPure) + UFUNCTION(BlueprintPure, Category="Setup") AInkRuntime* GetRuntime() const { return mpRuntime; } // Called before the thread begins executing - UFUNCTION(BlueprintNativeEvent) + UFUNCTION(BlueprintNativeEvent, Category="Events") void OnStartup(); // Called when the thread has printed a new line - UFUNCTION(BlueprintNativeEvent) + UFUNCTION(BlueprintNativeEvent, Category="Events") void OnLineWritten(const FString& line, UTagList* tags); // Called when a tag has been processed on the current line - UFUNCTION(BlueprintNativeEvent) + UFUNCTION(BlueprintNativeEvent, Category="Events") void OnTag(const FString& line); // Called when the thread has requested a branch - UFUNCTION(BlueprintNativeEvent) + UFUNCTION(BlueprintNativeEvent, Category="Events") void OnChoice(const TArray& choices); // Called before the thread is destroyed - UFUNCTION(BlueprintNativeEvent) + UFUNCTION(BlueprintNativeEvent, Category="Events") void OnShutdown(); - UPROPERTY(BlueprintAssignable) + UPROPERTY(BlueprintAssignable, Category="Events") FThreadShutdownDelegate OnThreadShutdown; // Picks a choice by index at the current branch - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Action") void PickChoice(int index); // Registers a callback for a named "tag function" - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Setup") void RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function); - UFUNCTION(BlueprintCallable) + UFUNCTION(BlueprintCallable, Category="Setup") void RegisterExternalFunction(const FString& functionName, const FExternalFunctionDelegate& function); protected: diff --git a/unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs b/unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs index 125b703c..fee4dc52 100644 --- a/unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs +++ b/unreal/inkcpp/Source/inkcpp/inkcpp.Build.cs @@ -21,7 +21,9 @@ public inkcpp(ReadOnlyTargetRules Target) : base(Target) PrivateIncludePaths.AddRange( new string[] { - Path.Combine(ModuleDirectory, "../shared/Private") + Path.Combine(ModuleDirectory, "../shared/Private"), + Path.Combine(ModuleDirectory, "Private/ink"), + Path.Combine(ModuleDirectory, "Public/ink") } ); From 8d4db767607e3a7cf6dd521cb0856aa10038ae40 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 21 Jul 2022 10:50:26 +0200 Subject: [PATCH 02/67] Fixes broken output formatting --- inkcpp/output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 21f75bd9..fba12080 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -142,7 +142,7 @@ namespace ink // Return processed string // remove mulitple accourencies of ' ' std::string result = str.str(); - auto end = clean_string(result.begin(), result.end()); + auto end = clean_string(result.begin(), result.end()); _last_char = *(end-1); result.resize(end - result.begin() - (_last_char == ' ' ? 1 : 0)); return result; From 7946df57f23811168a5a5a1c837885d29d6ead0a Mon Sep 17 00:00:00 2001 From: JBenda Date: Thu, 21 Jul 2022 21:30:26 +0200 Subject: [PATCH 03/67] Added get and set var for runtime --- inkcpp/globals_impl.cpp | 52 +++--------- inkcpp/globals_impl.h | 13 +-- inkcpp/include/globals.h | 73 ++++++++++++----- inkcpp/include/types.h | 18 +++++ inkcpp/value.cpp | 30 +++++++ inkcpp/value.h | 3 + .../Source/inkcpp/Private/InkRuntime.cpp | 79 ++++++------------- .../Source/inkcpp/Private/InkThread.cpp | 76 +++++++----------- .../inkcpp/Source/inkcpp/Private/InkVar.cpp | 32 ++++++++ .../inkcpp/Source/inkcpp/Public/InkRuntime.h | 46 ++++------- .../inkcpp/Source/inkcpp/Public/InkThread.h | 42 ++++------ unreal/inkcpp/Source/inkcpp/Public/InkVar.h | 5 ++ 12 files changed, 238 insertions(+), 231 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 1749366e..106a638e 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -110,45 +110,19 @@ namespace ink::runtime::internal return _variables.get(name); } - template - optional fetch_variable(const value* val) { - if (val && val->type() == ty) { - return optional(val->get()); - } - return {nullopt}; - } - - template - bool try_set_value(value* val, T x) { - if (val && val->type() == ty) { - val->set(x); - return true; - } - return false; - } - - optional globals_impl::get_int(hash_t name) const { - return fetch_variable(get_variable(name)); - } - bool globals_impl::set_int(hash_t name, int32_t i) { - return try_set_value(get_variable(name), i); - } - optional globals_impl::get_uint(hash_t name) const { - return fetch_variable(get_variable(name)); - } - bool globals_impl::set_uint(hash_t name, uint32_t i) { - return try_set_value(get_variable(name), i); - } - optional globals_impl::get_float(hash_t name) const { - return fetch_variable(get_variable(name)); - } - bool globals_impl::set_float(hash_t name, float i) { - return try_set_value(get_variable(name), i); - } - - optional globals_impl::get_str(hash_t name) const { - return fetch_variable(get_variable(name)); - } + optional globals_impl::get_var(hash_t name) const { + auto* var = get_variable(name); + if (!var) { return nullopt; } + return {var.to_interface_value()}; + } + + bool globals_impl::set_var(hash_t name, const ink::runtime::value& val) { + auto* var = get_variable(name); + if (!var) { return false; } + var = val; + return true; + } + bool globals_impl::set_str(hash_t name, const char* val) { value* v = get_variable(name); if (v->type() == value_type::string) diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index a2d9704b..7d1c91af 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -21,17 +21,8 @@ namespace ink::runtime::internal virtual ~globals_impl() { } protected: - optional get_uint(hash_t name) const override; - bool set_uint(hash_t name, uint32_t value) override; - - optional get_int(hash_t name) const override; - bool set_int(hash_t name, int32_t value) override; - - optional get_float(hash_t name) const override; - bool set_float(hash_t name, float value) override; - - optional get_str(hash_t name) const override; - bool set_str(hash_t name, const char* value) override; + optional get_var(hash_t name) const override; + bool set_var(hash_t name, const ink::runtime::value& val) override; public: // Records a visit to a container diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index ae6b1a97..165e02ce 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -45,49 +45,82 @@ namespace ink::runtime virtual ~globals_interface() = default; protected: - virtual optional get_uint(hash_t name) const = 0; - virtual bool set_uint(hash_t name, uint32_t val) = 0; - virtual optional get_int(hash_t name) const = 0; - virtual bool set_int(hash_t name, int32_t val) = 0; - virtual optional get_float(hash_t name) const = 0; - virtual bool set_float(hash_t name, float val) = 0; - virtual optional get_str(hash_t name) const = 0; - virtual bool set_str(hash_t name, const char* val) = 0; + virtual optional get_var(hash_t name) const = 0; + virtual bool set_var(hash_t name, const value& val) = 0; }; + + template<> + inline optional globals_interface::get(const char* name) const { + return get_var(hash_string(name)); + } + template<> + inline bool globals_interface::set(const char* name, const value& val) { + return set_var(hash_string(name), val); + } + + template<> + inline optional globals_interface::get(const char* name) const { + auto var = get_var(hash_string(name)); + if ( var && var->type == value::Type::Bool) { + return {var->v_bool}; + } + return nullopt; + } + template<> + inline bool globals_interface::set(const char* name, const bool& val) { + return set_var(hash_string(name), value(val)); + } template<> inline optional globals_interface::get(const char* name) const { - return get_uint(hash_string(name)); + auto var = get_var(hash_string(name)); + if (var && var->type == value::Type::Uint32) { + return {var->v_uint32}; + } + return nullopt; } template<> inline bool globals_interface::set(const char* name, const uint32_t& val) { - return set_uint(hash_string(name), val); + return set_var(hash_string(name), value(val)); } template<> inline optional globals_interface::get(const char* name) const { - return get_int(hash_string(name)); + auto var = get_var(hash_string(name)); + if (var && var->type == value::Type::Int32) { + return {v->v_int32}; + } + return nullopt; } template<> inline bool globals_interface::set(const char* name, const int32_t& val) { - return set_int(hash_string(name), val); + return set_var(hash_string(name), value(val)); } - + + template<> inline optional globals_interface::get(const char* name) const { - return get_float(hash_string(name)); + auto var = get_var(hash_string(name)); + if ( var && var->type == value::Type::Float) { + return {v->v_float}; + } + return nullopt; } template<> inline bool globals_interface::set(const char* name, const float& val) { - return set_float(hash_string(name), val); + return set_var(string_hash(name), value(val)); } - + template<> - inline optionalglobals_interface::get(const char* name) const { - return get_str(hash_string(name)); + inline optional globals_interface::get(const char* name) const { + auto var = get_var(hash_string(name)); + if ( var && var->type == value::Type::String ) { + return {v->v_string}; + } + return nullopt; } template<> - inline bool globals_interface::set(const char* name, const char * const & val) { - return set_str(hash_string(name), val); + inline bool globals_interface::set(const char* name, const char*& val) { + return set_var(string_hash(name), value(val)); } } diff --git a/inkcpp/include/types.h b/inkcpp/include/types.h index 406078d8..374a748e 100644 --- a/inkcpp/include/types.h +++ b/inkcpp/include/types.h @@ -14,4 +14,22 @@ namespace ink::runtime typedef story_ptr globals; typedef story_ptr runner; + + struct value { + union { + bool v_bool; + uint32_t v_uint32; + int32_t v_int32; + const char* v_string; + float v_float; + }; + enum class Type { + Bool, Uint32, Int32, String, Float + } type; + value(bool v) : v_bool{v}, type{Type::Bool} {} + value(uint32_t v) : v_uint32{v}, type{Type::Uint32} {} + value(int32_t v) : v_int32{v}, type{Type::Int32} {} + value{const char* v} : v_string{v}, type{Type::String} {} + value{float v} : v_float{v}, type{Type::float} {} + }; } \ No newline at end of file diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index b0ff301d..f56a813f 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -69,4 +69,34 @@ namespace ink::runtime::internal os.append(val); return os; } + + value(const ink::runtime::value& val) { + using types = ink::runtime::value::Type; + switch (val.type) { + case types::Bool: + set(val.v_bool); + break; + case types::Uint32: + set(val.v_uint32); + break; + case types::Int32: + set(val.v_int32); + break; + case types::String: + set(val.v_string); + break; + case types::Float: + set(val.v_float); + break; + } + } + ink::runtime::value to_interface_value() const { + using val = ink::runtime::value; + if(type() == value_type::boolean) { return val(get()); } + else if(type() == value_type::uint32) { return val(get()); } + else if(type() == value_type::int32) { return val(get()); } + else if(type() == value_type::string) { return val(get().str); } + else if(type() == value_type::float32) { return val(get()); } + inkFail("No valid type to convert to interface value!"); + } } diff --git a/inkcpp/value.h b/inkcpp/value.h index 7ae99ce7..6662aa93 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -88,6 +88,9 @@ namespace ink::runtime::internal { template struct ret { using type = void; }; constexpr value() : uint32_value{0}, _type{value_type::none}{} + + value(const ink::runtime::value& val); + ink::runtime::value to_interface_value() const; /// get value of the type (if possible) template diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index 545c56b2..979512e2 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -7,6 +7,7 @@ #include "InkThread.h" #include "TagList.h" #include "InkAsset.h" +#include "InkVar.h" // inkcpp includes #include "ink/story.h" @@ -98,48 +99,21 @@ void AInkRuntime::Tick(float DeltaTime) } } -/*void AInkRuntime::ExternalFunctionRegistered(FString functionName) -{ - // Add to registered function set - bool alreadyRegistered = false; - mRegisteredFunctions.Add(functionName, &alreadyRegistered); - - // If we've never head of this function before, register it with the Story runtime - if (!alreadyRegistered) - { - FExternalFunctionHandler handler; - handler.BindDynamic(this, &AInkRuntime::ExternalFunctionHandler); - mpRuntime->RegisterExternalFunction(functionName, handler); - } -}*/ - -UTagList* AInkRuntime::GetTagsAtPath(FString Path) +void AInkRuntime::HandleTagFunction(UInkThread* Caller, const TArray& Params) { - // TODO - // Get tags at knot - //auto tags = mpRuntime->TagsForContentAtPath(Path); - - // Convert to a tag list - UTagList* result = NewObject(this, UTagList::StaticClass()); - //result->Initialize(tags); - - return result; + // Look for method and execute with parameters + FGlobalTagFunctionMulticastDelegate* function = mGlobalTagFunctions.Find(FName(*Params[0])); + if (function != nullptr) + { + function->Broadcast(Caller, Params); + } } -/*FInkVar AInkRuntime::ExternalFunctionHandler(FString functionName, TArray arguments) +void AInkRuntime::RegisterTagFunction(FName functionName, const FTagFunctionDelegate & function) { - checkf(mpCurrentThread != nullptr, TEXT("Got callback for external function '%s' but no thread is currently active!"), *functionName); - - FInkVar result; - bool handled = mpCurrentThread->HandleExternalFunction(functionName, arguments, result); - if (!handled) - { - UE_LOG(InkRuntime, Warning, TEXT("Currently running thread has no implementation of external function %s"), *functionName); - return FInkVar(); - } - - return result; -}*/ + // Register tag function + mGlobalTagFunctions.FindOrAdd(functionName).Add(function); +} UInkThread* AInkRuntime::Start(TSubclassOf type, FString path, bool startImmediately) { @@ -185,24 +159,6 @@ UInkThread* AInkRuntime::StartExisting(UInkThread* thread, FString path, bool st return thread; } -void AInkRuntime::RegisterGlobalTagFunction(FName FunctionName, const FGlobalTagFunctionDelegate& Function) -{ - // Register tag function - mGlobalTagFunctions.FindOrAdd(FunctionName).Add(Function); -} - -void AInkRuntime::HandleTagFunction(UInkThread* Caller, const TArray& Params) -{ - // Look for method and execute with parameters - FGlobalTagFunctionMulticastDelegate* function = mGlobalTagFunctions.Find(FName(*Params[0])); - if (function != nullptr) - { -#define GET_PARAM(n) Params.Num() > n ? Params[n] : FString() - function->Broadcast(Caller, GET_PARAM(1), GET_PARAM(2), GET_PARAM(3)); -#undef GET_PARAM - } -} - void AInkRuntime::PushExclusiveThread(UInkThread* Thread) { // If we're already on the stack, ignore @@ -218,3 +174,14 @@ void AInkRuntime::PopExclusiveThread(UInkThread* Thread) // Remove from the stack mExclusiveStack.Remove(Thread); } + +void AInkRuntime::GetGlobalVariable(FName name, FInkVar& value) const { + ink::optional var = mpGlobals->get(TCHAR_TO_ANSI(*name)); + inkAssert(var, "Reguested variable does not exists!"); + if(var) { value = *var; } +} + +void AInkRuntime::SetGlobalVariable(FName name, const FInkVar& value) { + bool success = mpGlobals->set(TCHAR_TO_ANSI(*name), value.to_value()); + inkAssert(success, "Unable to set variable"); +} diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 3bd0ca28..8a32ee1d 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -36,13 +36,7 @@ void UInkThread::RegisterTagFunction(FName functionName, const FTagFunctionDeleg void UInkThread::RegisterExternalFunction(const FString& functionName, const FExternalFunctionDelegate& function) { - mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_ANSI (*functionName)), function); - /*// Notify the runtime. It needs to make sure the external function is registered with Ink - mpRuntime->ExternalFunctionRegistered(functionName); - - // Add to external functions registration map - // TODO: Check for duplicates? - mExternalFunctions.Add(functionName, function);*/ + mpRunner->bind(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function); } void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::runner thread) @@ -69,11 +63,11 @@ bool UInkThread::ExecuteInternal() // If this is the first time we're running, start us off at our starting path if (!mbHasRun) { - if (!ensureMsgf(!mStartPath.IsEmpty(), TEXT("Thread executing without call to ::Initialize"))) + if (!ensureMsgf(mbInitialized, TEXT("Thread executing without call to ::Initialize"))) return true; - - //pStory->ChoosePathString(mStartPath, true, TArray()); - // TODO: Move to correct story path + if (mStartPath.Len()) { + mpRunner->move_to(ink::hash_string(TCHAR_TO_ANSI(*mStartPath))); + } mbHasRun = true; } @@ -88,6 +82,7 @@ bool UInkThread::ExecuteInternal() mpRunner->choose(mnChoiceToChoose); } mnChoiceToChoose = -1; + mCurrentChoices.Empty(); } // Execute until story yields or finishes @@ -96,10 +91,6 @@ bool UInkThread::ExecuteInternal() // Handle text FString line = mpRunner->getline(); - // Get tags - // TODO: Tags - TArray tags;// = pStory->CurrentTags(); - // Special: Line begins with >> marker if (line.StartsWith(TEXT(">>"))) { @@ -129,21 +120,25 @@ bool UInkThread::ExecuteInternal() else { // Forward to handler - UTagList* pTags = NewObject(this, UTagList::StaticClass()); - pTags->Initialize(tags); + // Get tags + TArray tags; + for(size_t i = 0; i < mpRunner->num_tags(); ++i) { + tags.Add(FString(mpRunner->get_tag(i))); + } + mTags.Initialize(tags); OnLineWritten(line, pTags); - } - - // Handle tags/tag methods post-line - for (auto it = tags.CreateConstIterator(); it; ++it) - { - // Generic tag handler - OnTag(*it); + + // Handle tags/tag methods post-line + for (auto it = tags.CreateConstIterator(); it; ++it) + { + // Generic tag handler + OnTag(*it); - // Tag methods - TArray params; - it->ParseIntoArray(params, TEXT("_")); - ExecuteTagMethod(params); + // Tag methods + TArray params; + it->ParseIntoArray(params, TEXT("_")); + ExecuteTagMethod(params); + } } } @@ -152,17 +147,14 @@ bool UInkThread::ExecuteInternal() { mbInChoice = true; - // TODO: Record choices somewhere? - // Forward to handler - TArray choices; for (ink::size_t i = 0; i < mpRunner->num_choices(); i++) { UChoice* choice = NewObject(this); choice->Initialize(mpRunner->get_choice(i)); - choices.Add(choice); + mCurrentChoices.Add(choice); } - OnChoice(choices); + OnChoice(mCurrentChoices); // If we've chosen a choice already, go back up to handle it. if (mnChoiceToChoose != -1) @@ -187,27 +179,13 @@ bool UInkThread::ExecuteInternal() return false; } -/*bool UInkThread::HandleExternalFunction(const FString& functionName, TArray arguments, FInkVar& result) -{ - // Check to see if we even know about this function - const FExternalFunctionDelegate* pDelegate = mExternalFunctions.Find(functionName); - if (pDelegate == nullptr) - return false; - - // If so, execute and return - pDelegate->Execute(arguments, result); - return true; -}*/ - void UInkThread::ExecuteTagMethod(const TArray& Params) { // Look for method and execute with parameters FTagFunctionMulticastDelegate* function = mTagFunctions.Find(FName(*Params[0])); if (function != nullptr) { -#define GET_PARAM(n) Params.Num() > n ? Params[n] : FString() - function->Broadcast(GET_PARAM(1), GET_PARAM(2), GET_PARAM(3)); -#undef GET_PARAM + function->Broadcast(Params); } // Forward to runtime @@ -223,7 +201,7 @@ bool UInkThread::Execute() if (finished) { // Allow outsiders to subscribe - OnThreadShutdown.Broadcast(); + // TODO: OnThreadShutdown.Broadcast(); OnShutdown(); } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp index 5229e1f7..ee69b89d 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp @@ -1,7 +1,39 @@ #include "InkVar.h" +#include "ink/types.h" #include "Misc/AssertionMacros.h" +FinFInkVar::FInkVar(ink::runtime::value val) : FInkVar() { + using v_types = ink::runtime::value::Type; + switch(val.type) { + case v_type::Bool: + type = EInkVarType::Int; + intVar = static_cast(val.v_bool); + break; + case v_type::Uint32: + type = EInkVarType::Int; + /// @TODO: add warning if overflows + intVar = static_cast(val.v_uint32); + break; + case v_type::Int32: + type = EInkVarType::Int; + intVar = val.v_int32; + break; + case v_type::String: + type = EInkVarType::String; + stringVar = FString(val.v_string); + break; + case v_type::Float: + type = EInkVarType::Float; + floatVar = val.v_float; + break; + default: + inkFail("unknown type!, failed to convert ink::value to InkVar"); + } +} + + value to_value() const; + FString UInkVarLibrary::Conv_InkVarString(const FInkVar& InkVar) { if (ensureMsgf(InkVar.type == EInkVarType::String, TEXT("InkVar is not a String Type!"))) diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index 97f130a8..82d4a336 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -10,7 +10,10 @@ #include "InkRuntime.generated.h" +DECLARE_DYNAMIC_DELEGATE_OneParam(FGlobalTagFunctionDelegate, const TArray&, Params); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGlobalTagFunctionMulticastDelegate, const TArray&, Params); class UInkThread; +class FInkVar; namespace ink::runtime { class story; } UCLASS() @@ -23,25 +26,28 @@ class INKCPP_API AInkRuntime : public AActor AInkRuntime(); ~AInkRuntime(); - UFUNCTION(BlueprintCallable, Category="Start") + UFUNCTION(BlueprintCallable, Category="Ink") UInkThread* Start(TSubclassOf type, FString path, bool runImmediately = true); - UFUNCTION(BlueprintCallable, Category="Start") + UFUNCTION(BlueprintCallable, Category="Ink") UInkThread* StartExisting(UInkThread* thread, FString path, bool runImmediately = true); - UFUNCTION(BlueprintCallable, Category="Tags") - void RegisterGlobalTagFunction(FName FunctionName, const FGlobalTagFunctionDelegate& Function); - - // Called from UInkThread - void HandleTagFunction(UInkThread* Caller, const TArray& Params); - // Marks a thread as "exclusive". As long as it is running, no other threads will update. - UFUNCTION(BlueprintCallable, Category="Exclusive Thread") + UFUNCTION(BlueprintCallable, Category="Ink") void PushExclusiveThread(UInkThread* Thread); // Removes a thread from the exclusive stack. See PushExclusiveThread. - UFUNCTION(BlueprintCallable, Category="Exclusive Thread") + UFUNCTION(BlueprintCallable, Category="Ink") void PopExclusiveThread(UInkThread* Thread); + + UFUNCTION(BlueprintCallable, Category="Ink") + void RegisterTagFunction(FName functionName, const FTagFunctionDelegate & function); + + UFUNCTION(BlueprintCallable, Category="Ink") + void GetGlobalVariable(FName name, FInkVar& value) const; + + UFUNCTION(BlueprintCallable, Category="Ink") + void SetGlobalVariable(FName name, const FInkVar& value); protected: // Called when the game starts or when spawned @@ -55,31 +61,13 @@ class INKCPP_API AInkRuntime : public AActor UPROPERTY(EditAnywhere, Category="Put in correct category") class UInkAsset* InkAsset; - // Called by threads when they want to register an external function - void ExternalFunctionRegistered(FString functionName); - - /* Returns the tags at the specified knot */ - UFUNCTION(BlueprintPure, Category="Tags") - UTagList* GetTagsAtPath(FString Path); - -private: - // UFUNCTION() - // FInkVar ExternalFunctionHandler(FString functionName, TArray arguments); - private: ink::runtime::story* mpRuntime; ink::runtime::globals mpGlobals; UPROPERTY() TArray mThreads; - - /*UPROPERTY() - UInkThread* mpCurrentThread;*/ - - UPROPERTY() - TSet mRegisteredFunctions; - - UPROPERTY() + TMap mGlobalTagFunctions; UPROPERTY() diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h index 333a87d3..26c71160 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h @@ -16,13 +16,10 @@ class UTagList; class AInkRuntime; class UChoice; -DECLARE_DYNAMIC_DELEGATE_ThreeParams(FTagFunctionDelegate, FString, FirstParameter, FString, SecondParameter, FString, ThirdParameter); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FTagFunctionMulticastDelegate, FString, FirstParameter, FString, SecondParameter, FString, ThirdParameter); - +DECLARE_DYNAMIC_DELEGATE_OneParam(FTagFunctionDelegate, const TArray&, Params); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FTagFunctionMulticastDelegate, const TArray&, Params); DECLARE_DYNAMIC_DELEGATE_TwoParams(FExternalFunctionDelegate, const TArray&, Arguments, FInkVar&, Result); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FThreadShutdownDelegate); - /** * Base class for all ink threads */ @@ -35,47 +32,44 @@ class INKCPP_API UInkThread : public UObject UInkThread(); // Yields the thread immediately. Will wait until Resume(). - UFUNCTION(BlueprintCallable, Category="Controle") + UFUNCTION(BlueprintCallable, Category="Ink") void Yield(); - UFUNCTION(BlueprintPure, Category="Controle") + UFUNCTION(BlueprintPure, Category="Ink") bool IsYielding(); // Causes the thread to resume if yielded. - UFUNCTION(BlueprintCallable, Category="Controle") + UFUNCTION(BlueprintCallable, Category="Ink") void Resume(); // Kills the thread, regardless of state - UFUNCTION(BlueprintCallable, Category="Controle") + UFUNCTION(BlueprintCallable, Category="Ink") void Stop(); // Returns the runtime which owns this thread. - UFUNCTION(BlueprintPure, Category="Setup") + UFUNCTION(BlueprintPure, Category="Ink") AInkRuntime* GetRuntime() const { return mpRuntime; } // Called before the thread begins executing - UFUNCTION(BlueprintNativeEvent, Category="Events") + UFUNCTION(BlueprintImplementableEvent , Category="Ink") void OnStartup(); // Called when the thread has printed a new line - UFUNCTION(BlueprintNativeEvent, Category="Events") - void OnLineWritten(const FString& line, UTagList* tags); + UFUNCTION(BlueprintImplementableEvent , Category="Ink") + void OnLineWritten(const FString& line, const UTagList& tags); // Called when a tag has been processed on the current line - UFUNCTION(BlueprintNativeEvent, Category="Events") + UFUNCTION(BlueprintImplementableEvent , Category="Ink") void OnTag(const FString& line); // Called when the thread has requested a branch - UFUNCTION(BlueprintNativeEvent, Category="Events") + UFUNCTION(BlueprintImplementableEvent , Category="Ink") void OnChoice(const TArray& choices); // Called before the thread is destroyed - UFUNCTION(BlueprintNativeEvent, Category="Events") + UFUNCTION(BlueprintImplementableEvent , Category="Ink") void OnShutdown(); - UPROPERTY(BlueprintAssignable, Category="Events") - FThreadShutdownDelegate OnThreadShutdown; - // Picks a choice by index at the current branch UFUNCTION(BlueprintCallable, Category="Action") void PickChoice(int index); @@ -103,12 +97,12 @@ class INKCPP_API UInkThread : public UObject bool ExecuteInternal(); - //bool HandleExternalFunction(const FString& functionName, TArray arguments, FInkVar& result); - void ExecuteTagMethod(const TArray& Params); private: ink::runtime::runner mpRunner; + UTagList mTags; + TArray mCurrentChoices; /// @TODO: make accassible? FString mStartPath; bool mbHasRun; @@ -121,10 +115,4 @@ class INKCPP_API UInkThread : public UObject UPROPERTY() AInkRuntime* mpRuntime; - - UPROPERTY() - TMap mTagFunctions; - - /*UPROPERTY() - TMap mExternalFunctions;*/ }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h index a35b5da7..2080a197 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h @@ -19,6 +19,8 @@ enum class EInkVarType : uint8 None }; +namespace ink::runtime { struct value; } + USTRUCT(BlueprintType) struct INKCPP_API FInkVar { @@ -29,6 +31,9 @@ struct INKCPP_API FInkVar FInkVar(float val) { type = EInkVarType::Float; floatVar = val; } FInkVar(int val) { type = EInkVarType::Int; intVar = val; } FInkVar(FString val) { type = EInkVarType::String; stringVar = val; } + FInkVar(ink::runtime::value val); + + ink::runtime::value to_value() const; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ink") EInkVarType type; From ad03ea0331e29715b23423aa57e124108df50022 Mon Sep 17 00:00:00 2001 From: JBenda Date: Fri, 22 Jul 2022 16:29:15 +0200 Subject: [PATCH 04/67] Fixed syntax errors --- inkcpp/globals_impl.cpp | 25 ++++++++++--------------- inkcpp/include/globals.h | 14 +++++++------- inkcpp/include/types.h | 4 ++-- inkcpp/value.cpp | 12 ++++++++++-- inkcpp/value.h | 4 +++- 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 106a638e..bb45b92b 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -113,34 +113,29 @@ namespace ink::runtime::internal optional globals_impl::get_var(hash_t name) const { auto* var = get_variable(name); if (!var) { return nullopt; } - return {var.to_interface_value()}; + return {var->to_interface_value()}; } bool globals_impl::set_var(hash_t name, const ink::runtime::value& val) { auto* var = get_variable(name); if (!var) { return false; } - var = val; - return true; - } - - bool globals_impl::set_str(hash_t name, const char* val) { - value* v = get_variable(name); - if (v->type() == value_type::string) - { + if ( val.type == ink::runtime::value::Type::String && var->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); + for ( const char* i = val.v_string; *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) { + for ( const char* i = val.v_string; *i; ++i ) { *ptr++ = *i; } *ptr = 0; - *v = value{}.set(static_cast(new_string), true); + *var = value{}.set( static_cast( new_string ), true ); return true; + } else { + return var->set( val ); } - return false; + inkFail("Unchecked case in set var!"); } void globals_impl::initialize_globals(runner_impl* run) diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index 165e02ce..1d092399 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -1,6 +1,6 @@ #pragma once -#include "system.h" +#include "types.h" namespace ink::runtime { @@ -88,7 +88,7 @@ namespace ink::runtime inline optional globals_interface::get(const char* name) const { auto var = get_var(hash_string(name)); if (var && var->type == value::Type::Int32) { - return {v->v_int32}; + return {var->v_int32}; } return nullopt; } @@ -102,25 +102,25 @@ namespace ink::runtime inline optional globals_interface::get(const char* name) const { auto var = get_var(hash_string(name)); if ( var && var->type == value::Type::Float) { - return {v->v_float}; + return {var->v_float}; } return nullopt; } template<> inline bool globals_interface::set(const char* name, const float& val) { - return set_var(string_hash(name), value(val)); + return set_var(hash_string(name), value(val)); } template<> inline optional globals_interface::get(const char* name) const { auto var = get_var(hash_string(name)); if ( var && var->type == value::Type::String ) { - return {v->v_string}; + return {var->v_string}; } return nullopt; } template<> - inline bool globals_interface::set(const char* name, const char*& val) { - return set_var(string_hash(name), value(val)); + inline bool globals_interface::set(const char* name, const char* const& val) { + return set_var(hash_string(name), value(val)); } } diff --git a/inkcpp/include/types.h b/inkcpp/include/types.h index 374a748e..7e175977 100644 --- a/inkcpp/include/types.h +++ b/inkcpp/include/types.h @@ -29,7 +29,7 @@ namespace ink::runtime value(bool v) : v_bool{v}, type{Type::Bool} {} value(uint32_t v) : v_uint32{v}, type{Type::Uint32} {} value(int32_t v) : v_int32{v}, type{Type::Int32} {} - value{const char* v} : v_string{v}, type{Type::String} {} - value{float v} : v_float{v}, type{Type::float} {} + value(const char* v) : v_string{v}, type{Type::String} {} + value(float v) : v_float{v}, type{Type::Float} {} }; } \ No newline at end of file diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index f56a813f..108fe487 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -70,7 +70,7 @@ namespace ink::runtime::internal return os; } - value(const ink::runtime::value& val) { + value::value(const ink::runtime::value& val) : value() { using types = ink::runtime::value::Type; switch (val.type) { case types::Bool: @@ -90,7 +90,15 @@ namespace ink::runtime::internal break; } } - ink::runtime::value to_interface_value() const { + bool value::set( const ink::runtime::value& val ) { + auto var = value( val ); + if ( var.type() == type() ) { + *this = var; + return true; + } + return false; + } + ink::runtime::value value::to_interface_value() const { using val = ink::runtime::value; if(type() == value_type::boolean) { return val(get()); } else if(type() == value_type::uint32) { return val(get()); } diff --git a/inkcpp/value.h b/inkcpp/value.h index 6662aa93..ad4c469b 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -9,6 +9,7 @@ #include "command.h" #include "list_table.h" #include "tuple.hpp" +#include "types.h" #ifdef INK_ENABLE_STL #include @@ -89,7 +90,8 @@ namespace ink::runtime::internal { constexpr value() : uint32_value{0}, _type{value_type::none}{} - value(const ink::runtime::value& val); + explicit value(const ink::runtime::value& val); + bool set( const ink::runtime::value& val ); ink::runtime::value to_interface_value() const; /// get value of the type (if possible) From 19cb9712e59360f1918cdf2413b3883c1b0ac39d Mon Sep 17 00:00:00 2001 From: JBenda Date: Fri, 22 Jul 2022 18:35:55 +0200 Subject: [PATCH 05/67] fixed unreal syntax errors --- inkcpp/include/types.h | 1 + .../Source/inkcpp/Private/InkRuntime.cpp | 6 ++-- .../Source/inkcpp/Private/InkThread.cpp | 13 +++++---- .../inkcpp/Source/inkcpp/Private/InkVar.cpp | 28 ++++++++++++++----- .../Source/inkcpp/Public/InkDelegates.h | 9 ++++-- .../inkcpp/Source/inkcpp/Public/InkRuntime.h | 14 ++++++---- .../inkcpp/Source/inkcpp/Public/InkThread.h | 13 +++++---- 7 files changed, 57 insertions(+), 27 deletions(-) diff --git a/inkcpp/include/types.h b/inkcpp/include/types.h index 7e175977..61631bae 100644 --- a/inkcpp/include/types.h +++ b/inkcpp/include/types.h @@ -26,6 +26,7 @@ namespace ink::runtime enum class Type { Bool, Uint32, Int32, String, Float } type; + value() : v_uint32{0}, type{Type::Int32} {} value(bool v) : v_bool{v}, type{Type::Bool} {} value(uint32_t v) : v_uint32{v}, type{Type::Uint32} {} value(int32_t v) : v_int32{v}, type{Type::Int32} {} diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index 979512e2..e19a5259 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -13,6 +13,8 @@ #include "ink/story.h" #include "ink/globals.h" +namespace ink { using value = runtime::value; } + // Sets default values AInkRuntime::AInkRuntime() : mpRuntime(nullptr) { @@ -175,13 +177,13 @@ void AInkRuntime::PopExclusiveThread(UInkThread* Thread) mExclusiveStack.Remove(Thread); } -void AInkRuntime::GetGlobalVariable(FName name, FInkVar& value) const { +void AInkRuntime::GetGlobalVariable(const FString& name, FInkVar& value) const { ink::optional var = mpGlobals->get(TCHAR_TO_ANSI(*name)); inkAssert(var, "Reguested variable does not exists!"); if(var) { value = *var; } } -void AInkRuntime::SetGlobalVariable(FName name, const FInkVar& value) { +void AInkRuntime::SetGlobalVariable(const FString& name, const FInkVar& value) { bool success = mpGlobals->set(TCHAR_TO_ANSI(*name), value.to_value()); inkAssert(success, "Unable to set variable"); } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 8a32ee1d..6d603063 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -12,7 +12,9 @@ #include "Internationalization/Regex.h" UInkThread::UInkThread() : mbHasRun(false), mnChoiceToChoose(-1), mnYieldCounter(0), mbKill(false) { } - +UInkThread::~UInkThread() { + delete mpTags; +} void UInkThread::Yield() { mnYieldCounter++; @@ -36,7 +38,7 @@ void UInkThread::RegisterTagFunction(FName functionName, const FTagFunctionDeleg void UInkThread::RegisterExternalFunction(const FString& functionName, const FExternalFunctionDelegate& function) { - mpRunner->bind(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function); + mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function); } void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::runner thread) @@ -50,6 +52,7 @@ void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::ru mpRuntime = runtime; mbInitialized = true; mpRunner = thread; + mpTags = NewObject(); OnStartup(); } @@ -125,8 +128,8 @@ bool UInkThread::ExecuteInternal() for(size_t i = 0; i < mpRunner->num_tags(); ++i) { tags.Add(FString(mpRunner->get_tag(i))); } - mTags.Initialize(tags); - OnLineWritten(line, pTags); + mpTags->Initialize(tags); + OnLineWritten(line, mpTags); // Handle tags/tag methods post-line for (auto it = tags.CreateConstIterator(); it; ++it) @@ -185,7 +188,7 @@ void UInkThread::ExecuteTagMethod(const TArray& Params) FTagFunctionMulticastDelegate* function = mTagFunctions.Find(FName(*Params[0])); if (function != nullptr) { - function->Broadcast(Params); + function->Broadcast(this, Params); } // Forward to runtime diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp index ee69b89d..04784974 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp @@ -3,27 +3,27 @@ #include "Misc/AssertionMacros.h" -FinFInkVar::FInkVar(ink::runtime::value val) : FInkVar() { +FInkVar::FInkVar(ink::runtime::value val) : FInkVar() { using v_types = ink::runtime::value::Type; switch(val.type) { - case v_type::Bool: + case v_types::Bool: type = EInkVarType::Int; intVar = static_cast(val.v_bool); break; - case v_type::Uint32: + case v_types::Uint32: type = EInkVarType::Int; /// @TODO: add warning if overflows intVar = static_cast(val.v_uint32); break; - case v_type::Int32: + case v_types::Int32: type = EInkVarType::Int; intVar = val.v_int32; break; - case v_type::String: + case v_types::String: type = EInkVarType::String; stringVar = FString(val.v_string); break; - case v_type::Float: + case v_types::Float: type = EInkVarType::Float; floatVar = val.v_float; break; @@ -32,7 +32,21 @@ FinFInkVar::FInkVar(ink::runtime::value val) : FInkVar() { } } - value to_value() const; +ink::runtime::value FInkVar::to_value() const { + switch(type) { + case EInkVarType::Int: + return ink::runtime::value(intVar); + case EInkVarType::Float: + return ink::runtime::value(floatVar); + case EInkVarType::String: + return ink::runtime::value(TCHAR_TO_ANSI(*stringVar)); + case EInkVarType::None: + inkFail("None type cant be passed"); + default: + inkFail("Unsupported type"); + } + +} FString UInkVarLibrary::Conv_InkVarString(const FInkVar& InkVar) { diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkDelegates.h b/unreal/inkcpp/Source/inkcpp/Public/InkDelegates.h index 5d090553..e6625e78 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkDelegates.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkDelegates.h @@ -3,10 +3,15 @@ #include "CoreMinimal.h" #include "Delegates/Delegate.h" +#include "InkVar.h" + #include "InkDelegates.generated.h" -DECLARE_DYNAMIC_DELEGATE_FourParams(FGlobalTagFunctionDelegate, UInkThread*, Caller, const FString&, FirstParameter, const FString&, SecondParameter, const FString&, ThirdParameter); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FGlobalTagFunctionMulticastDelegate, UInkThread*, Caller, const FString&, FirstParameter, const FString&, SecondParameter, const FString&, ThirdParameter); +DECLARE_DYNAMIC_DELEGATE_TwoParams(FTagFunctionDelegate, UInkThread*, Caller, const TArray&, Params); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FTagFunctionMulticastDelegate, UInkThread*, Caller, const TArray&, Params); +DECLARE_DYNAMIC_DELEGATE_TwoParams(FExternalFunctionDelegate, const TArray&, Arguments, FInkVar&, Result); +DECLARE_DYNAMIC_DELEGATE_TwoParams(FGlobalTagFunctionDelegate, UInkThread*, Caller, const TArray&, Params); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGlobalTagFunctionMulticastDelegate, UInkThread*, Caller, const TArray&, Params); UCLASS() class UFuckYou : public UObject diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index 82d4a336..80848b58 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -4,16 +4,17 @@ #include "CoreMinimal.h" #include "GameFramework/Actor.h" + #include "InkDelegates.h" + #include "ink/types.h" #include "ink/globals.h" + #include "InkRuntime.generated.h" -DECLARE_DYNAMIC_DELEGATE_OneParam(FGlobalTagFunctionDelegate, const TArray&, Params); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGlobalTagFunctionMulticastDelegate, const TArray&, Params); class UInkThread; -class FInkVar; +struct FInkVar; namespace ink::runtime { class story; } UCLASS() @@ -43,11 +44,14 @@ class INKCPP_API AInkRuntime : public AActor UFUNCTION(BlueprintCallable, Category="Ink") void RegisterTagFunction(FName functionName, const FTagFunctionDelegate & function); + // for interanl use + void HandleTagFunction(UInkThread* Caller, const TArray& Params); + UFUNCTION(BlueprintCallable, Category="Ink") - void GetGlobalVariable(FName name, FInkVar& value) const; + void GetGlobalVariable(const FString& name, FInkVar& value) const; UFUNCTION(BlueprintCallable, Category="Ink") - void SetGlobalVariable(FName name, const FInkVar& value); + void SetGlobalVariable(const FString& name, const FInkVar& value); protected: // Called when the game starts or when spawned diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h index 26c71160..933a82fa 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h @@ -6,6 +6,8 @@ #include "UObject/NoExportTypes.h" #include "InkVar.h" +#include "InkDelegates.h" + #include "ink/runner.h" #include "ink/types.h" @@ -16,10 +18,6 @@ class UTagList; class AInkRuntime; class UChoice; -DECLARE_DYNAMIC_DELEGATE_OneParam(FTagFunctionDelegate, const TArray&, Params); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FTagFunctionMulticastDelegate, const TArray&, Params); -DECLARE_DYNAMIC_DELEGATE_TwoParams(FExternalFunctionDelegate, const TArray&, Arguments, FInkVar&, Result); - /** * Base class for all ink threads */ @@ -30,6 +28,7 @@ class INKCPP_API UInkThread : public UObject public: UInkThread(); + ~UInkThread(); // Yields the thread immediately. Will wait until Resume(). UFUNCTION(BlueprintCallable, Category="Ink") @@ -56,7 +55,7 @@ class INKCPP_API UInkThread : public UObject // Called when the thread has printed a new line UFUNCTION(BlueprintImplementableEvent , Category="Ink") - void OnLineWritten(const FString& line, const UTagList& tags); + void OnLineWritten(const FString& line, const UTagList* tags); // Called when a tag has been processed on the current line UFUNCTION(BlueprintImplementableEvent , Category="Ink") @@ -101,9 +100,11 @@ class INKCPP_API UInkThread : public UObject private: ink::runtime::runner mpRunner; - UTagList mTags; + UTagList* mpTags; TArray mCurrentChoices; /// @TODO: make accassible? + TMap mTagFunctions; + FString mStartPath; bool mbHasRun; From c0789143a0d086c97c2e339cf072d5c033701ce5 Mon Sep 17 00:00:00 2001 From: JBenda Date: Wed, 27 Jul 2022 13:14:02 +0200 Subject: [PATCH 06/67] Change BeginPlay order to load story first add some logging --- unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp | 9 +++++++-- unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index e19a5259..d47d8a5d 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -30,8 +30,6 @@ AInkRuntime::~AInkRuntime() // Called when the game starts or when spawned void AInkRuntime::BeginPlay() { - Super::BeginPlay(); - // Create the CPU for the story if (InkAsset != nullptr) { @@ -46,6 +44,8 @@ void AInkRuntime::BeginPlay() { UE_LOG(InkCpp, Warning, TEXT("No story asset assigned.")); } + + Super::BeginPlay(); } // Called every frame @@ -86,6 +86,7 @@ void AInkRuntime::Tick(float DeltaTime) for (auto iter = mThreads.CreateIterator(); iter; iter++) { UInkThread* pNextThread = *iter; + UE_LOG(InkCpp, Display, TEXT("EXEC thread")); // Ignore threads that aren't eligable for execution if (!pNextThread->CanExecute()) @@ -119,8 +120,10 @@ void AInkRuntime::RegisterTagFunction(FName functionName, const FTagFunctionDele UInkThread* AInkRuntime::Start(TSubclassOf type, FString path, bool startImmediately) { + UE_LOG(InkCpp, Display, TEXT("Start")); if (mpRuntime == nullptr || type == nullptr) { + UE_LOG(InkCpp, Warning, TEXT("failed to start")); return nullptr; } @@ -133,10 +136,12 @@ UInkThread* AInkRuntime::Start(TSubclassOf type, FString path, UInkThread* AInkRuntime::StartExisting(UInkThread* thread, FString path, bool startImmediately /*= true*/) { + UE_LOG(InkCpp, Display, TEXT("Start start")); if (mpRuntime == nullptr) { return nullptr; } + UE_LOG(InkCpp, Display, TEXT("Do More start")); // Initialize thread with new runner ink::runtime::runner runner = mpRuntime->new_runner(mpGlobals); diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 6d603063..860240b5 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -59,6 +59,7 @@ void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::ru bool UInkThread::ExecuteInternal() { + UE_LOG(InkCpp, Display, TEXT("UInkThread::ExecuteInternal")); // Kill thread if (mbKill) return true; @@ -93,7 +94,7 @@ bool UInkThread::ExecuteInternal() { // Handle text FString line = mpRunner->getline(); - + UE_LOG(InkCpp, Display, TEXT("line: %s"), *line); // Special: Line begins with >> marker if (line.StartsWith(TEXT(">>"))) { @@ -129,6 +130,7 @@ bool UInkThread::ExecuteInternal() tags.Add(FString(mpRunner->get_tag(i))); } mpTags->Initialize(tags); + UE_LOG(InkCpp, Display, TEXT("Call linewriten")); OnLineWritten(line, mpTags); // Handle tags/tag methods post-line @@ -197,6 +199,7 @@ void UInkThread::ExecuteTagMethod(const TArray& Params) bool UInkThread::Execute() { + UE_LOG(InkCpp, Display, TEXT("UInkThread::Execute")); // Execute thread bool finished = ExecuteInternal(); From 22038b583bd76422ddcae59ea1c30c6a8a19c904 Mon Sep 17 00:00:00 2001 From: JBenda Date: Wed, 27 Jul 2022 19:10:56 +0200 Subject: [PATCH 07/67] Successfull build first blueprint Int application in unreal --- inkcpp/output.cpp | 1 + inkcpp/runner_impl.cpp | 22 ++++++++++++++----- inkcpp/runner_impl.h | 2 +- .../Source/inkcpp/Private/InkRuntime.cpp | 4 +--- .../Source/inkcpp/Private/InkThread.cpp | 18 ++++++++------- .../inkcpp/Source/inkcpp/Public/InkThread.h | 8 +++---- 6 files changed, 34 insertions(+), 21 deletions(-) diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index fba12080..4acd0763 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -275,6 +275,7 @@ namespace ink break; case value_type::list_flag: length += lists.stringLen(_data[i].get()); + break; default: length += value_length(_data[i]); } } diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 4b2a665a..4c81dc0a 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -429,13 +429,25 @@ namespace ink::runtime::internal #ifdef INK_ENABLE_UNREAL FString runner_impl::getline() { - inkAssert(false, "Fix (see getline for std)"); + FString result{}; + bool fill = false; + do { + if ( fill ) { + result += " "; + } + // Advance interpreter one line + advance_line(); + // Read lin ve into std::string + const char* str = _output.get_alloc(_globals->strings(), _globals->lists()); + result.Append( str, c_str_len( str ) ); + fill = _output.last_char() == ' '; + } while ( _ptr != nullptr && _output.last_char() != '\n' ); - // Read line into std::string - FString result; + // TODO: fallback choice = no choice + if ( !has_choices() && _fallback_choice ) { choose( ~0 ); } // Return result - inkAssert(_output.is_empty(), "Output should be empty after getline!"); + inkAssert( _output.is_empty(), "Output should be empty after getline!" ); return result; } #endif @@ -526,7 +538,7 @@ namespace ink::runtime::internal char* runner_impl::getline_alloc() { /// TODO - inkAssert(false, "Not implemented yet!"); + inkFail("Not implemented yet!"); return nullptr; } #endif diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 7f208036..61daf718 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -234,7 +234,7 @@ namespace ink::runtime::internal // Choice list managed_array _choices; optional _fallback_choice; - size_t _backup_choice_len; + size_t _backup_choice_len = 0; // Tag list managed_array _tags; diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index d47d8a5d..be7ca357 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -86,7 +86,6 @@ void AInkRuntime::Tick(float DeltaTime) for (auto iter = mThreads.CreateIterator(); iter; iter++) { UInkThread* pNextThread = *iter; - UE_LOG(InkCpp, Display, TEXT("EXEC thread")); // Ignore threads that aren't eligable for execution if (!pNextThread->CanExecute()) @@ -136,12 +135,11 @@ UInkThread* AInkRuntime::Start(TSubclassOf type, FString path, UInkThread* AInkRuntime::StartExisting(UInkThread* thread, FString path, bool startImmediately /*= true*/) { - UE_LOG(InkCpp, Display, TEXT("Start start")); if (mpRuntime == nullptr) { + UE_LOG(InkCpp, Warning, TEXT("Failed to start existing")); return nullptr; } - UE_LOG(InkCpp, Display, TEXT("Do More start")); // Initialize thread with new runner ink::runtime::runner runner = mpRuntime->new_runner(mpGlobals); diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 860240b5..0df078a1 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -12,9 +12,7 @@ #include "Internationalization/Regex.h" UInkThread::UInkThread() : mbHasRun(false), mnChoiceToChoose(-1), mnYieldCounter(0), mbKill(false) { } -UInkThread::~UInkThread() { - delete mpTags; -} +UInkThread::~UInkThread() {} void UInkThread::Yield() { mnYieldCounter++; @@ -59,7 +57,6 @@ void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::ru bool UInkThread::ExecuteInternal() { - UE_LOG(InkCpp, Display, TEXT("UInkThread::ExecuteInternal")); // Kill thread if (mbKill) return true; @@ -94,7 +91,6 @@ bool UInkThread::ExecuteInternal() { // Handle text FString line = mpRunner->getline(); - UE_LOG(InkCpp, Display, TEXT("line: %s"), *line); // Special: Line begins with >> marker if (line.StartsWith(TEXT(">>"))) { @@ -130,7 +126,6 @@ bool UInkThread::ExecuteInternal() tags.Add(FString(mpRunner->get_tag(i))); } mpTags->Initialize(tags); - UE_LOG(InkCpp, Display, TEXT("Call linewriten")); OnLineWritten(line, mpTags); // Handle tags/tag methods post-line @@ -199,7 +194,6 @@ void UInkThread::ExecuteTagMethod(const TArray& Params) bool UInkThread::Execute() { - UE_LOG(InkCpp, Display, TEXT("UInkThread::Execute")); // Execute thread bool finished = ExecuteInternal(); @@ -215,10 +209,18 @@ bool UInkThread::Execute() return finished; } -void UInkThread::PickChoice(int index) +bool UInkThread::PickChoice(int index) { + if (index >= mCurrentChoices.Num()) { + UE_LOG(InkCpp, Warning, + TEXT("PickChoice: index(%i) out of range [0-%i)"), + index, + mCurrentChoices.Num()); + return false; + } mnChoiceToChoose = index; mbInChoice = false; + return true; } bool UInkThread::CanExecute() const diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h index 933a82fa..6a9b2d7e 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h @@ -70,14 +70,14 @@ class INKCPP_API UInkThread : public UObject void OnShutdown(); // Picks a choice by index at the current branch - UFUNCTION(BlueprintCallable, Category="Action") - void PickChoice(int index); + UFUNCTION(BlueprintCallable, Category="Ink") + bool PickChoice(int index); // Registers a callback for a named "tag function" - UFUNCTION(BlueprintCallable, Category="Setup") + UFUNCTION(BlueprintCallable, Category="Ink") void RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function); - UFUNCTION(BlueprintCallable, Category="Setup") + UFUNCTION(BlueprintCallable, Category="Ink") void RegisterExternalFunction(const FString& functionName, const FExternalFunctionDelegate& function); protected: From 715f29c7bd1c0434f326454f76757e965b98df2a Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 28 Jul 2022 11:14:17 +0200 Subject: [PATCH 08/67] Add ink story to show different unreal interaction + reset tags for unreal getline to keep tags line dependent --- inkcpp/runner_impl.cpp | 1 + .../inkcpp_editor/Private/InkAssetFactory.cpp | 18 +++++- unreal_example.ink | 63 +++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 unreal_example.ink diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 4c81dc0a..3b8334b1 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -429,6 +429,7 @@ namespace ink::runtime::internal #ifdef INK_ENABLE_UNREAL FString runner_impl::getline() { + clear_tags(); FString result{}; bool fill = false; do { diff --git a/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp b/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp index 48e9a19d..a75b9d47 100644 --- a/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp +++ b/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp @@ -7,12 +7,14 @@ #include "ink/compiler.h" #include +#include UInkAssetFactory::UInkAssetFactory(const FObjectInitializer& ObjectInitializer) : UFactory(ObjectInitializer), FReimportHandler() { // Add ink format Formats.Add(FString(TEXT("json;")) + NSLOCTEXT("UInkAssetFactory", "FormatInkJSON", "Ink JSON File").ToString()); + Formats.Add(FString(TEXT("ink;")) + NSLOCTEXT("UInkAssetFactory", "FormatInk", "Ink File").ToString()) // Set class SupportedClass = UInkAsset::StaticClass(); @@ -26,9 +28,23 @@ UInkAssetFactory::UInkAssetFactory(const FObjectInitializer& ObjectInitializer) UObject* UInkAssetFactory::FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) { std::stringstream output; + std::stringstream cmd; + static constexpr std::string ink_suffix{".ink"}; try { - ink::compiler::run(TCHAR_TO_ANSI(*Filename), output); + std::string cFilename = TCHAR_TO_ANSI(*Filename); + if (cFilename.size() > ink_suffix.size() + && std::equal(ink_suffix.rbegin(), ink_suffix.rend(), cFilename.rbegin())) + { + cmd << INKLECAT_CMD << " -o " << cFilename << ".json " << cFilename; + int result = std::system(cmd.str().c_str()) + if (result != 0) { + UE_LOG(InkCpp, Warning, TEXT("Inklecate failed with exit code %i"), result); + return nullptr; + } + cFilename += ".json"; + } + ink::compiler::run(cFilename, output); // Create ink asset UInkAsset* asset = NewObject(InParent, InClass, InName, Flags); diff --git a/unreal_example.ink b/unreal_example.ink new file mode 100644 index 00000000..06f0e8de --- /dev/null +++ b/unreal_example.ink @@ -0,0 +1,63 @@ +->Start +EXTERNAL SetBrightness(x) +=== function SetBrightness(x) === + +VAR brightness = 50 + +LIST background = (a), b, c + +=== Start === +Hello, your personal assistent here. +Why we don't start with some customisation options: +* Yes + -> Settings -> + How The story looks now? much better :) + ->DONE +* I Don't like you. + Then Bye + ->DONE + +=== Settings === + += All +Let Start with the color. +->Color-> +The illumination seems off? +->Brightness-> +And now some custom background :) +->Background-> +->-> + += Color +Which color do like? ++ Magenta + A wired choice ... # setColor_255_0_255 ++ Cyan + A think this will be a intense experience. # setColor_0_255_255 ++ Yellow + A delighting decision. # setColor_255_255_0 +- Do You Like your decision? ++ Yes ->-> ++ No -> Color + += Brightness +Do you like the Brightness level? ++ A bit more please. + ~ brightness += 5 ++ A bit less. + ~ brightness -= 5 ++ The settings {is fine| is now to my compliance!} + ->-> +- ~ SetBrightness(brightness) + ->Brightness + += Background +>> SetBg({background}) +How do you like this image? ++ {background < LIST_MAX(LIST_ALL(background))} Mhe the next please. + ~ background++ ++ {background > LIST_MIN(LIST_ALL(background))} Can you show me the previous please? + ~ background-- ++ This is great + ->-> +- -> Background From 219b16cb6f4debae44c88842785f7627fcde2786 Mon Sep 17 00:00:00 2001 From: JBenda Date: Thu, 28 Jul 2022 21:09:19 +0200 Subject: [PATCH 09/67] Start reworking the event system --- .github/workflows/build.yml | 2 +- inkcpp/include/functional.h | 16 ++++-- inkcpp/list_table.cpp | 2 +- unreal/CMakeLists.txt | 57 ++++++++++++++++++- .../Source/inkcpp/Private/InkThread.cpp | 5 ++ .../Source/inkcpp/Public/InkDelegates.h | 3 +- .../inkcpp/Source/inkcpp/Public/InkThread.h | 4 ++ .../inkcpp_editor/Private/InkAssetFactory.cpp | 22 +++++-- .../Private/inklecate_cmd.cpp.in | 3 + .../inkcpp_editor/inkcpp_editor.Build.cs | 1 + unreal_example.ink | 3 +- 11 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 unreal/inkcpp/Source/inkcpp_editor/Private/inklecate_cmd.cpp.in diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 78376445..b4f77be8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - os: macos-latest artifact: macos name: MacOSX - inklecate_url: https://github.com/inkle/ink/releases/download/0.9.0/inklecate_mac.zip + inklecate_url: https://github.com/inkle/ink/releases/download/v1.0.0/inklecate_mac.zip proof: false - os: windows-latest artifact: win64 diff --git a/inkcpp/include/functional.h b/inkcpp/include/functional.h index a0648993..fcb09ee4 100644 --- a/inkcpp/include/functional.h +++ b/inkcpp/include/functional.h @@ -128,17 +128,23 @@ namespace ink::runtime::internal // calls the underlying delegate using arguments on the stack virtual void call(basic_eval_stack* stack, size_t length, string_table&) override { + constexpr bool RET_VOID = + is_same::return_type, + void>::value; // Create variable array TArray variables; for (size_t i = 0; i < length; i++) { variables.Add(pop(stack)); } - - FInkVar result; - invocableDelegate.ExecuteIfBound(variables, result); - - push(stack, result); + if constexpr (RET_VOID) + { + invocableDelegate.Execute(variables); + push(stack, 0); + } else { + FInkVar result = invocableDelegate.Execute(variables); + push(stack, result); + } } private: D invocableDelegate; diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index 97e80d38..b5f6a89f 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -399,7 +399,7 @@ namespace ink::runtime::internal const data_t* data = getPtr(l.lid); for(int i = 0; i < numLists(); ++i) { if(hasList(data, i)) { - for(int j = listBegin(i); j < _list_end[j]; ++j) { + for(int j = listBegin(i); j < _list_end[i]; ++j) { if(hasFlag(data, j)) { int value = j - listBegin(i); if(res.flag < 0 || value < res.flag) { diff --git a/unreal/CMakeLists.txt b/unreal/CMakeLists.txt index 91909cea..55d8f238 100644 --- a/unreal/CMakeLists.txt +++ b/unreal/CMakeLists.txt @@ -1,2 +1,57 @@ + +include(FetchContent) +set(CMAKE_TLS_VERIFY true) +FetchContent_Declare(inklecate_mac +URL https://github.com/inkle/ink/releases/download/v1.0.0/inklecate_mac.zip +URL_HASH SHA256=B6F4DD1F95C180637CE193DBB5FA6D59AEAFE49A2121A05B7822E6CBBAA6931F +SOURCE_DIR "inkcpp/Resources/inklecate/mac" +) +FetchContent_Declare(inklecate_windows +URL https://github.com/inkle/ink/releases/download/v1.0.0/inklecate_windows.zip +URL_HASH SHA256=A93A5E03109FBD92C636374132253D6D1AEF62B3E060A7FD8CAB97DA883A8327 +SOURCE_DIR "inkcpp/Resources/inklecate/windows" +) +FetchContent_Declare(inklecate_linux +URL https://github.com/inkle/ink/releases/download/v1.0.0/inklecate_linux.zip +URL_HASH SHA256=6E17DB766222998BA0AE5A5DA9857E34896E683B9EC42FAD528C3F8BEA7398EA +SOURCE_DIR "inkcpp/Resources/inklecate/linux" +) +set(INKLECATE_CMD "") +if(WIN32) + FetchContent_MakeAvailable(inklecate_windows) + if(NOT inklecate_windows_SOURCE_DIR) + message(WARNING "failed to downloawd inklecate for windows, " + "the unreal plugin will be unable use a .ink file as asset directly") + else() + set(INKLECATE_CMD "/inklecate/windows") + endif() +elseif(APPLE) + FetchContent_MakeAvailable(inklecate_mac) + if(NOT inklecate_mac_SOURCE_DIRE) + message(WARNING "failed to downloawd inklecate for MacOS, " + "the unreal plugin will be unable use a .ink file as asset directly") + else() + set(INKLECATE_CMD "/inklecate/mac") + endif() +elseif(UNIX) + FetchContent_MakeAvailable(inklecate_linux) + if(NOT inklecate_linux_SOURCE_DIR) + message(WARNING "failed to downloawd inklecate for linux, " + "the unreal plugin will be unable use a .ink file as asset directly") + else() + set(INKLECATE_CMD "/inklecate/linux") + endif() +else() +message(WARNING "unable to determine OS -> unreal component cant compile ink files directly" + " to fix this may define WIN32/APPLE or UNIX manually and run again") +endif() + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/inkcpp/Source/inkcpp_editor/Private/inklecate_cmd.cpp.in" + "${CMAKE_CURRENT_BINARY_DIR}/inkcpp/Source/inkcpp_editor/Private/inklecate_cmd.cpp" +) + # Copy files into destination directory -install(DIRECTORY "inkcpp/" DESTINATION "inkcpp" COMPONENT unreal EXCLUDE_FROM_ALL) +install(DIRECTORY "inkcpp/" DESTINATION "inkcpp" COMPONENT unreal EXCLUDE_FROM_ALL + PATTERN "*.in" EXCLUDE) +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inkcpp/" DESTINATION "inkcpp" COMPONENT unreal EXCLUDE_FROM_ALL) diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 0df078a1..c0f1ddf7 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -39,6 +39,11 @@ void UInkThread::RegisterExternalFunction(const FString& functionName, const FEx mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function); } +void UInkThread::RegisterExternalEvent(const FString& functionName, const FExternalFunctionVoidDelegate& function) +{ + mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function); +} + void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::runner thread) { if (!ensureMsgf(!mbInitialized, TEXT("Thread already initialized!"))) diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkDelegates.h b/unreal/inkcpp/Source/inkcpp/Public/InkDelegates.h index e6625e78..ccab6c93 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkDelegates.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkDelegates.h @@ -9,7 +9,8 @@ DECLARE_DYNAMIC_DELEGATE_TwoParams(FTagFunctionDelegate, UInkThread*, Caller, const TArray&, Params); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FTagFunctionMulticastDelegate, UInkThread*, Caller, const TArray&, Params); -DECLARE_DYNAMIC_DELEGATE_TwoParams(FExternalFunctionDelegate, const TArray&, Arguments, FInkVar&, Result); +DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(FInkVar, FExternalFunctionDelegate, const TArray&, Arguments); +DECLARE_DYNAMIC_DELEGATE_OneParam(FExternalFunctionVoidDelegate, const TArray&, Arguments); DECLARE_DYNAMIC_DELEGATE_TwoParams(FGlobalTagFunctionDelegate, UInkThread*, Caller, const TArray&, Params); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGlobalTagFunctionMulticastDelegate, UInkThread*, Caller, const TArray&, Params); diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h index 6a9b2d7e..714145f9 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkThread.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkThread.h @@ -79,6 +79,10 @@ class INKCPP_API UInkThread : public UObject UFUNCTION(BlueprintCallable, Category="Ink") void RegisterExternalFunction(const FString& functionName, const FExternalFunctionDelegate& function); + + UFUNCTION(BlueprintCallable, Category="Ink") + void RegisterExternalEvent(const FString& functionName, const FExternalFunctionVoidDelegate& function); + protected: virtual void OnStartup_Implementation() { } diff --git a/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp b/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp index a75b9d47..9bf4025e 100644 --- a/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp +++ b/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp @@ -2,19 +2,24 @@ #include "EditorFramework/AssetImportData.h" #include "Misc/FileHelper.h" +#include "Interfaces/IPluginManager.h" #include "InkAsset.h" #include "ink/compiler.h" +#include "inklecate_cmd.cpp" #include #include +DECLARE_LOG_CATEGORY_EXTERN(InkCpp, Log, All); +DEFINE_LOG_CATEGORY(InkCpp); + UInkAssetFactory::UInkAssetFactory(const FObjectInitializer& ObjectInitializer) : UFactory(ObjectInitializer), FReimportHandler() { // Add ink format Formats.Add(FString(TEXT("json;")) + NSLOCTEXT("UInkAssetFactory", "FormatInkJSON", "Ink JSON File").ToString()); - Formats.Add(FString(TEXT("ink;")) + NSLOCTEXT("UInkAssetFactory", "FormatInk", "Ink File").ToString()) + Formats.Add(FString(TEXT("ink;")) + NSLOCTEXT("UInkAssetFactory", "FormatInk", "Ink File").ToString()); // Set class SupportedClass = UInkAsset::StaticClass(); @@ -29,22 +34,29 @@ UObject* UInkAssetFactory::FactoryCreateFile(UClass* InClass, UObject* InParent, { std::stringstream output; std::stringstream cmd; - static constexpr std::string ink_suffix{".ink"}; + static const std::string inklecate_cmd{ INKLECATE_CMD }; + static const std::string ink_suffix{".ink"}; try { std::string cFilename = TCHAR_TO_ANSI(*Filename); if (cFilename.size() > ink_suffix.size() && std::equal(ink_suffix.rbegin(), ink_suffix.rend(), cFilename.rbegin())) { - cmd << INKLECAT_CMD << " -o " << cFilename << ".json " << cFilename; - int result = std::system(cmd.str().c_str()) + if(inklecate_cmd.size() == 0) { + UE_LOG(InkCpp, Warning, TEXT("Inklecate provided with the plugin, please import ink.json files")); + return nullptr; + } + cmd + << TCHAR_TO_ANSI(*IPluginManager::Get().FindPlugin(TEXT("InkCPP"))->GetContentDir()) + << inklecate_cmd << " -o " << cFilename << ".json " << cFilename; + int result = std::system(cmd.str().c_str()); if (result != 0) { UE_LOG(InkCpp, Warning, TEXT("Inklecate failed with exit code %i"), result); return nullptr; } cFilename += ".json"; } - ink::compiler::run(cFilename, output); + ink::compiler::run(cFilename.c_str(), output); // Create ink asset UInkAsset* asset = NewObject(InParent, InClass, InName, Flags); diff --git a/unreal/inkcpp/Source/inkcpp_editor/Private/inklecate_cmd.cpp.in b/unreal/inkcpp/Source/inkcpp_editor/Private/inklecate_cmd.cpp.in new file mode 100644 index 00000000..f250c6d2 --- /dev/null +++ b/unreal/inkcpp/Source/inkcpp_editor/Private/inklecate_cmd.cpp.in @@ -0,0 +1,3 @@ +#ifndef INKLECATE_CMD +#define INKLECATE_CMD "@INKLECATE_CMD@" +#endif \ No newline at end of file diff --git a/unreal/inkcpp/Source/inkcpp_editor/inkcpp_editor.Build.cs b/unreal/inkcpp/Source/inkcpp_editor/inkcpp_editor.Build.cs index 413d0faf..137a87d2 100644 --- a/unreal/inkcpp/Source/inkcpp_editor/inkcpp_editor.Build.cs +++ b/unreal/inkcpp/Source/inkcpp_editor/inkcpp_editor.Build.cs @@ -31,6 +31,7 @@ public inkcpp_editor(ReadOnlyTargetRules Target) : base(Target) new string[] { "Core", + "Projects" // ... add other public dependencies that you statically link with here ... } ); diff --git a/unreal_example.ink b/unreal_example.ink index 06f0e8de..f3312b5c 100644 --- a/unreal_example.ink +++ b/unreal_example.ink @@ -1,6 +1,7 @@ ->Start -EXTERNAL SetBrightness(x) +// EXTERNAL SetBrightness(x) === function SetBrightness(x) === + oho VAR brightness = 50 From 522e13beae046828fb30fc405ca258241ba5a9cb Mon Sep 17 00:00:00 2001 From: JBenda Date: Sat, 30 Jul 2022 21:41:20 +0200 Subject: [PATCH 10/67] Set correct _last_char for get_alloc output --- inkcpp/output.cpp | 2 +- inkcpp/runner_impl.cpp | 1 - unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 4acd0763..0f08367a 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -327,7 +327,7 @@ namespace ink _size = start; // Return processed string - end = clean_string(buffer, buffer+length); + end = clean_string(buffer, buffer + c_str_len(buffer)); *end = 0; _last_char = end[-1]; if constexpr (RemoveTail) { diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 3b8334b1..08233947 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -366,7 +366,6 @@ namespace ink::runtime::internal // Read line into std::string std::string part; _output >> part; - result += part; fill = _output.last_char() == ' '; } while(_ptr != nullptr && _output.last_char() != '\n'); diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index c0f1ddf7..488a4f6b 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -101,7 +101,7 @@ bool UInkThread::ExecuteInternal() { // This is a special version of the tag function call // Expected: >> MyTagFunction(Arg1, Arg2, Arg3) - FRegexPattern pattern = FRegexPattern(TEXT("^>>\\s*(\\w+)\\s*(\\((([\\w ]+)(,\\s*([\\w ]+))*)?\\))?$")); + FRegexPattern pattern = FRegexPattern(TEXT("^>>\\s*(\\w+)(\\((\\s*(\\w+)\\s*(,\\s*(\\w+)\\s*)*)?\\))?$")); FRegexMatcher matcher = FRegexMatcher(pattern, line); if (matcher.FindNext()) { From a5e38c3a9eb8433e9e35a15102889aa2929fc8c4 Mon Sep 17 00:00:00 2001 From: JBenda Date: Sun, 31 Jul 2022 00:06:21 +0200 Subject: [PATCH 11/67] Add *.ink loading support for unreal --- unreal/CMakeLists.txt | 6 ++-- .../inkcpp_editor/Private/InkAssetFactory.cpp | 32 +++++++++++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/unreal/CMakeLists.txt b/unreal/CMakeLists.txt index 55d8f238..a567aeae 100644 --- a/unreal/CMakeLists.txt +++ b/unreal/CMakeLists.txt @@ -23,7 +23,7 @@ if(WIN32) message(WARNING "failed to downloawd inklecate for windows, " "the unreal plugin will be unable use a .ink file as asset directly") else() - set(INKLECATE_CMD "/inklecate/windows") + set(INKLECATE_CMD "Resources/inklecate/windows/inklecate.exe") endif() elseif(APPLE) FetchContent_MakeAvailable(inklecate_mac) @@ -31,7 +31,7 @@ elseif(APPLE) message(WARNING "failed to downloawd inklecate for MacOS, " "the unreal plugin will be unable use a .ink file as asset directly") else() - set(INKLECATE_CMD "/inklecate/mac") + set(INKLECATE_CMD "Resources/inklecate/mac/inklecate") endif() elseif(UNIX) FetchContent_MakeAvailable(inklecate_linux) @@ -39,7 +39,7 @@ elseif(UNIX) message(WARNING "failed to downloawd inklecate for linux, " "the unreal plugin will be unable use a .ink file as asset directly") else() - set(INKLECATE_CMD "/inklecate/linux") + set(INKLECATE_CMD "Resources/inklecate/linux/inklecate") endif() else() message(WARNING "unable to determine OS -> unreal component cant compile ink files directly" diff --git a/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp b/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp index 9bf4025e..45c8aa3d 100644 --- a/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp +++ b/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp @@ -2,6 +2,7 @@ #include "EditorFramework/AssetImportData.h" #include "Misc/FileHelper.h" +#include "Misc/Paths.h" #include "Interfaces/IPluginManager.h" #include "InkAsset.h" @@ -10,6 +11,8 @@ #include #include +#include +#include DECLARE_LOG_CATEGORY_EXTERN(InkCpp, Log, All); DEFINE_LOG_CATEGORY(InkCpp); @@ -33,30 +36,47 @@ UInkAssetFactory::UInkAssetFactory(const FObjectInitializer& ObjectInitializer) UObject* UInkAssetFactory::FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) { std::stringstream output; - std::stringstream cmd; + std::stringstream cmd{}; static const std::string inklecate_cmd{ INKLECATE_CMD }; static const std::string ink_suffix{".ink"}; try { std::string cFilename = TCHAR_TO_ANSI(*Filename); + bool use_temp_file = false; if (cFilename.size() > ink_suffix.size() && std::equal(ink_suffix.rbegin(), ink_suffix.rend(), cFilename.rbegin())) { + use_temp_file = true; if(inklecate_cmd.size() == 0) { UE_LOG(InkCpp, Warning, TEXT("Inklecate provided with the plugin, please import ink.json files")); return nullptr; } + using path = std::filesystem::path; + path path_bin(TCHAR_TO_ANSI(*IPluginManager::Get().FindPlugin(TEXT("InkCPP"))->GetBaseDir()), path::format::generic_format); + path_bin.make_preferred(); + path_bin /= path(inklecate_cmd, path::format::generic_format).make_preferred(); + path story_path(cFilename, path::format::generic_format); + story_path.make_preferred(); + cFilename = std::tmpnam(nullptr); + path json_path(cFilename, path::format::generic_format); + json_path.make_preferred(); cmd - << TCHAR_TO_ANSI(*IPluginManager::Get().FindPlugin(TEXT("InkCPP"))->GetContentDir()) - << inklecate_cmd << " -o " << cFilename << ".json " << cFilename; - int result = std::system(cmd.str().c_str()); + // if std::system start with a quote, the pair of qoute is removed, which leads to errors with pathes with spaces + // but if the quote is not the first symbol it works fine (a"b" is glued to ab from bash) + << path_bin.string()[0] << "\"" << (path_bin.string().c_str() + 1) << "\"" + << " -o \"" << json_path.string() << "\" " + << '"' << story_path.string() << "\" 2>&1"; + auto cmd_str = cmd.str(); + int result = std::system(cmd_str.c_str()); if (result != 0) { - UE_LOG(InkCpp, Warning, TEXT("Inklecate failed with exit code %i"), result); + UE_LOG(InkCpp, Warning, TEXT("Inklecate failed with exit code %i, executed was: '%s'"), result, *FString(cmd_str.c_str())); return nullptr; } - cFilename += ".json"; } ink::compiler::run(cFilename.c_str(), output); + if(use_temp_file) { + std::filesystem::remove(cFilename); + } // Create ink asset UInkAsset* asset = NewObject(InParent, InClass, InName, Flags); From 9c7fe2520179be620f1152fa1380d1c490f672ca Mon Sep 17 00:00:00 2001 From: JBenda Date: Sun, 31 Jul 2022 00:22:06 +0200 Subject: [PATCH 12/67] Uses output.get() instead of stream operator --- inkcpp/runner_impl.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 08233947..1afb6972 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -364,8 +364,7 @@ namespace ink::runtime::internal // Advance interpreter one line advance_line(); // Read line into std::string - std::string part; - _output >> part; + result += _output.get(); fill = _output.last_char() == ' '; } while(_ptr != nullptr && _output.last_char() != '\n'); From 56e2aee85b23064734e7196f126ad7f8bf1c9698 Mon Sep 17 00:00:00 2001 From: JBenda Date: Sun, 31 Jul 2022 00:50:29 +0200 Subject: [PATCH 13/67] some formation for example story --- unreal_example.ink | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/unreal_example.ink b/unreal_example.ink index f3312b5c..f94d6740 100644 --- a/unreal_example.ink +++ b/unreal_example.ink @@ -1,5 +1,5 @@ ->Start -// EXTERNAL SetBrightness(x) +EXTERNAL SetBrightness(x) === function SetBrightness(x) === oho @@ -10,11 +10,11 @@ LIST background = (a), b, c === Start === Hello, your personal assistent here. Why we don't start with some customisation options: -* Yes +* [Yes] -> Settings -> How The story looks now? much better :) ->DONE -* I Don't like you. +* [I Don't like you.] Then Bye ->DONE @@ -31,23 +31,23 @@ And now some custom background :) = Color Which color do like? -+ Magenta ++ [Magenta] A wired choice ... # setColor_255_0_255 -+ Cyan ++ [Cyan] A think this will be a intense experience. # setColor_0_255_255 -+ Yellow ++ [Yellow] A delighting decision. # setColor_255_255_0 - Do You Like your decision? -+ Yes ->-> -+ No -> Color ++ [Yes] ->-> ++ [No] -> Color = Brightness Do you like the Brightness level? -+ A bit more please. ++ [A bit more please.] ~ brightness += 5 -+ A bit less. ++ [A bit less.] ~ brightness -= 5 -+ The settings {is fine| is now to my compliance!} ++ [The settings {is fine| is now to my compliance!}] ->-> - ~ SetBrightness(brightness) ->Brightness @@ -55,10 +55,10 @@ Do you like the Brightness level? = Background >> SetBg({background}) How do you like this image? -+ {background < LIST_MAX(LIST_ALL(background))} Mhe the next please. ++ {background < LIST_MAX(LIST_ALL(background))} [Mhe the next please.] ~ background++ -+ {background > LIST_MIN(LIST_ALL(background))} Can you show me the previous please? ++ {background > LIST_MIN(LIST_ALL(background))} [Can you show me the previous please?] ~ background-- -+ This is great ++ [This is great] ->-> - -> Background From 71cfc65f6b6cf60d114c48f2dd106eda5a6b9154 Mon Sep 17 00:00:00 2001 From: JBenda Date: Sun, 31 Jul 2022 18:47:05 +0200 Subject: [PATCH 14/67] Fixing SetGlobalVariable to support Strings --- inkcpp/functional.cpp | 51 ++----------- inkcpp/globals_impl.cpp | 5 +- inkcpp/include/functional.h | 16 +++- inkcpp/value.cpp | 2 +- inkcpp_cl/inkcpp_cl.cpp | 2 - .../Source/inkcpp/Private/InkRuntime.cpp | 24 +++++- .../inkcpp/Source/inkcpp/Private/InkVar.cpp | 65 ++++++++-------- .../inkcpp/Source/inkcpp/Public/InkRuntime.h | 2 +- unreal/inkcpp/Source/inkcpp/Public/InkVar.h | 74 +++++++++++++++---- unreal_example.ink | 11 ++- 10 files changed, 149 insertions(+), 103 deletions(-) diff --git a/inkcpp/functional.cpp b/inkcpp/functional.cpp index 7deec6fa..034024ab 100644 --- a/inkcpp/functional.cpp +++ b/inkcpp/functional.cpp @@ -51,57 +51,20 @@ namespace ink::runtime::internal } #endif #ifdef INK_ENABLE_UNREAL - template<> - FString function_base::pop(basic_eval_stack* stack) { - return FString(pop(stack)); - } - template<> FInkVar function_base::pop(basic_eval_stack* stack) { - value v = stack->pop(); - switch (v.type()) - { - case value_type::null: - case value_type::divert: - inkFail("Trying to pass null or divert as ink parameter to external function"); - break; - case value_type::int32: - return FInkVar(v.get()); - case value_type::uint32: { - uint32_t n = v.get(); - inkAssert(n < (~(1<<31)), "Value to large to cast without overlfow to int!"); - return FInkVar(static_cast(n)); - } - case value_type::float32: - return FInkVar(v.get()); - case value_type::string: - return FInkVar(FString(v.get().str)); - } - - return FInkVar(); + return FInkVar(stack->pop().to_interface_value()); } template<> - void function_base::push(basic_eval_stack* stack, const FInkVar& value) + void function_base::push(basic_eval_stack* stack, const ink::runtime::value& value) { - switch (value.type) - { - case EInkVarType::None: - { - internal::value v; - stack->push(v); - } - break; - case EInkVarType::Int: - stack->push(internal::value{}.set(value.intVar)); - break; - case EInkVarType::Float: - stack->push(internal::value{}.set(value.floatVar)); - break; - case EInkVarType::String: - inkFail("NOT IMPLEMENTED"); // TODO: String support - return; + internal::value val{}; + if(val.set(value)) { + stack->push(val); + } else { + inkFail("unable to set variable?"); } } #endif diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index bb45b92b..28fe4a61 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -119,7 +119,8 @@ namespace ink::runtime::internal bool globals_impl::set_var(hash_t name, const ink::runtime::value& val) { auto* var = get_variable(name); if (!var) { return false; } - if ( val.type == ink::runtime::value::Type::String && var->type() == value_type::string) { + if ( val.type == ink::runtime::value::Type::String) { + if (!(var->type() == value_type::none || var->type() == value_type::string)) { return false; } size_t size = 0; char* ptr; for ( const char* i = val.v_string; *i; ++i ) { ++size; } @@ -143,12 +144,12 @@ namespace ink::runtime::internal // If no way to move there, then there are no globals. if (!run->move_to(hash_string("global decl"))) { - _globals_initialized = true; return; } // execute one line to startup the globals run->getline_silent(); + _globals_initialized = true; } void globals_impl::gc() diff --git a/inkcpp/include/functional.h b/inkcpp/include/functional.h index fcb09ee4..96a1fac7 100644 --- a/inkcpp/include/functional.h +++ b/inkcpp/include/functional.h @@ -126,7 +126,7 @@ namespace ink::runtime::internal function_array_delegate(const D& del) : invocableDelegate(del) { } // calls the underlying delegate using arguments on the stack - virtual void call(basic_eval_stack* stack, size_t length, string_table&) override + virtual void call(basic_eval_stack* stack, size_t length, string_table& strings) override { constexpr bool RET_VOID = is_same::return_type, @@ -142,7 +142,19 @@ namespace ink::runtime::internal invocableDelegate.Execute(variables); push(stack, 0); } else { - FInkVar result = invocableDelegate.Execute(variables); + + auto ret = invocableDelegate.Execute(variables); + ink::runtime::value result = ret.to_value(); + if(result.type == ink::runtime::value::Type::String) { + const char* src = result.v_string; + size_t len = string_handler::length(src); + char* buffer = allocate(strings, len + 1); + char* ptr = buffer; + while(*src != '\0') + *(ptr++) = *(src++); + *ptr = 0; + result.v_string = buffer; + } push(stack, result); } } diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index 108fe487..db241f26 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -92,7 +92,7 @@ namespace ink::runtime::internal } bool value::set( const ink::runtime::value& val ) { auto var = value( val ); - if ( var.type() == type() ) { + if ( type() == value_type::none || var.type() == type() ) { *this = var; return true; } diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 53a08209..c0e19279 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -140,12 +140,10 @@ int main(int argc, const char** argv) // Start runner runner thread = myInk->new_runner(); - while (true) { while (thread->can_continue()) std::cout << thread->getline(); - if (thread->has_tags()){ std::cout << "# tags: "; for (int i = 0; i < thread->num_tags(); ++i) { diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index be7ca357..4e6bf120 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -39,6 +39,8 @@ void AInkRuntime::BeginPlay() // create globals mpGlobals = mpRuntime->new_globals(); + // initialize globals + mpRuntime->new_runner(mpGlobals); } else { @@ -180,13 +182,27 @@ void AInkRuntime::PopExclusiveThread(UInkThread* Thread) mExclusiveStack.Remove(Thread); } -void AInkRuntime::GetGlobalVariable(const FString& name, FInkVar& value) const { +FInkVar AInkRuntime::GetGlobalVariable(const FString& name) const { ink::optional var = mpGlobals->get(TCHAR_TO_ANSI(*name)); - inkAssert(var, "Reguested variable does not exists!"); - if(var) { value = *var; } + (var, "Reguested variable does not exists!"); + if(var) { return FInkVar(*var); } + else { UE_LOG(InkCpp, Warning, TEXT("Failed to find global variable with name: %s"), *name); } + return FInkVar{}; } void AInkRuntime::SetGlobalVariable(const FString& name, const FInkVar& value) { bool success = mpGlobals->set(TCHAR_TO_ANSI(*name), value.to_value()); - inkAssert(success, "Unable to set variable"); + if(!success) { + UE_LOG(InkCpp, Warning, TEXT("Filed to set global variable with name: %s"), *name); + ink::optional var = mpGlobals->get(TCHAR_TO_ANSI(*name)); + if(var) { + UE_LOG(InkCpp, Warning, + TEXT("Reason: wrong type!, got: %i, expected: %i"), + static_cast(value.to_value().type), + static_cast(var->type) ); + } else { + UE_LOG(InkCpp, Warning, TEXT("Reason: no variable with this name exists! '%s'"), + *name); + } + } } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp index 04784974..b62f38b6 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp @@ -7,25 +7,21 @@ FInkVar::FInkVar(ink::runtime::value val) : FInkVar() { using v_types = ink::runtime::value::Type; switch(val.type) { case v_types::Bool: - type = EInkVarType::Int; - intVar = static_cast(val.v_bool); + value.SetSubtype(val.v_bool); break; case v_types::Uint32: - type = EInkVarType::Int; - /// @TODO: add warning if overflows - intVar = static_cast(val.v_uint32); + UE_LOG(InkCpp, Warning, TEXT("Converting uint to int, this will cause trouble if writing it back to ink (with SetGlobalVariable)!")); + value.SetSubtype(val.v_bool); + // value.SetSubtype(val.v_uint32); break; case v_types::Int32: - type = EInkVarType::Int; - intVar = val.v_int32; + value.SetSubtype(val.v_int32); break; case v_types::String: - type = EInkVarType::String; - stringVar = FString(val.v_string); + value.SetSubtype(FString(val.v_string)); break; case v_types::Float: - type = EInkVarType::Float; - floatVar = val.v_float; + value.SetSubtype(val.v_float); break; default: inkFail("unknown type!, failed to convert ink::value to InkVar"); @@ -33,15 +29,17 @@ FInkVar::FInkVar(ink::runtime::value val) : FInkVar() { } ink::runtime::value FInkVar::to_value() const { - switch(type) { + switch(type()) { case EInkVarType::Int: - return ink::runtime::value(intVar); + return ink::runtime::value(value.GetSubtype()); case EInkVarType::Float: - return ink::runtime::value(floatVar); + return ink::runtime::value(value.GetSubtype()); case EInkVarType::String: - return ink::runtime::value(TCHAR_TO_ANSI(*stringVar)); - case EInkVarType::None: - inkFail("None type cant be passed"); + return ink::runtime::value(reinterpret_cast(utf8.GetData())); + case EInkVarType::Bool: + return ink::runtime::value(value.GetSubtype()); + case EInkVarType::UInt: + return ink::runtime::value(value.GetSubtype()); default: inkFail("Unsupported type"); } @@ -50,46 +48,48 @@ ink::runtime::value FInkVar::to_value() const { FString UInkVarLibrary::Conv_InkVarString(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type == EInkVarType::String, TEXT("InkVar is not a String Type!"))) - return InkVar.stringVar; + if (ensureMsgf(InkVar.type() == EInkVarType::String, TEXT("InkVar is not a String Type!"))) + return InkVar.value.GetSubtype(); return FString(TEXT("")); } int UInkVarLibrary::Conv_InkVarInt(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type == EInkVarType::Int, TEXT("InkVar is not an Int Type!"))) - return InkVar.intVar; + if (ensureMsgf(InkVar.type() == EInkVarType::Int, TEXT("InkVar is not an Int Type!"))) + return InkVar.value.GetSubtype(); return 0; } float UInkVarLibrary::Conv_InkVarFloat(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type == EInkVarType::Float, TEXT("InkVar is not a Float Type!"))) - return InkVar.floatVar; + if (ensureMsgf(InkVar.type() == EInkVarType::Float, TEXT("InkVar is not a Float Type!"))) + return InkVar.value.GetSubtype(); return 0.f; } FName UInkVarLibrary::Conv_InkVarName(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type == EInkVarType::String, TEXT("InkVar is not a String Type!"))) - return FName(*InkVar.stringVar); + if (ensureMsgf(InkVar.type() == EInkVarType::String, TEXT("InkVar is not a String Type!"))) + return FName(*InkVar.value.GetSubtype()); return NAME_None; } FText UInkVarLibrary::Conv_InkVarText(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type == EInkVarType::String, TEXT("InkVar is not a String Type!"))) - return FText::FromString(InkVar.stringVar); + if (ensureMsgf(InkVar.type() == EInkVarType::String, TEXT("InkVar is not a String Type!"))) + return FText::FromString(InkVar.value.GetSubtype()); return FText::GetEmpty(); } bool UInkVarLibrary::Conv_InkVarBool(const FInkVar& InkVar) { - if (ensureMsgf(InkVar.type == EInkVarType::Int, TEXT("InkVar is not an Int Type!"))) - return InkVar.intVar > 0; + if (ensureMsgf(InkVar.type() == EInkVarType::Bool, TEXT("InkVar is not an Int Type!"))) + return InkVar.value.GetSubtype(); return false; } + + FInkVar UInkVarLibrary::Conv_StringInkVar(const FString& String) { return FInkVar(String); @@ -110,7 +110,12 @@ FInkVar UInkVarLibrary::Conv_TextInkVar(const FText& Text) return FInkVar(Text.ToString()); } +FInkVar UInkVarLibrary::Conv_NameInkVar(const FName& Name) +{ + return FInkVar(Name.ToString()); +} + FInkVar UInkVarLibrary::Conv_BoolInkVar(bool Boolean) { - return FInkVar(Boolean ? 1 : 0); + return FInkVar(Boolean); } diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index 80848b58..fd5899d0 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -48,7 +48,7 @@ class INKCPP_API AInkRuntime : public AActor void HandleTagFunction(UInkThread* Caller, const TArray& Params); UFUNCTION(BlueprintCallable, Category="Ink") - void GetGlobalVariable(const FString& name, FInkVar& value) const; + FInkVar GetGlobalVariable(const FString& name) const; UFUNCTION(BlueprintCallable, Category="Ink") void SetGlobalVariable(const FString& name, const FInkVar& value); diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h index 2080a197..fc898f38 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h @@ -5,6 +5,8 @@ #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "UObject/TextProperty.h" +#include "Containers/Union.h" +#include "Containers/StringConv.h" #include "InkVar.generated.h" @@ -15,6 +17,8 @@ enum class EInkVarType : uint8 { Float, Int, + UInt, + Bool, String, None }; @@ -26,26 +30,51 @@ struct INKCPP_API FInkVar { GENERATED_BODY() - FInkVar() { type = EInkVarType::None; } - - FInkVar(float val) { type = EInkVarType::Float; floatVar = val; } - FInkVar(int val) { type = EInkVarType::Int; intVar = val; } - FInkVar(FString val) { type = EInkVarType::String; stringVar = val; } + FInkVar() {} + + FInkVar(float val) { value.SetSubtype(val); } + FInkVar(int val) { value.SetSubtype(val); } + FInkVar(unsigned val) + { + UE_LOG(InkCpp, Warning, TEXT("Converting unsigned to signed int, since missing blueprint support for unsigned type")); + value.SetSubtype(val); + } // TODO: change if we find a way to support unsigned values in blueprints + FInkVar(bool val) { value.SetSubtype(val); } + FInkVar(FString val) { + value.SetSubtype(val); + BufferDecodedString(); + } FInkVar(ink::runtime::value val); ink::runtime::value to_value() const; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ink") - EInkVarType type; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ink") - float floatVar; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ink") - int intVar; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ink") - FString stringVar; + // allow changing via Editor, but not in controle flow, it is just a wrapper type to create a new one + // UPROPERTY(EditAnywhere, Category = "Ink") + TUnion value; + + TArray utf8{}; + + EInkVarType type() const { + uint8 id = value.GetCurrentSubtypeIndex(); + if(id >= static_cast(EInkVarType::None)) + { return EInkVarType::None; } + else + { return static_cast(id); } + } +private: + void BufferDecodedString() { + FTCHARToUTF8 Convert(*value.GetSubtype()); + utf8.SetNum(Convert.Length() + 1); + UTF8CHAR* dst = utf8.GetData(); + [this,&dst](const UTF8CHAR* src){ + int i = 0; + while(*src) { + *dst++ = *src++; + } + *dst = static_cast(0); + }(reinterpret_cast(Convert.Get())); + } }; UCLASS() @@ -54,6 +83,11 @@ class INKCPP_API UInkVarLibrary : public UBlueprintFunctionLibrary GENERATED_BODY() public: + UFUNCTION(BlueprintPure, meta = (DisplayName = "Var Type", BlueprintAutocast), Category="Ink") + inline EInkVarType InkVarType(const FInkVar& InkVar) const { + return InkVar.type(); + } + UFUNCTION(BlueprintPure, meta = (DisplayName = "String (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") static FString Conv_InkVarString(const FInkVar& InkVar); @@ -71,7 +105,11 @@ class INKCPP_API UInkVarLibrary : public UBlueprintFunctionLibrary UFUNCTION(BlueprintPure, meta = (DisplayName = "Bool (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") static bool Conv_InkVarBool(const FInkVar& InkVar); + + // UFUNCTION(BlueprintPure, meta = (DisplayName = "UInt (Ink Var)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + // static unsigned Conv_InkVarUInt(const FInkVar& InkVar); + UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (String)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") static FInkVar Conv_StringInkVar(const FString& String); @@ -83,7 +121,13 @@ class INKCPP_API UInkVarLibrary : public UBlueprintFunctionLibrary UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (Text)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") static FInkVar Conv_TextInkVar(const FText& Text); + + UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (Name)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + static FInkVar Conv_NameInkVar(const FName& Name); UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (Bool)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") static FInkVar Conv_BoolInkVar(bool Boolean); + + // UFUNCTION(BlueprintPure, meta = (DisplayName = "Ink Var (UInt)", CompactNodeTitle = "->", BlueprintAutocast), Category = "Ink") + // static FInkVar Conv_UIntInkVar(unsigned Number); }; \ No newline at end of file diff --git a/unreal_example.ink b/unreal_example.ink index f94d6740..64a3e7d9 100644 --- a/unreal_example.ink +++ b/unreal_example.ink @@ -1,14 +1,21 @@ ->Start EXTERNAL SetBrightness(x) === function SetBrightness(x) === - oho + ~ return + +EXTERNAL GetGreeting() +=== function GetGreeting() === + ~ return "Hey, " VAR brightness = 50 +VAR date = "???" + LIST background = (a), b, c === Start === -Hello, your personal assistent here. +{GetGreeting()}, your personal assistent here. # Story Start +Today is the {date} Why we don't start with some customisation options: * [Yes] -> Settings -> From 633704663fd8ff85bafe3decfdc48a44f28b9b61 Mon Sep 17 00:00:00 2001 From: JBenda Date: Sun, 31 Jul 2022 19:22:09 +0200 Subject: [PATCH 15/67] Find notation for unreal to create GetGlobalVariable --- unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp | 2 +- unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index 4e6bf120..09ec75e7 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -182,7 +182,7 @@ void AInkRuntime::PopExclusiveThread(UInkThread* Thread) mExclusiveStack.Remove(Thread); } -FInkVar AInkRuntime::GetGlobalVariable(const FString& name) const { +FInkVar AInkRuntime::GetGlobalVariable(const FString& name) { ink::optional var = mpGlobals->get(TCHAR_TO_ANSI(*name)); (var, "Reguested variable does not exists!"); if(var) { return FInkVar(*var); } diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index fd5899d0..e8951d21 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -48,7 +48,7 @@ class INKCPP_API AInkRuntime : public AActor void HandleTagFunction(UInkThread* Caller, const TArray& Params); UFUNCTION(BlueprintCallable, Category="Ink") - FInkVar GetGlobalVariable(const FString& name) const; + FInkVar GetGlobalVariable(const FString& name); UFUNCTION(BlueprintCallable, Category="Ink") void SetGlobalVariable(const FString& name, const FInkVar& value); From 94bfca494d6fd7e82ec301e812e9d1d3fb202231 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 5 Aug 2022 11:33:04 +0200 Subject: [PATCH 16/67] Fix indention --- inkcpp/runner_impl.cpp | 64 +++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 1afb6972..03f2407d 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -203,45 +203,45 @@ namespace ink::runtime::internal bool first = true; // Start moving forward (or backwards) if(inBound && (offset == nullptr || !reverse&&offset<=dest || reverse&&offset>dest) ) - while (_story->iterate_containers(iter, container_id, offset, reverse)) - { - // Break when we've past the destination - if (!reverse && offset > dest || reverse && offset <= dest) { - // jump back to start of same container - if(first && reverse && offset == dest - && _container.top() == container_id) { - // check if it was start flag - auto con_id = container_id; - _story->iterate_containers(iter, container_id, offset, true); - if(offset == nullptr || con_id == container_id) - { - _globals->visit(container_id); + while (_story->iterate_containers(iter, container_id, offset, reverse)) + { + // Break when we've past the destination + if (!reverse && offset > dest || reverse && offset <= dest) { + // jump back to start of same container + if(first && reverse && offset == dest + && _container.top() == container_id) { + // check if it was start flag + auto con_id = container_id; + _story->iterate_containers(iter, container_id, offset, true); + if(offset == nullptr || con_id == container_id) + { + _globals->visit(container_id); + } } + break; } - break; - } - first = false; + first = false; - // Two cases: + // Two cases: - // (1) Container iterator has the same value as the top of the stack. - // This means that this is an end marker for the container we're in - if (!_container.empty() && _container.top() == container_id) - { - if (_container.size() == pos) - pos--; + // (1) Container iterator has the same value as the top of the stack. + // This means that this is an end marker for the container we're in + if (!_container.empty() && _container.top() == container_id) + { + if (_container.size() == pos) + pos--; - // Get out of that container - _container.pop(); - } + // Get out of that container + _container.pop(); + } - // (2) This must be the entrance marker for a new container. Enter it - else - { - // Push it - _container.push(container_id); + // (2) This must be the entrance marker for a new container. Enter it + else + { + // Push it + _container.push(container_id); + } } - } // Iterate over the container stack marking any _new_ entries as "visited" if (record_visits) From 54c31f7e7988b28f132d0d456ec1f233a4dd152c Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 5 Aug 2022 13:43:23 +0200 Subject: [PATCH 17/67] Apply more unreal fixes --- inkcpp/collections/restorable.h | 2 +- inkcpp/executioner.h | 4 +-- inkcpp/header.cpp | 4 +-- inkcpp/include/functional.h | 3 ++ inkcpp/numeric_operations.h | 12 +++++--- inkcpp/operations.h | 2 +- inkcpp/output.cpp | 2 +- inkcpp/runner_impl.cpp | 11 ++++---- inkcpp/runner_impl.h | 28 +++++++------------ inkcpp/simple_restorable_stack.h | 4 +-- inkcpp/stack.cpp | 2 +- inkcpp/string_utils.h | 6 ++-- inkcpp/value.cpp | 1 + inkcpp/value.h | 3 +- inkcpp_compiler/binary_emitter.cpp | 2 +- shared/public/system.h | 8 +++--- .../Source/inkcpp/Private/InkRuntime.cpp | 1 - .../inkcpp/Source/inkcpp/Private/InkVar.cpp | 1 + 18 files changed, 50 insertions(+), 46 deletions(-) diff --git a/inkcpp/collections/restorable.h b/inkcpp/collections/restorable.h index 598a02c4..21a2e8b3 100644 --- a/inkcpp/collections/restorable.h +++ b/inkcpp/collections/restorable.h @@ -329,7 +329,7 @@ namespace ink::runtime::internal protected: // Called when we run out of space in buffer. virtual void overflow(ElementType*& buffer, size_t& size) { - throw ink_exception("Restorable run out of memory!"); + inkFail("Restorable run out of memory!"); } private: diff --git a/inkcpp/executioner.h b/inkcpp/executioner.h index 532b3b28..78703aa5 100644 --- a/inkcpp/executioner.h +++ b/inkcpp/executioner.h @@ -79,7 +79,7 @@ namespace ink::runtime::internal { typed_executer(const T& t) {} void operator()(value_type, basic_eval_stack&, value*) { - throw ink_exception("Operation for value not supported!"); + inkFail("Operation for value not supported!"); } }; @@ -140,7 +140,7 @@ namespace ink::runtime::internal { template executer_imp(const T& t) {} void operator()(Command, basic_eval_stack&) { - throw ink_exception("requested command was not found!"); + inkFail("requested command was not found!"); } }; diff --git a/inkcpp/header.cpp b/inkcpp/header.cpp index e80b2858..57faf733 100644 --- a/inkcpp/header.cpp +++ b/inkcpp/header.cpp @@ -27,11 +27,11 @@ namespace ink::internal { res.ink_bin_version_number = swap_bytes(*reinterpret_cast(ptr)); } else { - throw ink_exception("Failed to parse endian encoding!"); + inkFail("Failed to parse endian encoding!"); } if (res.ink_bin_version_number != InkBinVersion) { - throw ink_exception("InkCpp-version mismatch: file was compiled with different InkCpp-version!"); + inkFail("InkCpp-version mismatch: file was compiled with different InkCpp-version!"); } return res; } diff --git a/inkcpp/include/functional.h b/inkcpp/include/functional.h index 96a1fac7..96b029ad 100644 --- a/inkcpp/include/functional.h +++ b/inkcpp/include/functional.h @@ -3,6 +3,9 @@ #include "traits.h" #include "system.h" +#ifdef INK_ENABLE_UNREAL +#include "../InkVar.h" +#endif namespace ink::runtime::internal { class basic_eval_stack; diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index 9d87fa29..ac5e3bdc 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -52,7 +52,8 @@ namespace ink::runtime::internal { inline typename value::ret::type numeric_cast(const value& v) { if (to == v.type()) { return v.get(); } else { - throw ink_exception("invalid numeric_cast!"); + inkFail("invalid numeric_cast!"); + return 0; } } @@ -66,7 +67,8 @@ namespace ink::runtime::internal { case value_type::boolean: return static_cast(v.get()); default: - throw ink_exception("invalid cast to uint!"); + inkFail("invalid cast to uint!"); + return 0; } } @@ -78,7 +80,8 @@ namespace ink::runtime::internal { case value_type::boolean: return static_cast(v.get()); default: - throw ink_exception("invalid cast to int!"); + inkFail("invalid cast to int!"); + return 0; } } @@ -92,7 +95,8 @@ namespace ink::runtime::internal { case value_type::int32: return static_cast(v.get()); default: - throw ink_exception("invalid numeric_cast!"); + inkFail("invalid numeric_cast!"); + return 0; } } } diff --git a/inkcpp/operations.h b/inkcpp/operations.h index 9e79a711..1c58eb91 100644 --- a/inkcpp/operations.h +++ b/inkcpp/operations.h @@ -54,7 +54,7 @@ namespace ink::runtime::internal { * @param vs array of values, first one = first argument etc */ void operator()(basic_eval_stack& stack, value* vs) { - throw ink_exception("operation not implemented!"); + inkFail("operation not implemented!"); } }; } diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 0f08367a..c73864ee 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -316,7 +316,7 @@ namespace ink case value_type::list_flag: ptr = lists.toString(ptr, _data[i].get()); break; - default: throw ink_exception("cant convert expression to string!"); + default: inkFail("cant convert expression to string!"); } } diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 03f2407d..ff20cdbe 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -188,8 +188,8 @@ namespace ink::runtime::internal // Iterate until we find the container marker just before our own while (_story->iterate_containers(iter, container_id, offset, reverse)) { - if (!reverse && offset > _ptr - || reverse && offset < _ptr) { + if (( !reverse && offset > _ptr ) + || ( reverse && offset < _ptr )) { // Step back once in the iteration and break inBound = true; @@ -202,11 +202,11 @@ namespace ink::runtime::internal bool first = true; // Start moving forward (or backwards) - if(inBound && (offset == nullptr || !reverse&&offset<=dest || reverse&&offset>dest) ) + if(inBound && (offset == nullptr || (!reverse&&offset<=dest) || (reverse&&offset>dest)) ) while (_story->iterate_containers(iter, container_id, offset, reverse)) { // Break when we've past the destination - if (!reverse && offset > dest || reverse && offset <= dest) { + if ((!reverse && offset > dest) || (reverse && offset <= dest)) { // jump back to start of same container if(first && reverse && offset == dest && _container.top() == container_id) { @@ -587,6 +587,7 @@ namespace ink::runtime::internal return change_type::extended_past_newline; inkFail("Invalid change detction. Never should be here!"); + return change_type::no_change; } bool runner_impl::line_step() @@ -711,7 +712,7 @@ namespace ink::runtime::internal if(bEvaluationMode) { _eval.push(value{}.set(val, static_cast(flag) - 1)); } else { - throw ink_exception("never conciderd what should happend here! (value pointer print)"); + inkFail("never conciderd what should happend here! (value pointer print)"); } } break; diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 61daf718..1371cfb4 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -171,26 +171,23 @@ namespace ink::runtime::internal bool bSavedEvaluationMode = false; // Keeps track of what threads we're inside - class threads : public internal::simple_restorable_stack { - using base = internal::simple_restorable_stack; - static constexpr bool dynamic = config::limitThreadDepth < 0; - static constexpr size_t N = abs(config::limitThreadDepth); + template + class threads : public internal::managed_restorable_stack { + using base = internal::managed_restorable_stack; public: template = true > threads() - : base(nullptr, 0, ~0), + : base(~0), _threadDone(nullptr, reinterpret_cast(~0)) { static_assert(sizeof...(D) == 0, "Don't use explicit template arguments!"); } template = true > threads() - : _stack{}, - base(_stack.data(), N, ~0), + : base(~0), _threadDone(nullptr, reinterpret_cast(~0)) { static_assert(sizeof...(D) == 0, "Don't use explicit template arguments"); _threadDone.clear(nullptr); } - void clear() { base::clear(); _threadDone.clear(nullptr); @@ -227,9 +224,9 @@ namespace ink::runtime::internal void resize(size_t size, long) {} - managed_array _stack; array_type _threadDone; - } _threads; + }; + threads _threads; // Choice list managed_array _choices; @@ -251,16 +248,11 @@ namespace ink::runtime::internal prng _rng{}; }; - inline void runner_impl::threads::overflow(thread_t*& buffer, size_t& size) { + template + void runner_impl::threads::overflow(thread_t*& buffer, size_t& size) { + base::overflow(buffer, size); if constexpr (dynamic) { - if(buffer) { - _stack.extend(); - } - buffer = _stack.data(); - size = _stack.capacity(); resize(size, 0); - } else { - base::overflow(buffer, size); } } diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index b38a0e4c..69d34979 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -31,7 +31,7 @@ namespace ink::runtime::internal protected: virtual void overflow(T*& buffer, size_t& size) { - throw ink_exception("Stack overflow!"); + inkFail("Stack overflow!"); } void initialize_data(T* buffer, size_t size) { @@ -62,7 +62,7 @@ namespace ink::runtime::internal managed_restorable_stack(const T& null) : simple_restorable_stack(nullptr, 0, null), _stack{} { base::initialize_data(_stack.data(), N); } - virtual void overflow(T*& buffer, size_t& size) override final { + virtual void overflow(T*& buffer, size_t& size) override { if constexpr (dynamic) { if (buffer) { _stack.extend(); diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index 44d531c8..2775566a 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -208,7 +208,7 @@ namespace ink::runtime::internal } else if (vt == value_type::thread_start) { start.set(jump); } else { - throw ink_exception("unknown jump type"); + inkFail("unknown jump type"); } return threadIter.get(); } diff --git a/inkcpp/string_utils.h b/inkcpp/string_utils.h index db4f0b0a..3f8b97b5 100644 --- a/inkcpp/string_utils.h +++ b/inkcpp/string_utils.h @@ -74,7 +74,8 @@ namespace ink::runtime::internal { case value_type::newline: return toStr(buffer, size, "\n"); default: - throw ink_exception("only support toStr for numeric types"); + inkFail("only support toStr for numeric types"); + return -1; } } @@ -108,7 +109,8 @@ namespace ink::runtime::internal { case value_type::newline: return 1; default: - throw ink_exception("Can't determine length of this value type"); + inkFail("Can't determine length of this value type"); + return -1; } } diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index db241f26..b8180081 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -106,5 +106,6 @@ namespace ink::runtime::internal else if(type() == value_type::string) { return val(get().str); } else if(type() == value_type::float32) { return val(get()); } inkFail("No valid type to convert to interface value!"); + return val(); } } diff --git a/inkcpp/value.h b/inkcpp/value.h index ad4c469b..df7398e0 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -135,7 +135,8 @@ namespace ink::runtime::internal { template value redefine(const value& oth, const tuple& env) const { if constexpr ( ty == value_type::OP_END) { - throw ink_exception("Can't redefine value with this type! (It is not an variable type!)"); + inkFail("Can't redefine value with this type! (It is not an variable type!)"); + return value{}; } else if (ty != type()) { return redefine(oth, env); } else { diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index fdc89240..8be4ed5e 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -26,7 +26,7 @@ namespace ink::compiler::internal if ( context == nullptr || sep == nullptr || - s == nullptr && *context == nullptr ) + (s == nullptr && *context == nullptr) ) { errno = EINVAL; return nullptr; diff --git a/shared/public/system.h b/shared/public/system.h index 184dec43..46516da9 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -122,7 +122,7 @@ namespace ink void ink_assert(bool condition, const char* msg = nullptr); [[ noreturn ]] inline void ink_assert(const char* msg = nullptr) { ink_assert(false, msg); exit(EXIT_FAILURE);} #else - [[ noreturn ]] inline void ink_fail(const char*) { check(false); throw nullptr; } + inline void ink_fail(const char*) { checkNoEntry(); } #endif #ifdef INK_ENABLE_STL @@ -176,7 +176,7 @@ namespace ink public: optional() {} optional(nullopt_t) {} - optional(T&& val) : _has_value{true}, _value{std::forward(val)}{} + optional(T&& val) : _has_value{true}, _value{val}{} optional(const T& val) : _has_value{true}, _value{val}{} const T& operator*() const { return _value; } @@ -190,12 +190,12 @@ namespace ink constexpr operator bool() const { return has_value(); } template constexpr T value_or(U&& u) const { - return _has_value ? _value : static_cast(std::forward(u)); + return _has_value ? _value : static_cast(u); } private: void test_value() const { if ( ! _has_value) { - throw ink_exception("Can't access empty optional!"); + ink_fail("Can't access empty optional!"); } } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index 09ec75e7..59801dc4 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -184,7 +184,6 @@ void AInkRuntime::PopExclusiveThread(UInkThread* Thread) FInkVar AInkRuntime::GetGlobalVariable(const FString& name) { ink::optional var = mpGlobals->get(TCHAR_TO_ANSI(*name)); - (var, "Reguested variable does not exists!"); if(var) { return FInkVar(*var); } else { UE_LOG(InkCpp, Warning, TEXT("Failed to find global variable with name: %s"), *name); } return FInkVar{}; diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp index b62f38b6..36236263 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkVar.cpp @@ -42,6 +42,7 @@ ink::runtime::value FInkVar::to_value() const { return ink::runtime::value(value.GetSubtype()); default: inkFail("Unsupported type"); + return ink::runtime::value(); } } From 3bbcf4855dd46cf93c7862dcf18d570b8c84bf4c Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 12 Jul 2022 13:52:38 +0200 Subject: [PATCH 18/67] add fallback but not working if not defined --- inkcpp/functional.cpp | 6 ++++++ inkcpp/include/functional.h | 4 +++- inkcpp/runner_impl.cpp | 13 ++++++++----- inkcpp/value.h | 7 +++++++ inkcpp_compiler/json_compiler.cpp | 1 + inkcpp_test/NewLines.cpp | 1 + shared/private/command.h | 2 ++ 7 files changed, 28 insertions(+), 6 deletions(-) diff --git a/inkcpp/functional.cpp b/inkcpp/functional.cpp index 9c0cad62..a733dc6e 100644 --- a/inkcpp/functional.cpp +++ b/inkcpp/functional.cpp @@ -32,6 +32,12 @@ namespace ink::runtime::internal stack->push(value{}.set(v)); } + void function_base::push_void(basic_eval_stack* stack) + { + stack->push(values::null); + } + + void function_base::push_string(basic_eval_stack* stack, const char* dynamic_string) { stack->push(value{}.set(dynamic_string, true)); diff --git a/inkcpp/include/functional.h b/inkcpp/include/functional.h index a5461b9b..73a0123f 100644 --- a/inkcpp/include/functional.h +++ b/inkcpp/include/functional.h @@ -26,6 +26,8 @@ namespace ink::runtime::internal template static void push(basic_eval_stack* stack, const T& value); + static void push_void(basic_eval_stack* stack); + // string special push static void push_string(basic_eval_stack* stack, const char* dynamic_string); @@ -81,7 +83,7 @@ namespace ink::runtime::internal // Ink expects us to push something // TODO -- Should be a special "void" value - push(stack, 0); + push_void(stack); } else if constexpr (is_string::value) { diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index ee51511a..f97b5e9e 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -612,9 +612,6 @@ namespace ink::runtime::internal // If we're on a newline if (_output.ends_with(value_type::newline)) { - // TODO: REMOVE - // return true; - // Unless we are out of content, we are going to try // to continue a little further. This is to check for // glue (which means there is potentially more content @@ -850,7 +847,13 @@ namespace ink::runtime::internal } else { target = read(); } - start_frame(target); + if (!(flag & CommandFlag::FALLBACK_FUNCTION)) { + start_frame(target); + } else { + if(_eval.top_value().type() == value_type::ex_fn_not_found) { + start_frame(target); + } + } } break; case Command::TUNNEL_RETURN: @@ -930,7 +933,7 @@ namespace ink::runtime::internal _eval.pop(); // push void - _eval.push(value()); + _eval.push(values::ex_fn_not_found); } // TODO: Verify something was found? diff --git a/inkcpp/value.h b/inkcpp/value.h index 88382f5a..470827e7 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -41,6 +41,7 @@ namespace ink::runtime::internal { func_start, // start of function marker func_end, // end of function marker null, // void value, for function returns + ex_fn_not_found, // value for failed external function calls tunnel_frame, // return from tunnel function_frame, // return from function thread_frame, // return from thread @@ -351,6 +352,11 @@ namespace ink::runtime::internal { return *this; } template<> + inline constexpr value& value::set() { + _type = value_type::ex_fn_not_found; + return *this; + } + template<> inline constexpr value& value::set() { _type = value_type::newline; return *this; @@ -418,6 +424,7 @@ namespace ink::runtime::internal { namespace values { static constexpr value marker = value{}.set(); static constexpr value glue = value{}.set(); + static constexpr value ex_fn_not_found = value{}.set(); static constexpr value newline = value{}.set(); static constexpr value func_start = value{}.set(); static constexpr value func_end = value{}.set(); diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index d5ebe17f..8d34bb67 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -364,6 +364,7 @@ namespace ink::compiler::internal // Encode argument count into command flag and write out the hash of the function name _emitter->write(Command::CALL_EXTERNAL, hash_string(val.c_str()), static_cast(numArgs)); + _emitter->write_path(Command::FUNCTION, CommandFlag::FALLBACK_FUNCTION, val); } // list initialisation diff --git a/inkcpp_test/NewLines.cpp b/inkcpp_test/NewLines.cpp index adaa078c..910dfa88 100644 --- a/inkcpp_test/NewLines.cpp +++ b/inkcpp_test/NewLines.cpp @@ -2,6 +2,7 @@ #include "../inkcpp_cl/test.h" #include +#include #include #include diff --git a/shared/private/command.h b/shared/private/command.h index d84b6a72..15465377 100644 --- a/shared/private/command.h +++ b/shared/private/command.h @@ -140,6 +140,8 @@ namespace ink // == Function/Tunnel flags FUNCTION_TO_VARIABLE = 1 << 0, TUNNEL_TO_VARIABLE = 1 << 0, + FALLBACK_FUNCTION = 1 << 1, + // note a internal function which should only be called if external reference is not working }; inline bool operator& (CommandFlag lhs, CommandFlag rhs) From f37d65679053691c8cfa7fc0d260f6be024c6822 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 12 Jul 2022 14:55:41 +0200 Subject: [PATCH 19/67] Add fallback for external functions based on: https://github.com/inkle/ink/blob/master/Documentation/RunningYourInk.md#fallbacks-for-external-functions it is no possible to implement a dummey/error function for each external functions. It must have ben the same name and same amount of arguments and will be executed if the external functio was not bind when needed. IF the external functions will get bind later, then it will be called the next time. Introduces new flag: "FALLBACK_FUNCTION" to add a new execution dimension Introduce new value type: `ex_fn_not_found` to signal failed excetrnal executio --- inkcpp/runner_impl.cpp | 17 +++++++++-------- inkcpp_compiler/binary_emitter.cpp | 18 +++++++++++++++++- inkcpp_compiler/binary_emitter.h | 1 + inkcpp_compiler/binary_stream.cpp | 17 +++++++++++++++++ inkcpp_compiler/binary_stream.h | 3 +++ inkcpp_compiler/emitter.h | 6 ++++++ inkcpp_compiler/json_compiler.cpp | 12 ++++++++++-- 7 files changed, 63 insertions(+), 11 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index f97b5e9e..54350338 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -646,6 +646,13 @@ namespace ink::runtime::internal Command cmd = read(); CommandFlag flag = read(); + if (_eval->top_value().type() == value_type::ex_fn_not_found) { + inkAssert(cmd == Command::FUNCTION, "Failed to call external function and no " + "local function exists to call instead! Please bind external function or " + "add define a dummy function in your story!" + ); + } + // If we're falling and we hit a non-fallthrough command, stop the fall. if (_is_falling && !((cmd == Command::DIVERT && flag & CommandFlag::DIVERT_IS_FALLTHROUGH) || cmd == Command::END_CONTAINER_MARKER)) { @@ -851,6 +858,7 @@ namespace ink::runtime::internal start_frame(target); } else { if(_eval.top_value().type() == value_type::ex_fn_not_found) { + _eval.pop(); start_frame(target); } } @@ -925,18 +933,11 @@ namespace ink::runtime::internal // find and execute. will automatically push a valid if applicable bool success = _functions.call(functionName, &_eval, numArguments, _globals->strings()); - // If we failed, we need to at least pretend so our state doesn't get fucked + // If we failed, notify a potential fallback function if (!success) { - // pop arguments - for (int i = 0; i < numArguments; i++) - _eval.pop(); - - // push void _eval.push(values::ex_fn_not_found); } - - // TODO: Verify something was found? } break; diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index dd163e63..b34ef106 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -17,7 +17,6 @@ namespace ink::compiler::internal using std::vector; using std::map; using std::string; - using std::tuple; char* strtok_s(char * s, const char * sep, char** context) { #if defined(_WIN32) || defined(_WIN64) @@ -127,6 +126,23 @@ namespace ink::compiler::internal return _containers.pos(); } + int binary_emitter::function_container_arguments(const std::string& name) + { + if(_root == nullptr) { return -1; } + auto fn = _root->named_children.find(name); + if (fn == _root->named_children.end()) { return -1; } + + size_t offset = fn->second->offset; + byte_t cmd = _containers.get(offset); + int arity = 0; + while(static_cast(cmd) == Command::DEFINE_TEMP) { + offset += 6; // command(1) + flag(1) + variable_name_hash(4) + cmd = _containers.get(offset); + ++arity; + } + return arity; + } + void binary_emitter::write_raw(Command command, CommandFlag flag, const char* payload, ink::size_t payload_size) { _containers.write(command); diff --git a/inkcpp_compiler/binary_emitter.h b/inkcpp_compiler/binary_emitter.h index 1cf73c69..f4039569 100644 --- a/inkcpp_compiler/binary_emitter.h +++ b/inkcpp_compiler/binary_emitter.h @@ -18,6 +18,7 @@ namespace ink::compiler::internal // Begin emitter virtual uint32_t start_container(int index_in_parent, const std::string& name) override; virtual uint32_t end_container() override; + virtual int function_container_arguments(const std::string& name) override; virtual void write_raw(Command command, CommandFlag flag = CommandFlag::NO_FLAGS, const char* payload = nullptr, ink::size_t payload_size = 0) override; virtual void write_path(Command command, CommandFlag flag, const std::string& path, bool useCountIndex = false) override; virtual void write_variable(Command command, CommandFlag flag, const std::string& name) override; diff --git a/inkcpp_compiler/binary_stream.cpp b/inkcpp_compiler/binary_stream.cpp index 6f44f59e..6c57064d 100644 --- a/inkcpp_compiler/binary_stream.cpp +++ b/inkcpp_compiler/binary_stream.cpp @@ -124,6 +124,23 @@ namespace ink memcpy(ptr, data, len); } + byte_t binary_stream::get(size_t offset) const + { + // Find slab for offset + unsigned int slab_index = offset / DATA_SIZE; + size_t pos = offset % DATA_SIZE; + + // Get slab and ptr + byte_t* slab = nullptr; + if (slab_index < _slabs.size()) + slab = _slabs[slab_index]; + else if (slab_index == _slabs.size()) + slab = _currentSlab; + + inkAssert(slab != nullptr, "try to access invalid slab in binary stream"); + return slab[pos]; + } + void binary_stream::reset() { // Delete all slabs diff --git a/inkcpp_compiler/binary_stream.h b/inkcpp_compiler/binary_stream.h index 6d4efe25..d5228727 100644 --- a/inkcpp_compiler/binary_stream.h +++ b/inkcpp_compiler/binary_stream.h @@ -48,6 +48,9 @@ namespace ink // reset to 0 void reset(); + // read a byte from stream + byte_t get(size_t offset) const; + private: // Size of a data slab. Whenever // a slab runs out of data, diff --git a/inkcpp_compiler/emitter.h b/inkcpp_compiler/emitter.h index 75a99bc9..cee87890 100644 --- a/inkcpp_compiler/emitter.h +++ b/inkcpp_compiler/emitter.h @@ -28,6 +28,12 @@ namespace ink::compiler::internal // ends a container virtual uint32_t end_container() = 0; + // checks if _root contains a container named name to check + // if name is in valid internal function name + // @return number of arguments functions takes (arity) + // @retval -1 if the function was not found + virtual int function_container_arguments(const std::string& name) = 0; + // Writes a command with an optional payload virtual void write_raw(Command command, CommandFlag flag = CommandFlag::NO_FLAGS, const char* payload = nullptr, ink::size_t payload_size = 0) = 0; diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index 8d34bb67..d7f08b65 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -364,8 +364,16 @@ namespace ink::compiler::internal // Encode argument count into command flag and write out the hash of the function name _emitter->write(Command::CALL_EXTERNAL, hash_string(val.c_str()), static_cast(numArgs)); - _emitter->write_path(Command::FUNCTION, CommandFlag::FALLBACK_FUNCTION, val); - } + // if internal function with same name exists + int arity = _emitter->function_container_arguments(val); + if (arity >= 0) { + inkAssert(arity == numArgs, + ("fallback function for '" + val + "' takes " + std::to_string(arity) + + " but the external function is supposed to take " + std::to_string(numArgs) + + " arguments.").c_str() + ); + _emitter->write_path(Command::FUNCTION, CommandFlag::FALLBACK_FUNCTION, val); + } } // list initialisation else if (has(command, "list")) From 1703ed582e421019d75bc8fe8248cbfbc4fc6724 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 12 Jul 2022 16:57:55 +0200 Subject: [PATCH 20/67] fix referencing in error detection --- inkcpp/runner_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 54350338..53524156 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -646,7 +646,7 @@ namespace ink::runtime::internal Command cmd = read(); CommandFlag flag = read(); - if (_eval->top_value().type() == value_type::ex_fn_not_found) { + if (cmd == Command::FUNCTION && _eval.top_value().type() == value_type::ex_fn_not_found) { inkAssert(cmd == Command::FUNCTION, "Failed to call external function and no " "local function exists to call instead! Please bind external function or " "add define a dummy function in your story!" From 5e03e9c6a469652b98a0c44f4d3f051880f6da98 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 13:29:01 +0200 Subject: [PATCH 21/67] Make ex_fn_not_found check work on empty stack --- inkcpp/runner_impl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 53524156..d57d4e82 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -646,7 +646,7 @@ namespace ink::runtime::internal Command cmd = read(); CommandFlag flag = read(); - if (cmd == Command::FUNCTION && _eval.top_value().type() == value_type::ex_fn_not_found) { + if (!_eval.is_empty() && _eval.top().type() == value_type::ex_fn_not_found) { inkAssert(cmd == Command::FUNCTION, "Failed to call external function and no " "local function exists to call instead! Please bind external function or " "add define a dummy function in your story!" @@ -857,6 +857,7 @@ namespace ink::runtime::internal if (!(flag & CommandFlag::FALLBACK_FUNCTION)) { start_frame(target); } else { + inkAssert(!_eval.is_empty(), "fallback function but no function call before?"); if(_eval.top_value().type() == value_type::ex_fn_not_found) { _eval.pop(); start_frame(target); From 182e5c26eceffffc924f9daca743ea36e2f994a3 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 16 Aug 2022 12:37:48 +0200 Subject: [PATCH 22/67] Fixed out of order definition --- inkcpp/runner_impl.cpp | 12 +++--- inkcpp/stack.cpp | 3 ++ inkcpp/value.h | 2 +- inkcpp_compiler/binary_emitter.cpp | 32 ++++++++++++--- inkcpp_compiler/binary_emitter.h | 7 +++- inkcpp_compiler/json_compiler.cpp | 24 +++++++----- inkcpp_test/CMakeLists.txt | 23 +++++------ inkcpp_test/FallbackFunction.cpp | 63 ++++++++++++++++++++++++++++++ inkcpp_test/ink/FallBack.ink | 13 ++++++ 9 files changed, 144 insertions(+), 35 deletions(-) create mode 100644 inkcpp_test/FallbackFunction.cpp create mode 100644 inkcpp_test/ink/FallBack.ink diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index d57d4e82..54945f4a 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -6,6 +6,8 @@ #include "header.h" #include "string_utils.h" +#include + namespace ink::runtime { const choice* runner_interface::get_choice(size_t index) const @@ -646,13 +648,6 @@ namespace ink::runtime::internal Command cmd = read(); CommandFlag flag = read(); - if (!_eval.is_empty() && _eval.top().type() == value_type::ex_fn_not_found) { - inkAssert(cmd == Command::FUNCTION, "Failed to call external function and no " - "local function exists to call instead! Please bind external function or " - "add define a dummy function in your story!" - ); - } - // If we're falling and we hit a non-fallthrough command, stop the fall. if (_is_falling && !((cmd == Command::DIVERT && flag & CommandFlag::DIVERT_IS_FALLTHROUGH) || cmd == Command::END_CONTAINER_MARKER)) { @@ -860,6 +855,7 @@ namespace ink::runtime::internal inkAssert(!_eval.is_empty(), "fallback function but no function call before?"); if(_eval.top_value().type() == value_type::ex_fn_not_found) { _eval.pop(); + inkAssert(target != 0, "Exetrnal function was not binded, and no fallback function provided!"); start_frame(target); } } @@ -937,6 +933,7 @@ namespace ink::runtime::internal // If we failed, notify a potential fallback function if (!success) { + std::cout << "ex_fn_not_found" << static_cast(value_type::ex_fn_not_found) << "\n"; _eval.push(values::ex_fn_not_found); } } @@ -1135,6 +1132,7 @@ namespace ink::runtime::internal inkAssert(false, "Unrecognized command!"); break; } + } catch (...) { diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index 40c73127..af5894e4 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -1,6 +1,8 @@ #include "stack.h" #include "string_table.h" +#include + namespace ink::runtime::internal { basic_stack::basic_stack(entry* data, size_t size) @@ -504,6 +506,7 @@ namespace ink::runtime::internal void basic_eval_stack::push(const value& val) { + std::cout << "push" << static_cast(val.type()) << "\n"; base::push(val); } diff --git a/inkcpp/value.h b/inkcpp/value.h index 470827e7..36186988 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -41,7 +41,7 @@ namespace ink::runtime::internal { func_start, // start of function marker func_end, // end of function marker null, // void value, for function returns - ex_fn_not_found, // value for failed external function calls + ex_fn_not_found, // value for failed external function calls tunnel_frame, // return from tunnel function_frame, // return from function thread_frame, // return from thread diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index b34ef106..dbc50e76 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -12,6 +12,8 @@ #include #endif +#include + namespace ink::compiler::internal { using std::vector; @@ -106,8 +108,10 @@ namespace ink::compiler::internal _current->children.push_back(container); _current->indexed_children.insert({ index_in_parent, container }); - if (!name.empty()) + if (!name.empty()) { + std::cout << "add: " << name << "\n"; _current->named_children.insert({ name, container }); + } } // Set this as the current pointer @@ -128,9 +132,12 @@ namespace ink::compiler::internal int binary_emitter::function_container_arguments(const std::string& name) { + std::cout << "a\n"; if(_root == nullptr) { return -1; } + std::cout << "b\n"; auto fn = _root->named_children.find(name); if (fn == _root->named_children.end()) { return -1; } + std::cout << "c\n"; size_t offset = fn->second->offset; byte_t cmd = _containers.get(offset); @@ -158,7 +165,9 @@ namespace ink::compiler::internal // Note the position of this later so we can resolve the paths at the end size_t param_position = _containers.pos() - sizeof(uint32_t); - _paths.push_back(std::make_tuple(param_position, path, _current, useCountIndex)); + bool op = flag & CommandFlag::FALLBACK_FUNCTION; + if(op) std::cout << "op\n"; + _paths.push_back(std::make_tuple(param_position, path, op, _current, useCountIndex)); } void binary_emitter::write_variable(Command command, CommandFlag flag, const std::string& name) @@ -290,8 +299,9 @@ namespace ink::compiler::internal using std::get; size_t position = get<0>(pair); const std::string& path = get<1>(pair); - container_data* context = get<2>(pair); - bool useCountIndex = get<3>(pair); + bool optional = get<2>(pair); + container_data* context = get<3>(pair); + bool useCountIndex = get<4>(pair); // Start at the root container_data* container = _root; @@ -341,7 +351,10 @@ namespace ink::compiler::internal // Named child else { - container = container->named_children[token]; + auto itr = container->named_children.find(token); + container = itr == container->named_children.end() + ? nullptr + : itr->second; } firstParent = false; @@ -366,7 +379,14 @@ namespace ink::compiler::internal else { // Otherwise, write container address - _containers.set(position, container->offset); + if (container == nullptr) { + _containers.set(position, 0); + std::cout << "optional: ? " << optional << "\n"; + std::cout << path << "\n"; + inkAssert(optional, "Was not able to resolve a not optional path!"); + } else { + _containers.set(position, container->offset); + } } } } diff --git a/inkcpp_compiler/binary_emitter.h b/inkcpp_compiler/binary_emitter.h index f4039569..7033c909 100644 --- a/inkcpp_compiler/binary_emitter.h +++ b/inkcpp_compiler/binary_emitter.h @@ -54,6 +54,11 @@ namespace ink::compiler::internal binary_stream _lists; binary_stream _containers; - std::vector> _paths; + // positon to write address + // path as string + // if path may not exists (used for function fallbackes) + // container data + // use count index? + std::vector> _paths; }; } diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index d7f08b65..bd167ab2 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -121,6 +121,7 @@ namespace ink::compiler::internal const nlohmann::json& container, int index_in_parent, const std::string& name_override) { + std::cout << "compile: " << name_override << "\n"; // Grab metadata from the last object in this container container_meta meta; handle_container_metadata(*container.rbegin(), meta); @@ -357,6 +358,7 @@ namespace ink::compiler::internal // External function call else if (get(command, "x()", val)) { + std::cout << "fallback\n"; // Get argument count int numArgs = 0; get(command, "exArgs", numArgs); @@ -364,16 +366,20 @@ namespace ink::compiler::internal // Encode argument count into command flag and write out the hash of the function name _emitter->write(Command::CALL_EXTERNAL, hash_string(val.c_str()), static_cast(numArgs)); - // if internal function with same name exists - int arity = _emitter->function_container_arguments(val); - if (arity >= 0) { - inkAssert(arity == numArgs, - ("fallback function for '" + val + "' takes " + std::to_string(arity) + - " but the external function is supposed to take " + std::to_string(numArgs) - + " arguments.").c_str() - ); _emitter->write_path(Command::FUNCTION, CommandFlag::FALLBACK_FUNCTION, val); - } } + // if internal function with same name exists + // int arity = _emitter->function_container_arguments(val); + // std::cout << "arity: " << arity << "\n"; + // if (arity >= 0) { + // inkAssert(arity == numArgs, + // ("fallback function for '" + val + "' takes " + std::to_string(arity) + + // " but the external function is supposed to take " + std::to_string(numArgs) + // + " arguments.").c_str() + // ); + // std::cout << "write fallback\n"; + // _emitter->write_path(Command::FUNCTION, CommandFlag::FALLBACK_FUNCTION, val); + // } + } // list initialisation else if (has(command, "list")) diff --git a/inkcpp_test/CMakeLists.txt b/inkcpp_test/CMakeLists.txt index 4ee6fbb6..e94b72ff 100644 --- a/inkcpp_test/CMakeLists.txt +++ b/inkcpp_test/CMakeLists.txt @@ -1,15 +1,16 @@ add_executable(inkcpp_test catch.hpp Main.cpp - Array.cpp - Pointer.cpp - Stack.cpp - Callstack.cpp - Restorable.cpp - Value.cpp - Globals.cpp - Lists.cpp - Tags.cpp - NewLines.cpp - ) + Array.cpp + Pointer.cpp + Stack.cpp + Callstack.cpp + Restorable.cpp + Value.cpp + Globals.cpp + Lists.cpp + Tags.cpp + NewLines.cpp + FallbackFunction.cpp +) target_link_libraries(inkcpp_test PUBLIC inkcpp inkcpp_compiler inkcpp_shared) target_include_directories(inkcpp_test PRIVATE ../shared/private/) diff --git a/inkcpp_test/FallbackFunction.cpp b/inkcpp_test/FallbackFunction.cpp new file mode 100644 index 00000000..52cb8586 --- /dev/null +++ b/inkcpp_test/FallbackFunction.cpp @@ -0,0 +1,63 @@ +#include "catch.hpp" +#include "../inkcpp_cl/test.h" + +#include +#include +#include +#include +#include + +#include + +using namespace ink::runtime; + +SCENARIO("run a story with external function and fallback function", "[external function]") +{ + GIVEN("story with two external functions, one with fallback") + { + inklecate("ink/FallBack.ink", "FallBack.tmp"); + ink::compiler::run("FallBack.tmp", "FallBack.bin"); + auto ink = story::from_file("FallBack.bin"); + runner thread = ink->new_runner(); + + WHEN("bind both external functions") + { + int cnt_sqrt = 0; + auto fn_sqrt = [&cnt_sqrt](int x)->int{ ++cnt_sqrt; return sqrt(x); }; + int cnt_greeting = 0; + auto fn_greeting = [&cnt_greeting]()->const char*{++cnt_greeting; return "Hohooh"; }; + + thread->bind("sqrt", fn_sqrt); + thread->bind("greeting", fn_greeting); + + std::string out; + REQUIRE_NOTHROW(out = thread->getall()); + THEN("Both function should be called the correct amount of times") + { + REQUIRE(out == "Hohooh ! A small demonstraion of my power:\n4 * 4 = 16, stunning i would say\n"); + REQUIRE(cnt_sqrt == 2); + REQUIRE(cnt_greeting == 1); + } + } + WHEN("only bind function without fallback") + { + int cnt_sqrt = 0; + auto fn_sqrt = [&cnt_sqrt](int x)->int{++cnt_sqrt; return sqrt(x); }; + + thread ->bind("sqrt", fn_sqrt); + + std::string out; + REQUIRE_NOTHROW(out = thread->getall());; + THEN("Sqrt should be falled twice, and uses default greeting") + { + REQUIRE(out == "oho"); + REQUIRE(cnt_sqrt == 2); + } + } + WHEN("bind no function") + { + std::string out; + REQUIRE_THROWS_AS(out = thread->getall(), ink::ink_exception); + } + } +} diff --git a/inkcpp_test/ink/FallBack.ink b/inkcpp_test/ink/FallBack.ink new file mode 100644 index 00000000..5371ad47 --- /dev/null +++ b/inkcpp_test/ink/FallBack.ink @@ -0,0 +1,13 @@ +-> Start + +EXTERNAL sqrt(x) + +EXTERNAL greeting() + +=== function greeting() === +~ return "Hello" + +== Start == +{greeting()} ! A small demonstraion of my power: +{sqrt(16)} * {sqrt(16)} = 16, stunning i would say +-> DONE \ No newline at end of file From f0c569a5322f82e1501c11fad0f4be5f5873e1e7 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 16 Aug 2022 12:42:08 +0200 Subject: [PATCH 23/67] clean --- inkcpp/runner_impl.cpp | 3 --- inkcpp/stack.cpp | 3 --- inkcpp/value.h | 2 +- inkcpp_compiler/binary_emitter.cpp | 11 +---------- inkcpp_compiler/json_compiler.cpp | 18 ++---------------- 5 files changed, 4 insertions(+), 33 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 54945f4a..c7039386 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -6,8 +6,6 @@ #include "header.h" #include "string_utils.h" -#include - namespace ink::runtime { const choice* runner_interface::get_choice(size_t index) const @@ -933,7 +931,6 @@ namespace ink::runtime::internal // If we failed, notify a potential fallback function if (!success) { - std::cout << "ex_fn_not_found" << static_cast(value_type::ex_fn_not_found) << "\n"; _eval.push(values::ex_fn_not_found); } } diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index af5894e4..40c73127 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -1,8 +1,6 @@ #include "stack.h" #include "string_table.h" -#include - namespace ink::runtime::internal { basic_stack::basic_stack(entry* data, size_t size) @@ -506,7 +504,6 @@ namespace ink::runtime::internal void basic_eval_stack::push(const value& val) { - std::cout << "push" << static_cast(val.type()) << "\n"; base::push(val); } diff --git a/inkcpp/value.h b/inkcpp/value.h index 36186988..d72701fc 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -41,7 +41,7 @@ namespace ink::runtime::internal { func_start, // start of function marker func_end, // end of function marker null, // void value, for function returns - ex_fn_not_found, // value for failed external function calls + ex_fn_not_found, // value for failed external function calls tunnel_frame, // return from tunnel function_frame, // return from function thread_frame, // return from thread diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index dbc50e76..946c7726 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -12,8 +12,6 @@ #include #endif -#include - namespace ink::compiler::internal { using std::vector; @@ -109,7 +107,6 @@ namespace ink::compiler::internal _current->indexed_children.insert({ index_in_parent, container }); if (!name.empty()) { - std::cout << "add: " << name << "\n"; _current->named_children.insert({ name, container }); } } @@ -132,12 +129,9 @@ namespace ink::compiler::internal int binary_emitter::function_container_arguments(const std::string& name) { - std::cout << "a\n"; if(_root == nullptr) { return -1; } - std::cout << "b\n"; auto fn = _root->named_children.find(name); if (fn == _root->named_children.end()) { return -1; } - std::cout << "c\n"; size_t offset = fn->second->offset; byte_t cmd = _containers.get(offset); @@ -166,7 +160,6 @@ namespace ink::compiler::internal // Note the position of this later so we can resolve the paths at the end size_t param_position = _containers.pos() - sizeof(uint32_t); bool op = flag & CommandFlag::FALLBACK_FUNCTION; - if(op) std::cout << "op\n"; _paths.push_back(std::make_tuple(param_position, path, op, _current, useCountIndex)); } @@ -381,9 +374,7 @@ namespace ink::compiler::internal // Otherwise, write container address if (container == nullptr) { _containers.set(position, 0); - std::cout << "optional: ? " << optional << "\n"; - std::cout << path << "\n"; - inkAssert(optional, "Was not able to resolve a not optional path!"); + inkAssert(optional, ("Was not able to resolve a not optional path! '" + path + "'").c_str()); } else { _containers.set(position, container->offset); } diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index bd167ab2..961237a3 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -121,7 +121,6 @@ namespace ink::compiler::internal const nlohmann::json& container, int index_in_parent, const std::string& name_override) { - std::cout << "compile: " << name_override << "\n"; // Grab metadata from the last object in this container container_meta meta; handle_container_metadata(*container.rbegin(), meta); @@ -358,7 +357,6 @@ namespace ink::compiler::internal // External function call else if (get(command, "x()", val)) { - std::cout << "fallback\n"; // Get argument count int numArgs = 0; get(command, "exArgs", numArgs); @@ -366,20 +364,8 @@ namespace ink::compiler::internal // Encode argument count into command flag and write out the hash of the function name _emitter->write(Command::CALL_EXTERNAL, hash_string(val.c_str()), static_cast(numArgs)); - _emitter->write_path(Command::FUNCTION, CommandFlag::FALLBACK_FUNCTION, val); - // if internal function with same name exists - // int arity = _emitter->function_container_arguments(val); - // std::cout << "arity: " << arity << "\n"; - // if (arity >= 0) { - // inkAssert(arity == numArgs, - // ("fallback function for '" + val + "' takes " + std::to_string(arity) + - // " but the external function is supposed to take " + std::to_string(numArgs) - // + " arguments.").c_str() - // ); - // std::cout << "write fallback\n"; - // _emitter->write_path(Command::FUNCTION, CommandFlag::FALLBACK_FUNCTION, val); - // } - } + _emitter->write_path(Command::FUNCTION, CommandFlag::FALLBACK_FUNCTION, val); + } // list initialisation else if (has(command, "list")) From 87f36eddef4acb17afa2093a235f378ee7393cf7 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 16 Aug 2022 12:48:47 +0200 Subject: [PATCH 24/67] Set expected string --- inkcpp_test/FallbackFunction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkcpp_test/FallbackFunction.cpp b/inkcpp_test/FallbackFunction.cpp index 52cb8586..56a07af5 100644 --- a/inkcpp_test/FallbackFunction.cpp +++ b/inkcpp_test/FallbackFunction.cpp @@ -50,7 +50,7 @@ SCENARIO("run a story with external function and fallback function", "[external REQUIRE_NOTHROW(out = thread->getall());; THEN("Sqrt should be falled twice, and uses default greeting") { - REQUIRE(out == "oho"); + REQUIRE(out == "Hello ! A small demonstraion of my power:\n4 * 4 = 16, stunning i would say\n"); REQUIRE(cnt_sqrt == 2); } } From f147bf8b2a86bfc905b86e8d9f71c266c7984854 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 16 Aug 2022 12:52:03 +0200 Subject: [PATCH 25/67] Adapt README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 35195eaa..42098d67 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Many, but not all features of the Ink language are supported (see Glaring Omissi * Visit and read counts (`visits` and `CNT?` commands). * `seq` command and all sequence types (stopping, cycle, shuffle) * Global store that can be shared between runners -* External function binding (no fallback support yet) +* External function binding (define a function with same signature and name, which will be used if no function is bindeded) * Tunnels and internal functions * Ink threads (probably incredibly unstable though) From 37ae541a7366c50aace615ef94fd7c6dec363a91 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 24 Aug 2022 10:15:40 +0200 Subject: [PATCH 26/67] Update Policy for cmake version 3.24.1 --- unreal/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unreal/CMakeLists.txt b/unreal/CMakeLists.txt index a567aeae..2cf34442 100644 --- a/unreal/CMakeLists.txt +++ b/unreal/CMakeLists.txt @@ -1,4 +1,4 @@ - +cmake_policy(SET CMP0135 NEW) include(FetchContent) set(CMAKE_TLS_VERIFY true) FetchContent_Declare(inklecate_mac From fcb683c6f0e6ccdc91cda70336cecd7a4daf0c62 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 24 Aug 2022 10:30:41 +0200 Subject: [PATCH 27/67] Check cmake version before setting policy --- unreal/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unreal/CMakeLists.txt b/unreal/CMakeLists.txt index 2cf34442..be7faa74 100644 --- a/unreal/CMakeLists.txt +++ b/unreal/CMakeLists.txt @@ -1,4 +1,6 @@ -cmake_policy(SET CMP0135 NEW) +if(${CMAKE_VERSION} VERSION_GREATER "3.24.0") + cmake_policy(SET CMP0135 NEW) +endif() include(FetchContent) set(CMAKE_TLS_VERIFY true) FetchContent_Declare(inklecate_mac From 0221044c2b8a821ebb1bdb666db7c5d815eabe3a Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 14 Oct 2022 14:19:58 +0200 Subject: [PATCH 28/67] Fixed wrong forget function of restorable_stack spotted by an example --- inkcpp/simple_restorable_stack.h | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index 69d34979..b114eefe 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -146,7 +146,6 @@ namespace ink::runtime::internal inline void simple_restorable_stack::clear() { // Reset to start - // TODO: Support save! _save = _jump = InvalidIndex; _pos = 0; } @@ -210,18 +209,18 @@ namespace ink::runtime::internal { inkAssert(_save != InvalidIndex, "Can not forget when the stack has never been saved!"); - /*// If we have moven to a point earlier than the save point but we have a jump point - if (_pos < _save && _pos > _jump) - {*/ - // If we're at the save point, move us instead - if (_pos == _save) - _pos = _jump; - // Everything between the jump point and the save point needs to be nullified - else for (size_t i = _jump; i < _save; i++) - _buffer[i] = _null; - /*}*/ + inkAssert(_pos >= _save || _pos < _jump, "Pos is in backup areal! (should be impossible)"); + // if we are below the backup areal, no changes are needed + // if we above the backup areal, we need to collpse it + if (_pos >= _save) { + size_t delta = _save - _jump; + for(size_t i = _save; i < _pos; ++i) { + _buffer[i - delta] = _buffer[i]; + } + _pos -= delta; + } // Just reset save position - _save = InvalidIndex; + _save = _jump = InvalidIndex; } } From 76668792c4d05d86a89d4d4cad2cbcc43bbd671f Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 5 Jul 2022 12:44:57 +0200 Subject: [PATCH 29/67] Add interface for creating and loading snapshots snapshot creation is only possilbe via runner, because runner without global state wont work. The fact that the runner also snapshots all other runner with the same globals is a bit obscure and may should be changed, but since they are kind of related \_o_o_/ --- inkcpp/include/runner.h | 7 +++++++ inkcpp/include/snapshot.h | 18 ++++++++++++++++++ inkcpp/include/story.h | 15 ++++++++++++++- inkcpp/include/types.h | 3 ++- inkcpp/story_impl.cpp | 2 ++ inkcpp_cl/inkcpp_cl.cpp | 2 ++ 6 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 inkcpp/include/snapshot.h diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index 737553de..78c9c87f 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -3,6 +3,7 @@ #include "config.h" #include "system.h" #include "functional.h" +#include "types.h" #ifdef INK_ENABLE_UNREAL #include "Containers/UnrealString.h" @@ -65,6 +66,12 @@ namespace ink::runtime */ virtual char* getline_alloc() = 0; + /** + * @brief creates a snapshot containing the runner, globals and all other runners connected to the globals. + * @sa story::new_runner_from_snapshot, story::new_globals_from_snapshot + */ + virtual snapshot* create_snapshot() const = 0; + #ifdef INK_ENABLE_STL /** * Gets the next line of output using C++ STL string. diff --git a/inkcpp/include/snapshot.h b/inkcpp/include/snapshot.h new file mode 100644 index 00000000..733906f3 --- /dev/null +++ b/inkcpp/include/snapshot.h @@ -0,0 +1,18 @@ +#pragma once + +#include "types.h" + +namespace ink::runtime +{ + class snapshot { + public: + snapshot() = delete; + virtual ~snapshot() = 0; + + static snapshot* from_binary(unsigned char* data, size_t length, bool freeOnDestroy = true); + static snapshot* from_file(const char* filename); + + virtual const char* get_data() const = 0; + virtual unsigned get_data_len() const = 0; + }; +} diff --git a/inkcpp/include/story.h b/inkcpp/include/story.h index 0e4bfbd4..9f7e4935 100644 --- a/inkcpp/include/story.h +++ b/inkcpp/include/story.h @@ -19,6 +19,7 @@ namespace ink::runtime class story { public: + virtual ~story() = 0; #pragma region Interface Methods /** * Creates a new global store @@ -30,6 +31,7 @@ namespace ink::runtime * @return managed pointer to a new global store */ virtual globals new_globals() = 0; + virtual globals new_globals_from_snapshot(const snapshot&) = 0; /** * Creates a new runner @@ -41,6 +43,17 @@ namespace ink::runtime * @return managed pointer to a new runner */ virtual runner new_runner(globals store = nullptr) = 0; + /** + * @brief reconstruct runner from a snapshot + * @attention runner must be snap_shotted from the same story + * @attention if globals is explicit set, + * make sure the globals are from the same snapshot as + * @attention if you snap_shotted a multiple runner with shared global + * please reconstruct it in the same fashion + * @param store can be set if explicit access to globals is required or multiple runner with a shared global are used + * @param idx if the snapshot was of a multiple runner one global situation load first the global, and then each runner with global set and increasing idx + */ + virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) = 0; #pragma endregion #pragma region Factory Methods @@ -71,4 +84,4 @@ namespace ink::runtime static story* from_binary(unsigned char* data, size_t length, bool freeOnDestroy = true); #pragma endregion }; -} \ No newline at end of file +} diff --git a/inkcpp/include/types.h b/inkcpp/include/types.h index 406078d8..cdb22801 100644 --- a/inkcpp/include/types.h +++ b/inkcpp/include/types.h @@ -11,7 +11,8 @@ namespace ink::runtime { class globals_interface; class runner_interface; + class snapshot; typedef story_ptr globals; typedef story_ptr runner; -} \ No newline at end of file +} diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index f83a7e57..8f96c54b 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -236,6 +236,8 @@ namespace ink::runtime::internal _list_meta = nullptr; _lists = nullptr; } + inkAssert(_header.endien == header::endian_types::same, + "different endien support not yet implemented"); diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 53a08209..27259dce 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -177,6 +177,8 @@ int main(int argc, const char** argv) break; } + delete myInk; + return 0; } catch (const std::exception& e) From 43c3d5bf50a833d107f26fc092431af5bc992ba8 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Thu, 7 Jul 2022 22:50:32 +0200 Subject: [PATCH 30/67] More snapshot magic --- inkcpp/array.h | 30 +- inkcpp/avl_array.h | 38 +- inkcpp/globals_impl.cpp | 22 +- inkcpp/globals_impl.h | 21 +- inkcpp/include/globals.h | 5 +- inkcpp/include/snapshot.h | 9 +- inkcpp/list_table.cpp | 8 + inkcpp/list_table.h | 7 +- inkcpp/output.cpp | 820 +++++++++++++++++++------------------- inkcpp/output.h | 7 +- inkcpp/random.h | 9 +- inkcpp/runner_impl.cpp | 83 ++-- inkcpp/runner_impl.h | 70 ++-- inkcpp/snapshot_impl.cpp | 83 ++++ inkcpp/snapshot_impl.h | 62 +++ inkcpp/story_impl.h | 2 + inkcpp/string_table.cpp | 10 + inkcpp/string_table.h | 12 +- inkcpp/value.h | 7 +- 19 files changed, 802 insertions(+), 503 deletions(-) create mode 100644 inkcpp/snapshot_impl.cpp create mode 100644 inkcpp/snapshot_impl.h diff --git a/inkcpp/array.h b/inkcpp/array.h index 9ad97afb..8b6cfddb 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -1,11 +1,12 @@ #pragma once #include "system.h" +#include "snapshot_impl.h" namespace ink::runtime::internal { template - class managed_array { + class managed_array : snapshot_interface { public: managed_array() : _capacity{initialCapacity}, _size{0}{ if constexpr (dynamic) { @@ -48,11 +49,28 @@ namespace ink::runtime::internal } void clear() { _size = 0; } void resize(size_t size) { - ink_assert(size <= _size, "Only allow to reduce size"); + if constexpr (dynamic) { + if (size > _capacity) { + extend(size); + } + } else { + ink_assert(size <= _size, "Only allow to reduce size"); + } _size = size; } - void extend(); + void extend(size_t capacity = 0); + + size_t snap(unsigned char* data, const snapper&) const override + { + unsigned char* ptr = data; + ptr = snap_write(ptr, &_size, data); + for(const T& e : *this) { + ptr = snap_write(ptr, e, data); + } + return ptr - data; + } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; private: if_t _static_data[dynamic ? 1 : initialCapacity]; @@ -62,10 +80,12 @@ namespace ink::runtime::internal }; template - void managed_array::extend() + void managed_array::extend(size_t capacity) { static_assert(dynamic, "Can only extend if array is dynamic!"); - size_t new_capacity = 1.5f * _capacity; + size_t new_capacity = capacity > _capacity + ? 1.5f * _capacity + : capacity; if (new_capacity < 5) { new_capacity = 5; } T* new_data = new T[new_capacity]; diff --git a/inkcpp/avl_array.h b/inkcpp/avl_array.h index 9690149a..ed925706 100644 --- a/inkcpp/avl_array.h +++ b/inkcpp/avl_array.h @@ -41,6 +41,8 @@ #ifndef _AVL_ARRAY_H_ #define _AVL_ARRAY_H_ +#include "system.h" + #include @@ -73,16 +75,20 @@ class avl_array static const size_type INVALID_IDX = Size; // iterator class - typedef class tag_avl_array_iterator + template + class tag_avl_array_iterator { - avl_array* instance_; // array instance + template + using if_t = ink::runtime::internal::if_t; + if_t + instance_; // array instance size_type idx_; // actual node friend avl_array; // avl_array may access index pointer public: // ctor - tag_avl_array_iterator(avl_array* instance = nullptr, size_type idx = 0U) + tag_avl_array_iterator(if_t instance = nullptr, size_type idx = 0U) : instance_(instance) , idx_(idx) { } @@ -101,15 +107,15 @@ class avl_array { return !(*this == rhs); } // dereference - access value - inline T& operator*() const + inline if_t operator*() const { return val(); } // access value - inline T& val() const + inline if_t val() const { return instance_->val_[idx_]; } // access key - inline Key& key() const + inline const Key& key() const { return instance_->key_[idx_]; } // preincrement @@ -152,7 +158,7 @@ class avl_array ++(*this); return _copy; } - } avl_array_iterator; + }; public: @@ -163,7 +169,8 @@ class avl_array typedef T& reference; typedef const T& const_reference; typedef Key key_type; - typedef avl_array_iterator iterator; + typedef tag_avl_array_iterator iterator; + typedef tag_avl_array_iterator const_iterator; // ctor @@ -184,9 +191,24 @@ class avl_array return iterator(this, i); } + inline const_iterator begin() const + { + size_type i = INVALID_IDX; + if (root_ != INVALID_IDX) { + // find smallest element, it's the farthest node left from root + for (i = root_; child_[i].left != INVALID_IDX; i = child_[i].left); + } + return const_iterator(this, i); + } + inline iterator end() { return iterator(this, INVALID_IDX); } + inline const_iterator end() const + { + return const_iterator(this, INVALID_IDX); + } + // capacity inline size_type size() const diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 1749366e..90e5cb90 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -1,17 +1,21 @@ #include "globals_impl.h" #include "story_impl.h" #include "runner_impl.h" +#include "snapshot_impl.h" + +#include namespace ink::runtime::internal { globals_impl::globals_impl(const story_impl* story) : _num_containers(story->num_containers()) - , _visit_counts(_num_containers) + , _visit_counts() , _owner(story) , _runners_start(nullptr) , _lists(story->list_meta(), story->get_header()) , _globals_initialized(false) { + _visit_counts.resize(_num_containers); if (_lists) { // initelize static lists const list_flag* flags = story->lists(); @@ -216,4 +220,20 @@ namespace ink::runtime::internal { _variables.forget(); } + + snapshot* globals_impl::create_snapshot() const + { + return new snapshot_impl(*this); + } + + size_t globals_impl::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr; + inkAssert(_num_containers == _visit_counts.size(), "Should be equal!"); + inkAssert(_globals_initialized, "Only support snapshot of globals with runner! or you don't need a snapshot for this state"); + ptr += _visit_counts.snap( data ? ptr : nullptr, snapper ); + ptr += _strings.snap( data ? ptr : nullptr, snapper ); + ptr += _lists.snap( data ? ptr : nullptr, snapper ); + return ptr - data; + } } diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index a2d9704b..70ab71ae 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -6,20 +6,27 @@ #include "string_table.h" #include "list_table.h" #include "stack.h" +#include "snapshot_impl.h" namespace ink::runtime::internal { class story_impl; class runner_impl; + class snapshot_impl; // Implementation of the global store - class globals_impl : public globals_interface + class globals_impl : public globals_interface, public snapshot_interface { + friend snapshot_impl; + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; public: // Initializes a new global store from the given story globals_impl(const story_impl*); virtual ~globals_impl() { } + snapshot* create_snapshot() const override; + protected: optional get_uint(hash_t name) const override; bool set_uint(hash_t name, uint32_t value) override; @@ -66,6 +73,7 @@ namespace ink::runtime::internal // gets the allocated string table inline string_table& strings() { return _strings; } + inline const string_table& strings() const { return _strings; } // gets list entries list_table& lists() { return _lists; } @@ -93,16 +101,7 @@ namespace ink::runtime::internal return !(*this == vc); } }; - class visit_counts{ - visit_count* _data; - size_t _len; - public: - visit_counts(size_t len) - : _data{new visit_count[len]}, _len{len} {} - size_t size() const { return _len; } - visit_count& operator[](size_t i) { return _data[i]; } - const visit_count& operator[](size_t i) const { return _data[i]; } - } _visit_counts; + managed_array _visit_counts; // Pointer back to owner story. const story_impl* const _owner; diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index ae6b1a97..c39bc9d6 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -4,8 +4,7 @@ namespace ink::runtime { - class globals_interface; - namespace internal { class globals_impl;} + class snapshot; /** * Represents a global store to be shared amongst ink runners. @@ -42,6 +41,8 @@ namespace ink::runtime return false; } + virtual snapshot* create_snapshot() const = 0; + virtual ~globals_interface() = default; protected: diff --git a/inkcpp/include/snapshot.h b/inkcpp/include/snapshot.h index 733906f3..8d89c687 100644 --- a/inkcpp/include/snapshot.h +++ b/inkcpp/include/snapshot.h @@ -6,13 +6,14 @@ namespace ink::runtime { class snapshot { public: - snapshot() = delete; virtual ~snapshot() = 0; - static snapshot* from_binary(unsigned char* data, size_t length, bool freeOnDestroy = true); + static snapshot* from_binary(const unsigned char* data, size_t length, bool freeOnDestroy = true); +#ifdef INK_ENABLE_STL static snapshot* from_file(const char* filename); +#endif - virtual const char* get_data() const = 0; - virtual unsigned get_data_len() const = 0; + virtual const unsigned char* get_data() const = 0; + virtual size_t get_data_len() const = 0; }; } diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index 06b57aa8..cf8a9cb5 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -650,5 +650,13 @@ namespace ink::runtime::internal } #endif + size_t list_table::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr += _data.snap(data ? ptr : nullptr, snapper); + ptr += _entry_state.snap(data ? ptr : nullptr, snapper); + return ptr - data; + } + } diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index 3ebd5497..d3396b29 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -2,6 +2,7 @@ #include "system.h" #include "array.h" +#include "snapshot_impl.h" #ifdef INK_ENABLE_STL #include @@ -24,7 +25,7 @@ namespace ink::runtime::internal } /// managed all list entries and list metadata - class list_table + class list_table : public snapshot_interface { using data_t = int; enum class state : char { @@ -77,6 +78,10 @@ namespace ink::runtime::internal */ char* toString(char* out, const list& l) const; + // snapshot interface implementation + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + /** special traitment when a list get assignet again * when a list get assigned and would have no origin, it gets the origin of the base with origin * eg. I072 diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 559de9ff..976faa54 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -8,500 +8,506 @@ #include #endif -namespace ink +namespace ink::runtime::internal { - namespace runtime + basic_stream::basic_stream(value* buffer, size_t len) + : _data(buffer), _max(len), _size(0), _save(~0) + {} + + void basic_stream::append(const value& in) { - namespace internal + // SPECIAL: Incoming newline + if (in.type() == value_type::newline && _size > 1) { - basic_stream::basic_stream(value* buffer, size_t len) - : _data(buffer), _max(len), _size(0), _save(~0) - {} + // If the end of the stream is a function start marker, we actually + // want to ignore this. Function start trimming. + if (_data[_size - 1].type() == value_type::func_start) + return; + } - void basic_stream::append(const value& in) - { - // SPECIAL: Incoming newline - if (in.type() == value_type::newline && _size > 1) - { - // If the end of the stream is a function start marker, we actually - // want to ignore this. Function start trimming. - if (_data[_size - 1].type() == value_type::func_start) - return; - } + // Ignore leading newlines + if (in.type() == value_type::newline && _size == 0) + return; - // Ignore leading newlines - if (in.type() == value_type::newline && _size == 0) - return; + // Add to data stream + inkAssert(_size < _max, "Output stream overflow"); + _data[_size++] = in; - // Add to data stream - inkAssert(_size < _max, "Output stream overflow"); - _data[_size++] = in; + // Special: Incoming glue. Trim whitespace/newlines prior + // This also applies when a function ends to trim trailing whitespace. + if ((in.type() == value_type::glue || in.type() == value_type::func_end) && _size > 1) + { + // Run backwards + size_t i = _size - 2; + while(true) + { + value& d = _data[i]; - // Special: Incoming glue. Trim whitespace/newlines prior - // This also applies when a function ends to trim trailing whitespace. - if ((in.type() == value_type::glue || in.type() == value_type::func_end) && _size > 1) - { - // Run backwards - size_t i = _size - 2; - while(true) - { - value& d = _data[i]; - - // Nullify newlines - if (d.type() == value_type::newline) { - d = value{}; - } - - // Nullify whitespace - else if ( d.type() == value_type::string - && is_whitespace(d.get())) - d = value{}; - - // If it's not a newline or whitespace, stop - else break; - - // If we've hit the end, break - if (i == 0) - break; - - // Move on to next element - i--; - } + // Nullify newlines + if (d.type() == value_type::newline) { + d = value{}; } - } - void basic_stream::append(const value* in, unsigned int length) - { - // TODO: Better way to bulk while still executing glue checks? - for (size_t i = 0; i < length; i++) - append(in[i]); - } + // Nullify whitespace + else if ( d.type() == value_type::string + && is_whitespace(d.get())) + d = value{}; - template - inline void write_char(OUT& output, char c) - { - static_assert(always_false::value, "Invalid output type"); - } + // If it's not a newline or whitespace, stop + else break; - template<> - inline void write_char(char*& output, char c) - { - (*output++) = c; - } + // If we've hit the end, break + if (i == 0) + break; - template<> - inline void write_char(std::stringstream& output, char c) - { - output.put(c); + // Move on to next element + i--; } + } + } - inline bool get_next(const value* list, size_t i, size_t size, const value** next) - { - while (i + 1 < size) - { - *next = &list[i + 1]; - value_type type = (*next)->type(); - if ((*next)->printable()) { return true; } - i++; - } + void basic_stream::append(const value* in, unsigned int length) + { + // TODO: Better way to bulk while still executing glue checks? + for (size_t i = 0; i < length; i++) + append(in[i]); + } - return false; - } + template + inline void write_char(OUT& output, char c) + { + static_assert(always_false::value, "Invalid output type"); + } - template - void basic_stream::copy_string(const char* str, size_t& dataIter, OUT& output) - { - while(*str != 0) { - write_char(output, *str++); - } - } + template<> + inline void write_char(char*& output, char c) + { + (*output++) = c; + } -#ifdef INK_ENABLE_STL - std::string basic_stream::get() - { - size_t start = find_start(); + template<> + inline void write_char(std::stringstream& output, char c) + { + output.put(c); + } - // Move up from marker - bool hasGlue = false, lastNewline = false; - std::stringstream str; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue, lastNewline)) - continue; - if (_data[i].printable()){ - _data[i].write(str, _lists_table); - } + inline bool get_next(const value* list, size_t i, size_t size, const value** next) + { + while (i + 1 < size) + { + *next = &list[i + 1]; + value_type type = (*next)->type(); + if ((*next)->printable()) { return true; } + i++; + } - } + return false; + } + + template + void basic_stream::copy_string(const char* str, size_t& dataIter, OUT& output) + { + while(*str != 0) { + write_char(output, *str++); + } + } - // Reset stream size to where we last held the marker - _size = start; +#ifdef INK_ENABLE_STL + std::string basic_stream::get() + { + size_t start = find_start(); - // Return processed string - // remove mulitple accourencies of ' ' - std::string result = str.str(); - auto end = clean_string(result.begin(), result.end()); - _last_char = *(end-1); - result.resize(end - result.begin() - (_last_char == ' ' ? 1 : 0)); - return result; + // Move up from marker + bool hasGlue = false, lastNewline = false; + std::stringstream str; + for (size_t i = start; i < _size; i++) + { + if (should_skip(i, hasGlue, lastNewline)) + continue; + if (_data[i].printable()){ + _data[i].write(str, _lists_table); } -#endif -#ifdef INK_ENABLE_UNREAL - FString basic_stream::get() - { - size_t start = find_start(); - // TODO: Slow! FString concatonation. - // Is there really no equivilent of stringstream in Unreal? Some kind of String Builder? + } - // Move up from marker - bool hasGlue = false; - FString str; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue)) - continue; + // Reset stream size to where we last held the marker + _size = start; - switch (_data[i].type) - { - case value_type::int32: - str += FString::Printf(TEXT("%d"), _data[i].integer_value); - break; - case value_type::float32: - // TODO: Whitespace cleaning - str += FString::Printf(TEXT("%f"), _data[i].float_value); - break; - case value_type::string: - str += _data[i].string_val; - break; - case data_type::newline: - str += "\n"; - break; - default: - break; - } - } + // Return processed string + // remove mulitple accourencies of ' ' + std::string result = str.str(); + auto end = clean_string(result.begin(), result.end()); + _last_char = *(end-1); + result.resize(end - result.begin() - (_last_char == ' ' ? 1 : 0)); + return result; + } +#endif +#ifdef INK_ENABLE_UNREAL + FString basic_stream::get() + { + size_t start = find_start(); - // Reset stream size to where we last held the marker - _size = start; + // TODO: Slow! FString concatonation. + // Is there really no equivilent of stringstream in Unreal? Some kind of String Builder? - // Return processed string - return str; - } -#endif + // Move up from marker + bool hasGlue = false; + FString str; + for (size_t i = start; i < _size; i++) + { + if (should_skip(i, hasGlue)) + continue; - int basic_stream::queued() const + switch (_data[i].type) { - size_t start = find_start(); - return _size - start; + case value_type::int32: + str += FString::Printf(TEXT("%d"), _data[i].integer_value); + break; + case value_type::float32: + // TODO: Whitespace cleaning + str += FString::Printf(TEXT("%f"), _data[i].float_value); + break; + case value_type::string: + str += _data[i].string_val; + break; + case data_type::newline: + str += "\n"; + break; + default: + break; } + } - const value& basic_stream::peek() const - { - inkAssert(_size > 0, "Attempting to peek empty stream!"); - return _data[_size - 1]; - } + // Reset stream size to where we last held the marker + _size = start; - void basic_stream::discard(size_t length) - { - // discard elements - _size -= length; - if (_size < 0) - _size = 0; - } + // Return processed string + return str; + } +#endif - void basic_stream::get(value* ptr, size_t length) - { - // Find start - size_t start = find_start(); + int basic_stream::queued() const + { + size_t start = find_start(); + return _size - start; + } - const value* end = ptr + length; - //inkAssert(_size - start < length, "Insufficient space in data array to store stream contents!"); + const value& basic_stream::peek() const + { + inkAssert(_size > 0, "Attempting to peek empty stream!"); + return _data[_size - 1]; + } - // Move up from marker - bool hasGlue = false, lastNewline = false; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue, lastNewline)) - continue; + void basic_stream::discard(size_t length) + { + // discard elements + _size -= length; + if (_size < 0) + _size = 0; + } - // Make sure we can fit the next element - inkAssert(ptr < end, "Insufficient space in data array to store stream contents!"); + void basic_stream::get(value* ptr, size_t length) + { + // Find start + size_t start = find_start(); - // Copy any value elements - if (_data[i].printable()) { - *(ptr++) = _data[i]; - } - } + const value* end = ptr + length; + //inkAssert(_size - start < length, "Insufficient space in data array to store stream contents!"); - // Reset stream size to where we last held the marker - _size = start; - } + // Move up from marker + bool hasGlue = false, lastNewline = false; + for (size_t i = start; i < _size; i++) + { + if (should_skip(i, hasGlue, lastNewline)) + continue; - bool basic_stream::has_marker() const - { - // TODO: Cache? - for (size_t i = 0; i < _size; i++) - { - if (_data[i].type() == value_type::marker) - return true; - } + // Make sure we can fit the next element + inkAssert(ptr < end, "Insufficient space in data array to store stream contents!"); - return false; + // Copy any value elements + if (_data[i].printable()) { + *(ptr++) = _data[i]; } + } - bool basic_stream::ends_with(value_type type) const - { - if (_size == 0) - return false; + // Reset stream size to where we last held the marker + _size = start; + } - return _data[_size - 1].type() == type; - } + bool basic_stream::has_marker() const + { + // TODO: Cache? + for (size_t i = 0; i < _size; i++) + { + if (_data[i].type() == value_type::marker) + return true; + } - bool basic_stream::saved_ends_with(value_type type) const - { - inkAssert(_save != ~0, "Stream is not saved!"); + return false; + } - if (_save == 0) - return false; + bool basic_stream::ends_with(value_type type) const + { + if (_size == 0) + return false; - return _data[_save - 1].type() == type; - } + return _data[_size - 1].type() == type; + } - void basic_stream::save() - { - inkAssert(_save == ~0, "Can not save over existing save point!"); + bool basic_stream::saved_ends_with(value_type type) const + { + inkAssert(_save != ~0, "Stream is not saved!"); - // Save the current size - _save = _size; - } + if (_save == 0) + return false; - void basic_stream::restore() - { - inkAssert(_save != ~0, "No save point to restore!"); + return _data[_save - 1].type() == type; + } - // Restore size to saved position - _size = _save; - _save = ~0; - } + void basic_stream::save() + { + inkAssert(_save == ~0, "Can not save over existing save point!"); - void basic_stream::forget() - { - inkAssert(_save != ~0, "No save point to forget!"); + // Save the current size + _save = _size; + } - // Just null the save point and continue as normal - _save = ~0; - } - - template char* basic_stream::get_alloc(string_table& strings, list_table& lists); - template char* basic_stream::get_alloc(string_table& strings, list_table& lists); + void basic_stream::restore() + { + inkAssert(_save != ~0, "No save point to restore!"); - template - char* basic_stream::get_alloc(string_table& strings, list_table& lists) - { - size_t start = find_start(); + // Restore size to saved position + _size = _save; + _save = ~0; + } - // Two passes. First for length - size_t length = 0; - bool hasGlue = false, lastNewline = false; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue, lastNewline)) - continue; - ++length; // potenzial space to sperate - if (_data[i].printable()) { - switch(_data[i].type()) { - case value_type::list: - length += lists.stringLen(_data[i].get()); - break; - case value_type::list_flag: - length += lists.stringLen(_data[i].get()); - default: length += value_length(_data[i]); - } - } - } + void basic_stream::forget() + { + inkAssert(_save != ~0, "No save point to forget!"); - // Allocate - char* buffer = strings.create(length + 1); - char* end = buffer + length + 1; - char* ptr = buffer; - hasGlue = false; lastNewline = false; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue, lastNewline)) - continue; - if(!_data[i].printable()) { continue; } - switch (_data[i].type()) - { - case value_type::int32: - case value_type::float32: - case value_type::uint32: - // Convert to string and advance - toStr(ptr, end - ptr, _data[i]); - while (*ptr != 0) ptr++; + // Just null the save point and continue as normal + _save = ~0; + } + + template char* basic_stream::get_alloc(string_table& strings, list_table& lists); + template char* basic_stream::get_alloc(string_table& strings, list_table& lists); - break; - case value_type::string: - { - // Copy string and advance - const char* value = _data[i].get(); - copy_string(value, i, ptr); - } break; - case value_type::newline: - *ptr = '\n'; ptr++; - break; + template + char* basic_stream::get_alloc(string_table& strings, list_table& lists) + { + size_t start = find_start(); + + // Two passes. First for length + size_t length = 0; + bool hasGlue = false, lastNewline = false; + for (size_t i = start; i < _size; i++) + { + if (should_skip(i, hasGlue, lastNewline)) + continue; + ++length; // potenzial space to sperate + if (_data[i].printable()) { + switch(_data[i].type()) { case value_type::list: - ptr = lists.toString(ptr, _data[i].get()); + length += lists.stringLen(_data[i].get()); break; case value_type::list_flag: - ptr = lists.toString(ptr, _data[i].get()); - break; - default: throw ink_exception("cant convert expression to string!"); - } + length += lists.stringLen(_data[i].get()); + default: length += value_length(_data[i]); } + } + } - // Make sure last character is a null - *ptr = 0; + // Allocate + char* buffer = strings.create(length + 1); + char* end = buffer + length + 1; + char* ptr = buffer; + hasGlue = false; lastNewline = false; + for (size_t i = start; i < _size; i++) + { + if (should_skip(i, hasGlue, lastNewline)) + continue; + if(!_data[i].printable()) { continue; } + switch (_data[i].type()) + { + case value_type::int32: + case value_type::float32: + case value_type::uint32: + // Convert to string and advance + toStr(ptr, end - ptr, _data[i]); + while (*ptr != 0) ptr++; + + break; + case value_type::string: + { + // Copy string and advance + const char* value = _data[i].get(); + copy_string(value, i, ptr); + } break; + case value_type::newline: + *ptr = '\n'; ptr++; + break; + case value_type::list: + ptr = lists.toString(ptr, _data[i].get()); + break; + case value_type::list_flag: + ptr = lists.toString(ptr, _data[i].get()); + break; + default: throw ink_exception("cant convert expression to string!"); + } + } - // Reset stream size to where we last held the marker - _size = start; + // Make sure last character is a null + *ptr = 0; - // Return processed string - { - auto end = clean_string(buffer, buffer+length); - *end = 0; - _last_char = end[-1]; - if constexpr (RemoveTail) { - if (_last_char == ' ') { end[-1] = 0; } - } - } - return buffer; - } + // Reset stream size to where we last held the marker + _size = start; - size_t basic_stream::find_start() const - { - // Find marker (or start) - size_t start = _size; - while (start > 0) - { - start--; - if (_data[start].type() == value_type::marker) - break; - } + // Return processed string + { + auto end = clean_string(buffer, buffer+length); + *end = 0; + _last_char = end[-1]; + if constexpr (RemoveTail) { + if (_last_char == ' ') { end[-1] = 0; } + } + } + return buffer; + } - // Make sure we're not violating a save point - if (_save != ~0 && start < _save) { - // TODO: check if we don't reset save correct - // at some point we can modifiy the output even behind save (probally discard?) and push a new element -> invalid save point - // inkAssert(false, "Trying to access output stream prior to save point!"); - const_cast(*this).clear(); - } + size_t basic_stream::find_start() const + { + // Find marker (or start) + size_t start = _size; + while (start > 0) + { + start--; + if (_data[start].type() == value_type::marker) + break; + } - return start; - } + // Make sure we're not violating a save point + if (_save != ~0 && start < _save) { + // TODO: check if we don't reset save correct + // at some point we can modifiy the output even behind save (probally discard?) and push a new element -> invalid save point + // inkAssert(false, "Trying to access output stream prior to save point!"); + const_cast(*this).clear(); + } - bool basic_stream::should_skip(size_t iter, bool& hasGlue, bool& lastNewline) const + return start; + } + + bool basic_stream::should_skip(size_t iter, bool& hasGlue, bool& lastNewline) const + { + if (_data[iter].printable() + && _data[iter].type() != value_type::newline + && _data[iter].type() != value_type::string) { + lastNewline = false; + hasGlue = false; + } else { + switch (_data[iter].type()) + { + case value_type::newline: + if (lastNewline) + return true; + if (hasGlue) + return true; + lastNewline = true; + break; + case value_type::glue: + hasGlue = true; + break; + case value_type::string: { - if (_data[iter].printable() - && _data[iter].type() != value_type::newline - && _data[iter].type() != value_type::string) { - lastNewline = false; - hasGlue = false; - } else { - switch (_data[iter].type()) + lastNewline = false; + // an empty string don't count as glued I095 + for(const char* i=_data[iter].get(); + *i; ++i) { - case value_type::newline: - if (lastNewline) - return true; - if (hasGlue) - return true; - lastNewline = true; - break; - case value_type::glue: - hasGlue = true; - break; - case value_type::string: - { - lastNewline = false; - // an empty string don't count as glued I095 - for(const char* i=_data[iter].get(); - *i; ++i) - { - // isspace only supports characters in [0, UCHAR_MAX] - if (!isspace(static_cast(*i))) { - hasGlue = false; - break; - } - } - } break; - default: + // isspace only supports characters in [0, UCHAR_MAX] + if (!isspace(static_cast(*i))) { + hasGlue = false; break; } } - - return false; + } break; + default: + break; } + } - bool basic_stream::text_past_save() const - { - // Check if there is text past the save - for (size_t i = _save; i < _size; i++) - { - const value& d = _data[i]; - if (d.type() == value_type::string) - { - // TODO: Cache what counts as whitespace? - if (!is_whitespace(d.get(), false)) - return true; - } - } - - // No text - return false; - } + return false; + } - void basic_stream::clear() + bool basic_stream::text_past_save() const + { + // Check if there is text past the save + for (size_t i = _save; i < _size; i++) + { + const value& d = _data[i]; + if (d.type() == value_type::string) { - _save = ~0; - _size = 0; + // TODO: Cache what counts as whitespace? + if (!is_whitespace(d.get(), false)) + return true; } + } - void basic_stream::mark_strings(string_table& strings) const - { - // Find all allocated strings and mark them as used - for (int i = 0; i < _size; i++) - { - if (_data[i].type() == value_type::string) { - string_type str = _data[i].get(); - if (str.allocated) { - strings.mark_used(str.str); - } - } + // No text + return false; + } + + void basic_stream::clear() + { + _save = ~0; + _size = 0; + } + + void basic_stream::mark_strings(string_table& strings) const + { + // Find all allocated strings and mark them as used + for (int i = 0; i < _size; i++) + { + if (_data[i].type() == value_type::string) { + string_type str = _data[i].get(); + if (str.allocated) { + strings.mark_used(str.str); } } + } + } #ifdef INK_ENABLE_STL - std::ostream& operator<<(std::ostream& out, basic_stream& in) - { - out << in.get(); - return out; - } + std::ostream& operator<<(std::ostream& out, basic_stream& in) + { + out << in.get(); + return out; + } - basic_stream& operator>>(basic_stream& in, std::string& out) - { - out = in.get(); - return in; - } + basic_stream& operator>>(basic_stream& in, std::string& out) + { + out = in.get(); + return in; + } #endif #ifdef INK_ENABLE_UNREAL - basic_stream& operator>>(basic_stream& in, FString& out) - { - out = in.get(); - return in; - } + basic_stream& operator>>(basic_stream& in, FString& out) + { + out = in.get(); + return in; + } #endif - + size_t basic_stream::snap(unsigned char* data, const snapper& snapper) const + { + const string_table& strings = snapper.strings; + unsigned char* ptr = data; + ptr = snap_write(ptr, _last_char, data); + ptr = snap_write(ptr, _size, data); + ptr = snap_write(ptr, _save, data); + for(auto itr = _data; itr != _data + _size; ++itr) + { + ptr += itr->snap(data ? ptr : nullptr, snapper); } + return ptr - data; } } diff --git a/inkcpp/output.h b/inkcpp/output.h index 60663f8e..1185ee20 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -2,6 +2,7 @@ #include "value.h" #include "platform.h" +#include "snapshot_impl.h" namespace ink { @@ -11,7 +12,7 @@ namespace ink { class string_table; class list_table; - class basic_stream + class basic_stream : public snapshot_interface { protected: basic_stream(value*, size_t); @@ -99,6 +100,10 @@ namespace ink bool saved() const { return _save != ~0; } + // snapshot interface + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + private: size_t find_start() const; bool should_skip(size_t iter, bool& hasGlue, bool& lastNewline) const; diff --git a/inkcpp/random.h b/inkcpp/random.h index 995b7947..0738aa75 100644 --- a/inkcpp/random.h +++ b/inkcpp/random.h @@ -7,15 +7,15 @@ namespace ink::runtime::internal { * @brief pseudo random number generator based on Linear Congruential Generator. */ class prng { - static constexpr uint32_t C = 12345; - static constexpr uint32_t A = 1103515245; - static constexpr uint32_t M = 1<<31; + static constexpr uint64_t C = 12345; + static constexpr uint64_t A = 1103515245; + static constexpr uint64_t M = 1<<31; public: void srand(int32_t seed) { _x = seed; } uint32_t rand() { - _x = (A*_x+ C) % M; + _x = static_cast((A*_x+ C) % M); return _x; } int32_t rand(int32_t max) { @@ -23,6 +23,7 @@ namespace ink::runtime::internal { prod *= max; return static_cast(prod / M); } + uint32_t get_state() const { return _x; } private: uint32_t _x = 1337; }; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index ee51511a..499ae741 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -5,6 +5,7 @@ #include "globals_impl.h" #include "header.h" #include "string_utils.h" +#include "snapshot_impl.h" namespace ink::runtime { @@ -269,10 +270,10 @@ namespace ink::runtime::internal // Push next address onto the callstack { size_t address = _ptr - _story->instructions(); - _stack.push_frame(address, bEvaluationMode); - _ref_stack.push_frame(address, bEvaluationMode); + _stack.push_frame(address, _evaluation_mode); + _ref_stack.push_frame(address, _evaluation_mode); } - bEvaluationMode = false; // unset eval mode when enter function or tunnel + _evaluation_mode = false; // unset eval mode when enter function or tunnel // Do the jump inkAssert(_story->instructions() + target < _story->end(), "Diverting past end of story data!"); @@ -284,12 +285,12 @@ namespace ink::runtime::internal // Pop the callstack _ref_stack.fetch_values(_stack); frame_type type; - offset_t offset = _stack.pop_frame(&type,bEvaluationMode); + offset_t offset = _stack.pop_frame(&type,_evaluation_mode); _ref_stack.push_values(_stack); { frame_type t; bool eval; // TODO: write all refs to new frame offset_t o = _ref_stack.pop_frame(&t, eval); - inkAssert(o == offset && t == type && eval == bEvaluationMode, + inkAssert(o == offset && t == type && eval == _evaluation_mode, "_ref_stack and _stack should be in frame sync!"); } @@ -327,7 +328,7 @@ namespace ink::runtime::internal _backup(nullptr), _done(nullptr), _choices() { _ptr = _story->instructions(); - bEvaluationMode = false; + _evaluation_mode = false; // register with globals _globals->add_runner(this); @@ -525,6 +526,34 @@ namespace ink::runtime::internal return _tags[index]; } + snapshot* runner_impl::create_snapshot() const + { + return _globals->create_snapshot(); + } + + size_t runner_impl::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _ptr, data); + ptr = snap_write(ptr, _backup, data); + ptr = snap_write(ptr, _done, data); + ptr = snap_write(ptr, _rng.get_state(), data); + ptr = snap_write(ptr, _evaluation_mode, data); + ptr = snap_write(ptr, _saved_evaluation_mode, data); + ptr = snap_write(ptr, _eval, data); + ptr = snap_write(ptr, _saved, data); + ptr = snap_write(ptr, _is_falling, data); + ptr += _output.snap(data ? ptr : nullptr, snapper); + // _output + // _stack + // _ref_stack + // _threads + // _tags + // _choices + // _container + return ptr - data; + } + #ifdef INK_ENABLE_CSTD char* runner_impl::getline_alloc() { @@ -665,7 +694,7 @@ namespace ink::runtime::internal case Command::STR: { const char* str = read(); - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(value{}.set(str)); else _output << value{}.set(str); @@ -674,7 +703,7 @@ namespace ink::runtime::internal case Command::INT: { int val = read(); - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(value{}.set(val)); // TEST-CASE B006 don't print integers } @@ -682,7 +711,7 @@ namespace ink::runtime::internal case Command::BOOL: { bool val = read() ? true : false; - if(bEvaluationMode) + if(_evaluation_mode) _eval.push(value{}.set(val)); else _output << value{}.set(val); @@ -691,14 +720,14 @@ namespace ink::runtime::internal case Command::FLOAT: { float val = read(); - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(value{}.set(val)); // TEST-CASE B006 don't print floats } break; case Command::VALUE_POINTER: { hash_t val = read(); - if(bEvaluationMode) { + if(_evaluation_mode) { _eval.push(value{}.set(val, static_cast(flag) - 1)); } else { throw ink_exception("never conciderd what should happend here! (value pointer print)"); @@ -708,7 +737,7 @@ namespace ink::runtime::internal case Command::LIST: { list_table::list list(read()); - if(bEvaluationMode) + if(_evaluation_mode) _eval.push(value{}.set(list)); else { char* str = _globals->strings().create(_globals->lists().stringLen( @@ -720,7 +749,7 @@ namespace ink::runtime::internal break; case Command::DIVERT_VAL: { - inkAssert(bEvaluationMode, "Can not push divert value into the output stream!"); + inkAssert(_evaluation_mode, "Can not push divert value into the output stream!"); // Push the divert target onto the stack uint32_t target = read(); @@ -729,7 +758,7 @@ namespace ink::runtime::internal break; case Command::NEWLINE: { - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(values::newline); else _output << values::newline; @@ -737,7 +766,7 @@ namespace ink::runtime::internal break; case Command::GLUE: { - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(values::glue); else _output << values::glue; @@ -745,7 +774,7 @@ namespace ink::runtime::internal break; case Command::VOID: { - if (bEvaluationMode) + if (_evaluation_mode) _eval.push(values::null); // TODO: void type? } break; @@ -865,8 +894,8 @@ namespace ink::runtime::internal // Push a thread frame so we can return easily // TODO We push ahead of a single divert. Is that correct in all cases....????? auto returnTo = _ptr + CommandSize; - _stack.push_frame(returnTo - _story->instructions(), bEvaluationMode); - _ref_stack.push_frame(returnTo - _story->instructions(), bEvaluationMode); + _stack.push_frame(returnTo - _story->instructions(), _evaluation_mode); + _ref_stack.push_frame(returnTo - _story->instructions(), _evaluation_mode); // Fork a new thread on the callstack thread_t thread = _stack.fork_thread(); @@ -939,10 +968,10 @@ namespace ink::runtime::internal // == Evaluation stack case Command::START_EVAL: - bEvaluationMode = true; + _evaluation_mode = true; break; case Command::END_EVAL: - bEvaluationMode = false; + _evaluation_mode = false; // Assert stack is empty? Is that necessary? break; @@ -970,15 +999,15 @@ namespace ink::runtime::internal } case Command::START_STR: { - inkAssert(bEvaluationMode, "Can not enter string mode while not in evaluation mode!"); - bEvaluationMode = false; + inkAssert(_evaluation_mode, "Can not enter string mode while not in evaluation mode!"); + _evaluation_mode = false; _output << values::marker; } break; case Command::END_STR: { // TODO: Assert we really had a marker on there? - inkAssert(!bEvaluationMode, "Must be in evaluation mode"); - bEvaluationMode = true; + inkAssert(!_evaluation_mode, "Must be in evaluation mode"); + _evaluation_mode = true; // Load value from output stream // Push onto stack @@ -1184,7 +1213,7 @@ namespace ink::runtime::internal _stack.clear(); _ref_stack.clear(); _threads.clear(); - bEvaluationMode = false; + _evaluation_mode = false; _saved = false; _choices.clear(); _ptr = nullptr; @@ -1219,7 +1248,7 @@ namespace ink::runtime::internal _eval.save(); _threads.save(); _backup_choice_len = _choices.size(); - bSavedEvaluationMode = bEvaluationMode; + _saved_evaluation_mode = _evaluation_mode; // Not doing this anymore. There can be lingering stack entries from function returns // inkAssert(_eval.is_empty(), "Can not save interpreter state while eval stack is not empty"); @@ -1238,7 +1267,7 @@ namespace ink::runtime::internal _eval.restore(); _threads.restore(); _choices.resize(_backup_choice_len); - bEvaluationMode = bSavedEvaluationMode; + _evaluation_mode = _saved_evaluation_mode; // Not doing this anymore. There can be lingering stack entries from function returns // inkAssert(_eval.is_empty(), "Can not save interpreter state while eval stack is not empty"); diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 7f208036..5f8eb15f 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -13,6 +13,7 @@ #include "list_table.h" #include "array.h" #include "random.h" +#include "snapshot_impl.h" #include "runner.h" #include "choice.h" @@ -23,8 +24,10 @@ namespace ink::runtime::internal { class story_impl; class globals_impl; + class snapshot_impl; - class runner_impl : public runner_interface + + class runner_impl : public runner_interface, public snapshot_interface { public: // Creates a new runner at the start of a loaded ink story @@ -55,6 +58,11 @@ namespace ink::runtime::internal virtual size_t num_tags() const override; virtual const char* get_tag(size_t index) const override; + snapshot* create_snapshot() const override; + + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + #ifdef INK_ENABLE_CSTD // c-style getline @@ -146,31 +154,7 @@ namespace ink::runtime::internal inline thread_t current_thread() const { return _threads.empty() ? ~0 : _threads.top(); } - private: - const story_impl* const _story; - story_ptr _globals; - executer _operations; - - // == State == - - // Instruction pointer - ip_t _ptr; - ip_t _backup; // backup pointer - ip_t _done; // when we last hit a done - - // Output stream - internal::stream _output; - - // Runtime stack. Used to store temporary variables and callstack - internal::stack _stack; - internal::stack _ref_stack; - - // Evaluation stack - bool bEvaluationMode = false; - internal::eval_stack _eval; - bool bSavedEvaluationMode = false; - - // Keeps track of what threads we're inside + public: class threads : public internal::simple_restorable_stack { using base = internal::simple_restorable_stack; static constexpr bool dynamic = config::limitThreadDepth < 0; @@ -222,14 +206,40 @@ namespace ink::runtime::internal using array_type = if_t, internal::fixed_restorable_array>; - template - void resize(size_t size, int) { (_threadDone.*A)(size); } - void resize(size_t size, long) {} + + void resize(size_t size, int) { _threadDone.resize(size); } managed_array _stack; array_type _threadDone; - } _threads; + }; + + private: + const story_impl* const _story; + story_ptr _globals; + executer _operations; + + // == State == + + // Instruction pointer + ip_t _ptr; + ip_t _backup; // backup pointer + ip_t _done; // when we last hit a done + + // Output stream + internal::stream _output; + + // Runtime stack. Used to store temporary variables and callstack + internal::stack _stack; + internal::stack _ref_stack; + + // Evaluation stack + bool _evaluation_mode = false; + internal::eval_stack _eval; + bool _saved_evaluation_mode = false; + + // Keeps track of what threads we're inside + threads _threads; // Choice list managed_array _choices; diff --git a/inkcpp/snapshot_impl.cpp b/inkcpp/snapshot_impl.cpp new file mode 100644 index 00000000..ae693367 --- /dev/null +++ b/inkcpp/snapshot_impl.cpp @@ -0,0 +1,83 @@ +#include "snapshot_impl.h" +#include "globals_impl.h" +#include "runner_impl.h" + +#include +#ifdef INK_ENABLE_STL +#include +#endif + +namespace ink::runtime +{ + snapshot* snapshot::from_binary(const unsigned char* data, size_t length, bool freeOnDestroy) + { + return new internal::snapshot_impl(data, length, freeOnDestroy); + } + +#ifdef INK_ENABLE_STL + snapshot* snapshot::from_file(const char* filename) { + std::ifstream ifs(filename, std::ios::binary | std::ios::ate); + if(!ifs.is_open()) { + throw ink_exception("Failed to open snapshot file: " + std::string(filename)); + } + + size_t length = static_cast(ifs.tellg()); + unsigned char* data = new unsigned char[length]; + ifs.seekg(0, std::ios::beg); + ifs.read(reinterpret_cast(data), length); + ifs.close(); + + return from_binary(data, length); + } +#endif +} + +namespace ink::runtime::internal +{ + size_t snapshot_impl::file_size(size_t serialization_length, size_t runner_cnt) { + return serialization_length + sizeof(header) + runner_cnt * sizeof(size_t); + } + + const unsigned char* snapshot_impl::get_data() const { + return _file; + } + size_t snapshot_impl::get_data_len() const { + return _length; + } + + snapshot_impl::snapshot_impl(const globals_impl& globals) + : _managed{true} + { + _length = globals.snap(); + size_t runner_cnt = 0; + for(auto node = globals._runners_start; node; node = node->next) + { + _length += node->object->snap(); + ++runner_cnt; + } + + _length = file_size(_length, runner_cnt); + unsigned char* data = new unsigned char[_length]; + _file = data; + unsigned char* ptr = data; + // write header + memcpy(ptr, &_header, sizeof(_header)); + // write lookup table + ptr += sizeof(header); + { + size_t offset = 0; + for(auto node = globals._runners_start; node; node = node->next) + { + offset += node->object->snap(); + memcpy(ptr, &offset, sizeof(offset)); + ptr += sizeof(offset); + } + } + + ptr += globals.snap(ptr); + for (auto node = globals._runners_start; node; node = node->next) + { + ptr += node->object->snap(ptr); + } + } +} diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h new file mode 100644 index 00000000..e65844ef --- /dev/null +++ b/inkcpp/snapshot_impl.h @@ -0,0 +1,62 @@ +#pragma once + +#include "snapshot.h" + +#include + +namespace ink::runtime::internal +{ + class globals_impl; + class value; + class string_table; + class snapshot_interface { + protected: + static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) + { + memcpy(ptr, data, length); + return ptr += length; + } + template + static unsigned char* snap_write(unsigned char* ptr, const T& data, bool write) + { + return snap_write(ptr, &data, sizeof(data), write); + } + public: + struct snapper { + const string_table& strings; + }; + struct loader { + string_table& strings; + }; + virtual size_t snap(unsigned char* data, const snapper&) const = 0; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) = 0; + }; + class snapshot_impl : public snapshot + { + public: + ~snapshot_impl() override { + if (_managed) { delete[] _file; } + }; + const unsigned char* get_data() const override; + size_t get_data_len() const override; + + snapshot_impl(const globals_impl&); + // write down all allocated strings + // replace pointer with idx + // reconsrtuct static strings index + // list_table _data & _entry_state + snapshot_impl(const unsigned char* data, size_t length, bool managed); + + private: + // file information + const unsigned char* _file; + size_t _length; + bool _managed; + static size_t file_size(size_t, size_t); + struct header { + size_t num_runners; + size_t length; + + } _header; + }; +} diff --git a/inkcpp/story_impl.h b/inkcpp/story_impl.h index bf6dea04..b832f3cc 100644 --- a/inkcpp/story_impl.h +++ b/inkcpp/story_impl.h @@ -40,7 +40,9 @@ namespace ink::runtime::internal // Creates a new global store for use with runners executing this story virtual globals new_globals() override; + virtual globals new_globals_from_snapshot(const snapshot&) override; virtual runner new_runner(globals store = nullptr) override; + virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) override; const ink::internal::header& get_header() const { return _header; } diff --git a/inkcpp/string_table.cpp b/inkcpp/string_table.cpp index aa8cda88..fd113f50 100644 --- a/inkcpp/string_table.cpp +++ b/inkcpp/string_table.cpp @@ -94,4 +94,14 @@ namespace ink::runtime::internal iter++; } } + + size_t string_table::snap(unsigned char* data, const snapper&) const + { + unsigned char* ptr = data; + for(auto itr = _table.begin(); itr != _table.end(); ++itr) { + ptr = snap_write(ptr, itr.key(), strlen(itr.key()) + 1, data); + } + ptr = snap_write(ptr, "\0", 1, data); + return ptr - data; + } } diff --git a/inkcpp/string_table.h b/inkcpp/string_table.h index e2eb6049..883b5958 100644 --- a/inkcpp/string_table.h +++ b/inkcpp/string_table.h @@ -2,11 +2,12 @@ #include "avl_array.h" #include "system.h" +#include "snapshot_impl.h" namespace ink::runtime::internal { // hash tree sorted by string pointers - class string_table + class string_table : public snapshot_interface { public: ~string_table(); @@ -21,6 +22,15 @@ namespace ink::runtime::internal // mark a string as used void mark_used(const char* string); + + // snapshot interface implementation + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + + // get position of string when iterate through data + // used to enable storing a string table references + void get_id(const char* string); + // deletes all unused strings void gc(); diff --git a/inkcpp/value.h b/inkcpp/value.h index 88382f5a..e90bcb63 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -8,6 +8,7 @@ #include "system.h" #include "../shared/private/command.h" #include "list_table.h" +#include "snapshot_impl.h" #include "tuple.hpp" #ifdef INK_ENABLE_STL @@ -82,8 +83,12 @@ namespace ink::runtime::internal { /** * @brief class to wrap stack value to common type. */ - class value { + class value : public snapshot_interface { public: + // snapshot interface + size_t snap(unsigned char* data, const snapper&) const; + const unsigned char* snap_load(const unsigned char* data, const loader&); + /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; From a90088cc296b197217ead832fb10c5d9e9837e63 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 8 Jul 2022 13:52:09 +0200 Subject: [PATCH 31/67] Add snap functions --- inkcpp/array.h | 22 ++++++++++++++++- inkcpp/collections/restorable.cpp | 36 ++++++++++++++++++++++++++++ inkcpp/collections/restorable.h | 16 ++++++++++++- inkcpp/include/traits.h | 13 ++++++++++ inkcpp/runner_impl.cpp | 40 +++++++++++++++++++++++++------ inkcpp/runner_impl.h | 4 ++++ inkcpp/simple_restorable_stack.h | 24 ++++++++++++++++++- inkcpp/snapshot_impl.cpp | 16 +++++++++---- inkcpp/snapshot_impl.h | 1 + inkcpp/stack.cpp | 9 +++++++ inkcpp/stack.h | 14 ++++++++++- inkcpp/string_table.h | 2 +- inkcpp/value.cpp | 23 ++++++++++++++++++ inkcpp/value.h | 13 ++++++++++ 14 files changed, 216 insertions(+), 17 deletions(-) diff --git a/inkcpp/array.h b/inkcpp/array.h index 8b6cfddb..0eeb7b61 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -1,6 +1,7 @@ #pragma once #include "system.h" +#include "traits.h" #include "snapshot_impl.h" namespace ink::runtime::internal @@ -63,6 +64,7 @@ namespace ink::runtime::internal size_t snap(unsigned char* data, const snapper&) const override { + inkAssert(!is_pointer{}(), "here is a special case oversight"); unsigned char* ptr = data; ptr = snap_write(ptr, &_size, data); for(const T& e : *this) { @@ -99,7 +101,7 @@ namespace ink::runtime::internal } template - class basic_restorable_array + class basic_restorable_array : public snapshot_interface { public: basic_restorable_array(T* array, size_t capacity, T nullValue) @@ -136,6 +138,10 @@ namespace ink::runtime::internal // Resets all values and clears any save points void clear(const T& value); + // snapshot interface + virtual size_t snap(unsigned char* data, const snapper&) const override; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + protected: inline T* buffer() { return _array; } void set_new_buffer(T* buffer, size_t capacity) { @@ -164,6 +170,20 @@ namespace ink::runtime::internal const T _null; }; + template + inline size_t basic_restorable_array::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _saved, data); + ptr = snap_write(ptr, _capacity, data); + ptr = snap_write(ptr, _null, data); + for(size_t i = 0; i < _capacity; ++i) { + ptr = snap_write(ptr, _array[i], data); + ptr = snap_write(ptr, _temp[i], data); + } + return ptr - data; + } + template inline void basic_restorable_array::set(size_t index, const T& value) { diff --git a/inkcpp/collections/restorable.cpp b/inkcpp/collections/restorable.cpp index e69de29b..34b96ed0 100644 --- a/inkcpp/collections/restorable.cpp +++ b/inkcpp/collections/restorable.cpp @@ -0,0 +1,36 @@ +#include "restorable.h" +#include "../stack.h" + +namespace ink::runtime::internal { + template<> + size_t restorable::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _pos, data); + ptr = snap_write(ptr, _jump, data); + ptr = snap_write(ptr, _save, data); + size_t max = _pos; + if (_jump > max) { max = _jump; } + if (_save > max) { max = _save; } + for(size_t i = 0; i < max; ++i) { + ptr = snap_write(ptr, _buffer[i].name, data); + ptr += _buffer[i].data.snap(data ? ptr : nullptr, snapper); + } + return ptr - data; + } + template<> + size_t restorable::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _pos, data); + ptr = snap_write(ptr, _jump, data); + ptr = snap_write(ptr, _save, data); + size_t max = _pos; + if (_jump > max) { max = _jump; } + if (_save > max) { max = _save; } + for(size_t i = 0; i < max; ++i) { + ptr += _buffer[i].snap(data ? ptr : nullptr, snapper); + } + return ptr - data; + } +} diff --git a/inkcpp/collections/restorable.h b/inkcpp/collections/restorable.h index ad64175e..28fa05ae 100644 --- a/inkcpp/collections/restorable.h +++ b/inkcpp/collections/restorable.h @@ -1,7 +1,13 @@ +#pragma once + +#include "../snapshot_impl.h" + #include +#include namespace ink::runtime::internal { + struct entry; template constexpr auto EmptyNullPredicate = [](const ElementType&) { return false; }; @@ -68,7 +74,7 @@ namespace ink::runtime::internal * from this class gain this functionality. */ template - class restorable + class restorable : public snapshot_interface { public: restorable(ElementType* buffer, size_t size) @@ -326,6 +332,10 @@ namespace ink::runtime::internal return count; } + // snapshot interface + virtual size_t snap(unsigned char* data, const snapper&) const override; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + protected: // Called when we run out of space in buffer. virtual void overflow(ElementType*& buffer, size_t& size) { @@ -374,4 +384,8 @@ namespace ink::runtime::internal size_t _jump; size_t _save; }; + template<> + size_t restorable::snap(unsigned char* data, const snapper& snapper) const; + template<> + size_t restorable::snap(unsigned char* data, const snapper& snapper) const; } diff --git a/inkcpp/include/traits.h b/inkcpp/include/traits.h index 86ade898..d7733f27 100644 --- a/inkcpp/include/traits.h +++ b/inkcpp/include/traits.h @@ -9,6 +9,13 @@ namespace ink::runtime::internal { + template + constexpr size_t sizeof_largest_type() + { + size_t ret = 0; + return ( (ret = sizeof(Ts) > ret ? sizeof(Ts) : ret), ... ); + } + template struct get_ith_type : get_ith_type {}; @@ -37,6 +44,12 @@ namespace ink::runtime::internal template struct is_same : true_type {}; + template + struct is_pointer : false_type {}; + + template + struct is_pointer : true_type {}; + // == string testing (from me) == template diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 499ae741..bcde17fb 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -544,13 +544,39 @@ namespace ink::runtime::internal ptr = snap_write(ptr, _saved, data); ptr = snap_write(ptr, _is_falling, data); ptr += _output.snap(data ? ptr : nullptr, snapper); - // _output - // _stack - // _ref_stack - // _threads - // _tags - // _choices - // _container + ptr += _stack.snap(data ? ptr : nullptr, snapper); + ptr += _ref_stack.snap(data ? ptr : nullptr, snapper); + ptr += _eval.snap(data ? ptr : nullptr, snapper); + ptr = snap_write(ptr, _tags.size(), data); + for (const auto& tag : _tags) { + ptr = snap_write(ptr, tag - snapper.story_string_table, data); + } + ptr += _container.snap(data ? ptr : nullptr, snapper); + ptr += _threads.snap(data ? ptr : nullptr, snapper); + ptr = snap_write(ptr, _backup_choice_len, data); + ptr = snap_write(ptr, _fallback_choice.has_value(), data); + auto snap_choice = [&snapper, write = static_cast(data)](unsigned char* data, const choice& c) -> size_t { + unsigned char* ptr = data; + ptr = snap_write(ptr, c._index, write); + ptr = snap_write(ptr, c._path, write); + ptr = snap_write(ptr, c._thread, write); + ptr = snap_write(ptr, snapper.strings.get_id(c._text), write); + return ptr - data; + }; + if (_fallback_choice) { + ptr += snap_choice(ptr, _fallback_choice.value()); + } + for (const choice& c : _choices) { + ptr += snap_choice(ptr, c); + } + return ptr - data; + } + + size_t runner_impl::threads::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr += base::snap(data ? ptr : nullptr, snapper); + ptr += _threadDone.snap(data ? ptr : nullptr, snapper); return ptr - data; } diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 5f8eb15f..a1e64565 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -200,6 +200,10 @@ namespace ink::runtime::internal } const ip_t& operator[](size_t index) const { return get(index); } + // snapshot interface + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + protected: virtual void overflow(thread_t*& buffer, size_t& size) override final; private: diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index bf09d633..b7ccd7bf 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -2,11 +2,12 @@ #include "system.h" #include "array.h" +#include "snapshot_impl.h" namespace ink::runtime::internal { template - class simple_restorable_stack + class simple_restorable_stack : public snapshot_interface { public: simple_restorable_stack(T* buffer, size_t size, const T& null) @@ -27,6 +28,9 @@ namespace ink::runtime::internal void restore(); void forget(); + virtual size_t snap(unsigned char* data, const snapper&) const override; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + protected: virtual void overflow(T*& buffer, size_t& size) { throw ink_exception("Stack overflow!"); @@ -222,4 +226,22 @@ namespace ink::runtime::internal // Just reset save position _save = InvalidIndex; } + template + size_t simple_restorable_stack::snap(unsigned char* data, const snapper&) const + { + static_assert(is_same{}() || is_same{}()); + unsigned char* ptr = data; + ptr = snap_write(ptr, _null, data); + ptr = snap_write(ptr, _pos, data); + ptr = snap_write(ptr, _save, data); + ptr = snap_write(ptr, _jump, data); + size_t max = _pos; + if (_save > max) { max = _save; } + if (_jump > max) { max = _jump; } + for(size_t i = 0; i < max; ++i) + { + ptr = snap_write(ptr, _buffer[i], data); + } + return ptr - data; + } } diff --git a/inkcpp/snapshot_impl.cpp b/inkcpp/snapshot_impl.cpp index ae693367..a5104af2 100644 --- a/inkcpp/snapshot_impl.cpp +++ b/inkcpp/snapshot_impl.cpp @@ -1,4 +1,6 @@ #include "snapshot_impl.h" + +#include "story_impl.h" #include "globals_impl.h" #include "runner_impl.h" @@ -48,11 +50,15 @@ namespace ink::runtime::internal snapshot_impl::snapshot_impl(const globals_impl& globals) : _managed{true} { - _length = globals.snap(); + snapshot_interface::snapper snapper{ + .strings = globals.strings(), + .story_string_table = globals._owner->string(0) + }; + _length = globals.snap(nullptr, snapper); size_t runner_cnt = 0; for(auto node = globals._runners_start; node; node = node->next) { - _length += node->object->snap(); + _length += node->object->snap(nullptr, snapper); ++runner_cnt; } @@ -68,16 +74,16 @@ namespace ink::runtime::internal size_t offset = 0; for(auto node = globals._runners_start; node; node = node->next) { - offset += node->object->snap(); + offset += node->object->snap(nullptr, snapper); memcpy(ptr, &offset, sizeof(offset)); ptr += sizeof(offset); } } - ptr += globals.snap(ptr); + ptr += globals.snap(ptr, snapper); for (auto node = globals._runners_start; node; node = node->next) { - ptr += node->object->snap(ptr); + ptr += node->object->snap(ptr, snapper); } } } diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index e65844ef..42debbcd 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -24,6 +24,7 @@ namespace ink::runtime::internal public: struct snapper { const string_table& strings; + const char* story_string_table; }; struct loader { string_table& strings; diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index 40c73127..79dacf6c 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -587,4 +587,13 @@ namespace ink::runtime::internal stack.set(itr.get()->name, itr.get()->data); } } + + size_t basic_stack::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _next_thread, data); + ptr = snap_write(ptr, _backup_next_thread, data); + ptr += base::snap(data ? ptr : nullptr, snapper); + return ptr - data; + } } diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 147e4c54..7c3c7b35 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -3,6 +3,7 @@ #include "value.h" #include "collections/restorable.h" #include "array.h" +#include "snapshot_impl.h" namespace ink { @@ -24,7 +25,7 @@ namespace ink thread }; - class basic_stack : protected restorable + class basic_stack : protected restorable { protected: basic_stack(entry* data, size_t size); @@ -79,6 +80,10 @@ namespace ink // push all values to other _stack void push_values(basic_stack& _stack); + // snapshot interface + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override; + private: entry& add(hash_t name, const value& val); const entry* pop(); @@ -135,6 +140,7 @@ namespace ink using base = restorable; public: + // Push value onto the stack void push(const value&); @@ -160,6 +166,12 @@ namespace ink void save(); void restore(); void forget(); + + // snapshot interface + size_t snap(unsigned char* data, const snapper& snapper) const override + { return base::snap(data, snapper); } + const unsigned char* snap_load(const unsigned char* data, const loader& loader) override + { return base::snap_load(data, loader); } }; template diff --git a/inkcpp/string_table.h b/inkcpp/string_table.h index 883b5958..4fd0cbd2 100644 --- a/inkcpp/string_table.h +++ b/inkcpp/string_table.h @@ -29,7 +29,7 @@ namespace ink::runtime::internal // get position of string when iterate through data // used to enable storing a string table references - void get_id(const char* string); + size_t get_id(const char* string) const; // deletes all unused strings void gc(); diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index b0ff301d..e1f6b756 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -2,6 +2,7 @@ #include "output.h" #include "list_table.h" #include "string_utils.h" +#include "string_table.h" namespace ink::runtime::internal { @@ -69,4 +70,26 @@ namespace ink::runtime::internal os.append(val); return os; } + + size_t value::snap(unsigned char* data, const snapper& snapper) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _type, data); + if (_type == value_type::string) { + unsigned char buf[max_value_size]; + string_type* res = reinterpret_cast(buf); + auto str = get(); + res->allocated = str.allocated; + if (str.allocated) { + res->str = reinterpret_cast(snapper.strings.get_id(str.str)); + } else { + res->str = reinterpret_cast(str.str - snapper.story_string_table); + } + ptr = snap_write(ptr, buf, data); + } else { + // TODO more space efficent? + ptr = snap_write(ptr, &bool_value, max_value_size, data); + } + return ptr - data; + } } diff --git a/inkcpp/value.h b/inkcpp/value.h index e90bcb63..fbb9c045 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -165,6 +165,19 @@ namespace ink::runtime::internal { char ci; } pointer; }; + static constexpr size_t max_value_size = + sizeof_largest_type< + decltype(bool_value), + decltype(int32_value), + decltype(string_value), + decltype(uint32_value), + decltype(float_value), + decltype(jump), + decltype(list_value), + decltype(list_flag_value), + decltype(frame_value), + decltype(pointer) + >(); value_type _type; }; From d7d23a1d21d14f33cf5bff024953db7fdeb79f37 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 11 Jul 2022 15:28:47 +0200 Subject: [PATCH 32/67] Can load globals, start loading runner --- inkcpp/CMakeLists.txt | 1 + inkcpp/array.h | 25 +++++++++---- inkcpp/avl_array.h | 24 +++++++++---- inkcpp/collections/restorable.cpp | 15 ++++++++ inkcpp/collections/restorable.h | 4 ++- inkcpp/globals_impl.cpp | 13 ++++++- inkcpp/globals_impl.h | 2 +- inkcpp/include/snapshot.h | 10 +++--- inkcpp/include/story.h | 2 +- inkcpp/list_table.cpp | 7 ++++ inkcpp/output.h | 2 +- inkcpp/runner_impl.cpp | 56 +++++++++++++++++++++++++++++- inkcpp/runner_impl.h | 2 +- inkcpp/simple_restorable_stack.h | 4 +-- inkcpp/snapshot_impl.cpp | 29 ++++++++++++++-- inkcpp/snapshot_impl.h | 50 ++++++++++++-------------- inkcpp/snapshot_interface.h | 47 +++++++++++++++++++++++++ inkcpp/stack.h | 2 +- inkcpp/story_impl.cpp | 15 ++++++++ inkcpp/story_impl.h | 5 ++- inkcpp/string_table.cpp | 22 ++++++++++++ inkcpp/value.h | 4 +-- inkcpp_cl/inkcpp_cl.cpp | 26 ++++++++++---- inkcpp_compiler/binary_emitter.cpp | 6 ++-- 24 files changed, 305 insertions(+), 68 deletions(-) create mode 100644 inkcpp/snapshot_interface.h diff --git a/inkcpp/CMakeLists.txt b/inkcpp/CMakeLists.txt index 876b6b18..45e41cec 100644 --- a/inkcpp/CMakeLists.txt +++ b/inkcpp/CMakeLists.txt @@ -13,6 +13,7 @@ list(APPEND SOURCES runner_impl.h runner_impl.cpp simple_restorable_stack.h stack.h stack.cpp story_impl.h story_impl.cpp + snapshot_impl.h snapshot_impl.cpp snapshot_interface.h story_ptr.cpp system.cpp value.h value.cpp diff --git a/inkcpp/array.h b/inkcpp/array.h index 0eeb7b61..ca81910d 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -2,7 +2,7 @@ #include "system.h" #include "traits.h" -#include "snapshot_impl.h" +#include "snapshot_interface.h" namespace ink::runtime::internal { @@ -66,13 +66,26 @@ namespace ink::runtime::internal { inkAssert(!is_pointer{}(), "here is a special case oversight"); unsigned char* ptr = data; - ptr = snap_write(ptr, &_size, data); + ptr = snap_write(ptr, _size, data); for(const T& e : *this) { ptr = snap_write(ptr, e, data); } return ptr - data; } - const unsigned char* snap_load(const unsigned char* data, const loader&) override; + const unsigned char* snap_load(const unsigned char* ptr, const loader&) override + { + decltype(_size) size; + ptr = snap_read(ptr, size); + if constexpr (dynamic) { + resize(size); + } else { + inkAssert(size <= initialCapacity, "capacity of non dynamic array is to small vor snapshot!"); + } + for (T& e : *this) { + ptr = snap_read(ptr, e); + } + return ptr; + } private: if_t _static_data[dynamic ? 1 : initialCapacity]; @@ -86,8 +99,8 @@ namespace ink::runtime::internal { static_assert(dynamic, "Can only extend if array is dynamic!"); size_t new_capacity = capacity > _capacity - ? 1.5f * _capacity - : capacity; + ? capacity + : 1.5f * _capacity; if (new_capacity < 5) { new_capacity = 5; } T* new_data = new T[new_capacity]; @@ -140,7 +153,7 @@ namespace ink::runtime::internal // snapshot interface virtual size_t snap(unsigned char* data, const snapper&) const override; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } protected: inline T* buffer() { return _array; } diff --git a/inkcpp/avl_array.h b/inkcpp/avl_array.h index ed925706..e9c4769d 100644 --- a/inkcpp/avl_array.h +++ b/inkcpp/avl_array.h @@ -87,12 +87,19 @@ class avl_array friend avl_array; // avl_array may access index pointer public: + // ctor tag_avl_array_iterator(if_t instance = nullptr, size_type idx = 0U) : instance_(instance) , idx_(idx) { } + template> + tag_avl_array_iterator(const tag_avl_array_iterator& itr) + : instance_(itr.instance_) + , idx_(itr.idx_) + {} + inline tag_avl_array_iterator& operator=(const tag_avl_array_iterator& other) { instance_ = other.instance_; @@ -118,6 +125,11 @@ class avl_array inline const Key& key() const { return instance_->key_[idx_]; } + // returns unique number for each entry + // the numbers are unique as long no operation are executed + // on the avl + inline size_t temp_identifier() const { return idx_; } + // preincrement tag_avl_array_iterator& operator++() { @@ -193,12 +205,7 @@ class avl_array inline const_iterator begin() const { - size_type i = INVALID_IDX; - if (root_ != INVALID_IDX) { - // find smallest element, it's the farthest node left from root - for (i = root_; child_[i].left != INVALID_IDX; i = child_[i].left); - } - return const_iterator(this, i); + return const_iterator(const_cast(*this).begin()); } inline iterator end() @@ -341,6 +348,11 @@ class avl_array return end(); } + inline const_iterator find(const key_type& key) const + { + return const_iterator(const_cast(*this).find(key)); + } + /** * Count elements with a specific key diff --git a/inkcpp/collections/restorable.cpp b/inkcpp/collections/restorable.cpp index 34b96ed0..961d21b5 100644 --- a/inkcpp/collections/restorable.cpp +++ b/inkcpp/collections/restorable.cpp @@ -33,4 +33,19 @@ namespace ink::runtime::internal { } return ptr - data; } + template<> + size_t restorable::snap(unsigned char* data, const snapper&) const + { + unsigned char* ptr = data; + ptr = snap_write(ptr, _pos, data); + ptr = snap_write(ptr, _jump, data); + ptr = snap_write(ptr, _save, data); + size_t max = _pos; + if (_jump > max) { max = _jump; } + if (_save > max) { max = _save; } + for(size_t i = 0; i < max; ++i) { + ptr = snap_write(ptr, _buffer[i], data); + } + return ptr - data; + } } diff --git a/inkcpp/collections/restorable.h b/inkcpp/collections/restorable.h index 28fa05ae..0ddb8a77 100644 --- a/inkcpp/collections/restorable.h +++ b/inkcpp/collections/restorable.h @@ -334,7 +334,7 @@ namespace ink::runtime::internal // snapshot interface virtual size_t snap(unsigned char* data, const snapper&) const override; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } protected: // Called when we run out of space in buffer. @@ -388,4 +388,6 @@ namespace ink::runtime::internal size_t restorable::snap(unsigned char* data, const snapper& snapper) const; template<> size_t restorable::snap(unsigned char* data, const snapper& snapper) const; + template<> + size_t restorable::snap(unsigned char* data, const snapper&) const; } diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 90e5cb90..c1544b24 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -184,6 +184,7 @@ namespace ink::runtime::internal // execute one line to startup the globals run->getline_silent(); + _globals_initialized = true; } void globals_impl::gc() @@ -228,7 +229,7 @@ namespace ink::runtime::internal size_t globals_impl::snap(unsigned char* data, const snapper& snapper) const { - unsigned char* ptr; + unsigned char* ptr = data; inkAssert(_num_containers == _visit_counts.size(), "Should be equal!"); inkAssert(_globals_initialized, "Only support snapshot of globals with runner! or you don't need a snapshot for this state"); ptr += _visit_counts.snap( data ? ptr : nullptr, snapper ); @@ -236,4 +237,14 @@ namespace ink::runtime::internal ptr += _lists.snap( data ? ptr : nullptr, snapper ); return ptr - data; } + + const unsigned char* globals_impl::snap_load(const unsigned char* ptr, const loader& loader) + { + _globals_initialized = true; + ptr = _visit_counts.snap_load(ptr, loader); + inkAssert(_num_containers == _visit_counts.size(), "errer when loading visit counts, story file dont match snapshot!"); + ptr = _strings.snap_load(ptr, loader); + ptr = _lists.snap_load(ptr, loader); + return ptr; + } } diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index 70ab71ae..ea51a3d7 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -18,9 +18,9 @@ namespace ink::runtime::internal class globals_impl : public globals_interface, public snapshot_interface { friend snapshot_impl; + public: size_t snap(unsigned char* data, const snapper&) const override; const unsigned char* snap_load(const unsigned char* data, const loader&) override; - public: // Initializes a new global store from the given story globals_impl(const story_impl*); virtual ~globals_impl() { } diff --git a/inkcpp/include/snapshot.h b/inkcpp/include/snapshot.h index 8d89c687..ad4de434 100644 --- a/inkcpp/include/snapshot.h +++ b/inkcpp/include/snapshot.h @@ -6,14 +6,16 @@ namespace ink::runtime { class snapshot { public: - virtual ~snapshot() = 0; + virtual ~snapshot() {}; static snapshot* from_binary(const unsigned char* data, size_t length, bool freeOnDestroy = true); -#ifdef INK_ENABLE_STL - static snapshot* from_file(const char* filename); -#endif virtual const unsigned char* get_data() const = 0; virtual size_t get_data_len() const = 0; + +#ifdef INK_ENABLE_STL + static snapshot* from_file(const char* filename); + void write_to_file(const char* filename) const; +#endif }; } diff --git a/inkcpp/include/story.h b/inkcpp/include/story.h index 9f7e4935..c62cbbdd 100644 --- a/inkcpp/include/story.h +++ b/inkcpp/include/story.h @@ -19,7 +19,7 @@ namespace ink::runtime class story { public: - virtual ~story() = 0; + virtual ~story(){}; #pragma region Interface Methods /** * Creates a new global store diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index cf8a9cb5..21cc5093 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -658,5 +658,12 @@ namespace ink::runtime::internal return ptr - data; } + const unsigned char* list_table::snap_load(const unsigned char* ptr, const loader& loader) + { + ptr = _data.snap_load(ptr, loader); + ptr = _entry_state.snap_load(ptr, loader); + return ptr; + } + } diff --git a/inkcpp/output.h b/inkcpp/output.h index 1185ee20..e4a07eaa 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -102,7 +102,7 @@ namespace ink // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } private: size_t find_start() const; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index bcde17fb..7a509603 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -549,7 +549,8 @@ namespace ink::runtime::internal ptr += _eval.snap(data ? ptr : nullptr, snapper); ptr = snap_write(ptr, _tags.size(), data); for (const auto& tag : _tags) { - ptr = snap_write(ptr, tag - snapper.story_string_table, data); + std::uintptr_t offset = tag - snapper.story_string_table; + ptr = snap_write(ptr, offset, data); } ptr += _container.snap(data ? ptr : nullptr, snapper); ptr += _threads.snap(data ? ptr : nullptr, snapper); @@ -566,12 +567,65 @@ namespace ink::runtime::internal if (_fallback_choice) { ptr += snap_choice(ptr, _fallback_choice.value()); } + ptr = snap_write(ptr, _choices.size(), data); for (const choice& c : _choices) { ptr += snap_choice(ptr, c); } return ptr - data; } + const unsigned char* runner_impl::snap_load(const unsigned char* data, const loader& loader) + { + auto ptr = data; + ptr = snap_read(ptr, _ptr); + ptr = snap_read(ptr, _backup); + ptr = snap_read(ptr, _done); + int32_t seed; + ptr = snap_read(ptr, seed); + _rng.srand(seed); + ptr = snap_read(ptr, _evaluation_mode); + ptr = snap_read(ptr, _saved_evaluation_mode); + ptr = snap_read(ptr, _eval); + ptr = snap_read(ptr, _saved); + ptr = snap_read(ptr, _is_falling); + ptr = _output.snap_load(ptr, loader); + ptr = _stack.snap_load(ptr, loader); + ptr = _ref_stack.snap_load(ptr, loader); + ptr = _eval.snap_load(ptr, loader); + size_t num_tags; + ptr = snap_read(ptr, num_tags); + for(size_t i = 0; i < num_tags; ++i) { + std::uintptr_t offset; + ptr = snap_read(ptr, offset); + _tags.push() = offset + loader.story_string_table; + } + ptr = _container.snap_load(ptr, loader); + ptr = _threads.snap_load(ptr, loader); + ptr = snap_read(ptr, _backup_choice_len); + auto read_choice = [&ptr,&loader]() -> choice{ + choice c; + ptr = snap_read(ptr, c._index); + ptr = snap_read(ptr, c._path); + ptr = snap_read(ptr, c._thread); + size_t string_id; + ptr = snap_read(ptr, string_id); + c._text = loader.string_table[string_id]; + return c; + }; + bool has_fallback_choice; + ptr = snap_read(ptr, has_fallback_choice); + _fallback_choice = has_fallback_choice + ? optional{read_choice()} + : nullopt; + size_t num_choices; + ptr = snap_read(ptr, num_choices); + for (size_t i = 0; i < num_choices; ++i) { + _choices.push() = read_choice(); + } + + return ptr; + } + size_t runner_impl::threads::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index a1e64565..5de6f2c2 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -202,7 +202,7 @@ namespace ink::runtime::internal // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } protected: virtual void overflow(thread_t*& buffer, size_t& size) override final; diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index b7ccd7bf..1616e2a6 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -29,7 +29,7 @@ namespace ink::runtime::internal void forget(); virtual size_t snap(unsigned char* data, const snapper&) const override; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } protected: virtual void overflow(T*& buffer, size_t& size) { @@ -229,7 +229,7 @@ namespace ink::runtime::internal template size_t simple_restorable_stack::snap(unsigned char* data, const snapper&) const { - static_assert(is_same{}() || is_same{}()); + static_assert(is_same{}() || is_same{}() || is_same{}()); unsigned char* ptr = data; ptr = snap_write(ptr, _null, data); ptr = snap_write(ptr, _pos, data); diff --git a/inkcpp/snapshot_impl.cpp b/inkcpp/snapshot_impl.cpp index a5104af2..28c6c179 100644 --- a/inkcpp/snapshot_impl.cpp +++ b/inkcpp/snapshot_impl.cpp @@ -31,13 +31,23 @@ namespace ink::runtime return from_binary(data, length); } + + void snapshot::write_to_file(const char* filename) const + { + std::ofstream ofs(filename, std::ios::binary); + if(!ofs.is_open()) { + throw ink_exception("Failed to open file to write snapshot: " + + std::string(filename)); + } + ofs.write(reinterpret_cast(get_data()), get_data_len()); + } #endif } namespace ink::runtime::internal { size_t snapshot_impl::file_size(size_t serialization_length, size_t runner_cnt) { - return serialization_length + sizeof(header) + runner_cnt * sizeof(size_t); + return serialization_length + sizeof(header) + (runner_cnt + 1) * sizeof(size_t); } const unsigned char* snapshot_impl::get_data() const { @@ -63,6 +73,8 @@ namespace ink::runtime::internal } _length = file_size(_length, runner_cnt); + _header.length = _length; + _header.num_runners = runner_cnt; unsigned char* data = new unsigned char[_length]; _file = data; unsigned char* ptr = data; @@ -71,12 +83,15 @@ namespace ink::runtime::internal // write lookup table ptr += sizeof(header); { - size_t offset = 0; + size_t offset = (ptr - data) + (_header.num_runners + 1) * sizeof(size_t); + memcpy(ptr, &offset, sizeof(offset)); + ptr += sizeof(offset); + offset += globals.snap(nullptr, snapper); for(auto node = globals._runners_start; node; node = node->next) { - offset += node->object->snap(nullptr, snapper); memcpy(ptr, &offset, sizeof(offset)); ptr += sizeof(offset); + offset += node->object->snap(nullptr, snapper); } } @@ -86,4 +101,12 @@ namespace ink::runtime::internal ptr += node->object->snap(ptr, snapper); } } + + snapshot_impl::snapshot_impl(const unsigned char* data, size_t length, bool managed) + : _file{data}, _length{length}, _managed{managed} + { + const unsigned char* ptr = data; + memcpy(&_header, ptr, sizeof(_header)); + inkAssert(_header.length == _length, "Corrupted file length"); + } } diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index 42debbcd..e90cb480 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -1,43 +1,23 @@ #pragma once #include "snapshot.h" +#include "snapshot_interface.h" +#include "array.h" -#include namespace ink::runtime::internal { - class globals_impl; - class value; - class string_table; - class snapshot_interface { - protected: - static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) - { - memcpy(ptr, data, length); - return ptr += length; - } - template - static unsigned char* snap_write(unsigned char* ptr, const T& data, bool write) - { - return snap_write(ptr, &data, sizeof(data), write); - } - public: - struct snapper { - const string_table& strings; - const char* story_string_table; - }; - struct loader { - string_table& strings; - }; - virtual size_t snap(unsigned char* data, const snapper&) const = 0; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) = 0; - }; + class snapshot_impl : public snapshot { public: ~snapshot_impl() override { if (_managed) { delete[] _file; } }; + + managed_array& strings() const { + return string_table; + } const unsigned char* get_data() const override; size_t get_data_len() const override; @@ -48,8 +28,18 @@ namespace ink::runtime::internal // list_table _data & _entry_state snapshot_impl(const unsigned char* data, size_t length, bool managed); + const unsigned char* get_globals_snap() const { + return _file + get_offset(0); + } + + const unsigned char* get_runner_snap(size_t idx) const { + return _file + get_offset(idx + 1); + } + private: // file information + // only populated when loading snapshots + mutable managed_array string_table; const unsigned char* _file; size_t _length; bool _managed; @@ -59,5 +49,11 @@ namespace ink::runtime::internal size_t length; } _header; + + size_t get_offset(size_t idx) const { + inkAssert(idx <= _header.num_runners); + return reinterpret_cast(_file + sizeof(header))[idx]; + } + }; } diff --git a/inkcpp/snapshot_interface.h b/inkcpp/snapshot_interface.h new file mode 100644 index 00000000..7609991a --- /dev/null +++ b/inkcpp/snapshot_interface.h @@ -0,0 +1,47 @@ +#pragma once + +#include "snapshot.h" +#include + +namespace ink::runtime::internal +{ + class globals_impl; + class value; + class string_table; + template + class managed_array; + class snapshot_interface { + protected: + static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) + { + if (write) { memcpy(ptr, data, length); } + return ptr + length; + } + template + static unsigned char* snap_write(unsigned char* ptr, const T& data, bool write) + { + return snap_write(ptr, &data, sizeof(data), write); + } + static const unsigned char* snap_read(const unsigned char* ptr, void* data, size_t length) + { + memcpy(data, ptr, length); + return ptr + length; + } + template + static const unsigned char* snap_read(const unsigned char* ptr, T& data) + { + return snap_read(ptr, &data, sizeof(data)); + } + public: + struct snapper { + const string_table& strings; + const char* story_string_table; + }; + struct loader { + managed_array& string_table; + const char* story_string_table; + }; + virtual size_t snap(unsigned char* data, const snapper&) const = 0; + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) = 0; + }; +} diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 7c3c7b35..8ef1764d 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -82,7 +82,7 @@ namespace ink // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } private: entry& add(hash_t name, const value& val); diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 8f96c54b..0cc1a444 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -2,6 +2,8 @@ #include "platform.h" #include "runner_impl.h" #include "globals_impl.h" +#include "snapshot.h" +#include "snapshot_impl.h" #include "version.h" #ifdef INK_ENABLE_STL @@ -175,6 +177,19 @@ namespace ink::runtime::internal return globals(new globals_impl(this), _block); } + globals story_impl::new_globals_from_snapshot(const snapshot& data) + { + const snapshot_impl& snapshot = reinterpret_cast(data); + auto* globs = new globals_impl(this); + auto end = globs->snap_load(snapshot.get_globals_snap(), snapshot_interface::loader{ + .string_table = snapshot.strings(), + .story_string_table = _string_table, + + }); + inkAssert(end == snapshot.get_runner_snap(0), "not all data were used for global reconstruction"); + return globals(globs, _block); + } + runner story_impl::new_runner(globals store) { if (store == nullptr) diff --git a/inkcpp/story_impl.h b/inkcpp/story_impl.h index b832f3cc..ef73264f 100644 --- a/inkcpp/story_impl.h +++ b/inkcpp/story_impl.h @@ -42,7 +42,10 @@ namespace ink::runtime::internal virtual globals new_globals() override; virtual globals new_globals_from_snapshot(const snapshot&) override; virtual runner new_runner(globals store = nullptr) override; - virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) override; + virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) override { + inkAssert(false, "not implmented yet!"); + return nullptr; + } const ink::internal::header& get_header() const { return _header; } diff --git a/inkcpp/string_table.cpp b/inkcpp/string_table.cpp index fd113f50..3647c68d 100644 --- a/inkcpp/string_table.cpp +++ b/inkcpp/string_table.cpp @@ -104,4 +104,26 @@ namespace ink::runtime::internal ptr = snap_write(ptr, "\0", 1, data); return ptr - data; } + + const unsigned char* string_table::snap_load(const unsigned char* data, const loader& loader) + { + auto* ptr = data; + while(*ptr) { + size_t len = 0; + for(;ptr[len];++len); + ++len; + auto str = create(len); + loader.string_table.push() = str; + ptr = snap_read(ptr, str, len); + mark_used(str); + } + return ptr + 1; + } + + size_t string_table::get_id(const char* string) const + { + auto iter = _table.find(string); + inkAssert(iter != _table.end(), "Try to fetch not contained string!"); + return iter.temp_identifier(); + } } diff --git a/inkcpp/value.h b/inkcpp/value.h index fbb9c045..c450b903 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -86,8 +86,8 @@ namespace ink::runtime::internal { class value : public snapshot_interface { public: // snapshot interface - size_t snap(unsigned char* data, const snapper&) const; - const unsigned char* snap_load(const unsigned char* data, const loader&); + size_t snap(unsigned char* data, const snapper&) const override; + const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 27259dce..92047431 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "test.h" @@ -19,7 +20,7 @@ void usage() cout << "Usage: inkcpp_cl \n" << "\t-o :\tOutput file name\n" - << "\t-p:\tPlay mode\n" + << "\t-p []:\tPlay mode\n\toptional snapshot file to load\n" << endl; } @@ -35,6 +36,7 @@ int main(int argc, const char** argv) // Parse options std::string outputFilename; bool playMode = false, testMode = false, testDirectory = false; + std::string snapshotFile; for (int i = 1; i < argc - 1; i++) { std::string option = argv[i]; @@ -43,8 +45,13 @@ int main(int argc, const char** argv) outputFilename = argv[i + 1]; i += 1; } - else if (option == "-p") + else if (option == "-p") { playMode = true; + if (i + 1 < argc - 1 && argv[i+1][0] != '-') { + ++i; + snapshotFile = argv[i]; + } + } else if (option == "-t") testMode = true; else if (option == "-td") @@ -139,7 +146,11 @@ int main(int argc, const char** argv) story* myInk = story::from_file(outputFilename.c_str()); // Start runner - runner thread = myInk->new_runner(); + runner thread; + if (snapshotFile.size()) { + globals glob = myInk->new_globals_from_snapshot(*snapshot::from_file(snapshotFile.c_str())); + } + thread = myInk->new_runner(); while (true) { @@ -167,18 +178,21 @@ int main(int argc, const char** argv) int c = 0; std::cin >> c; + if (c == -1) { + snapshot* snap = thread->create_snapshot(); + // snap->write_to_file("test.snap"); + snap->write_to_file("test.snap"); + break; + } thread->choose(c - 1); std::cout << "?> "; continue; - continue; } // out of content break; } - delete myInk; - return 0; } catch (const std::exception& e) diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index dd163e63..defe4383 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -67,9 +67,9 @@ namespace ink::compiler::internal // Clear lists children.clear(); - named_children.clear(); - indexed_children.clear(); - noop_offsets.clear(); + //named_children.clear(); + //indexed_children.clear(); + //noop_offsets.clear(); parent = nullptr; } }; From 460a81b20e4fddbf8c0d81fbb1e0567f27d98a6f Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 11 Jul 2022 23:51:30 +0200 Subject: [PATCH 33/67] can store and load --- inkcpp/array.h | 18 ++++++- inkcpp/avl_array.h | 2 +- inkcpp/collections/restorable.cpp | 81 ++++++++++++++++++++++++------- inkcpp/collections/restorable.h | 9 +++- inkcpp/globals_impl.cpp | 2 + inkcpp/output.cpp | 14 +++++- inkcpp/output.h | 2 +- inkcpp/runner_impl.cpp | 8 ++- inkcpp/runner_impl.h | 2 +- inkcpp/simple_restorable_stack.h | 23 ++++++++- inkcpp/snapshot_impl.h | 2 + inkcpp/snapshot_interface.h | 4 +- inkcpp/stack.cpp | 8 +++ inkcpp/stack.h | 2 +- inkcpp/story_impl.cpp | 18 +++++++ inkcpp/story_impl.h | 6 +-- inkcpp/string_table.cpp | 10 +++- inkcpp/value.cpp | 13 +++++ inkcpp/value.h | 2 +- inkcpp_cl/inkcpp_cl.cpp | 5 +- 20 files changed, 191 insertions(+), 40 deletions(-) diff --git a/inkcpp/array.h b/inkcpp/array.h index ca81910d..c72ca2e6 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -153,7 +153,7 @@ namespace ink::runtime::internal // snapshot interface virtual size_t snap(unsigned char* data, const snapper&) const override; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; protected: inline T* buffer() { return _array; } @@ -197,6 +197,22 @@ namespace ink::runtime::internal return ptr - data; } + template + inline const unsigned char* basic_restorable_array::snap_load(const unsigned char* data, const loader& loader) + { + auto ptr = data; + ptr = snap_read(ptr, _saved); + ptr = snap_read(ptr, _capacity); + T null; + ptr = snap_read(ptr, null); + inkAssert(null == _null, "null value is different to snapshot!"); + for(size_t i = 0; i < _capacity; ++i) { + ptr = snap_read(ptr, _array[i]); + ptr = snap_read(ptr, _temp[i]); + } + return ptr; + } + template inline void basic_restorable_array::set(size_t index, const T& value) { diff --git a/inkcpp/avl_array.h b/inkcpp/avl_array.h index e9c4769d..54db8b9e 100644 --- a/inkcpp/avl_array.h +++ b/inkcpp/avl_array.h @@ -128,7 +128,7 @@ class avl_array // returns unique number for each entry // the numbers are unique as long no operation are executed // on the avl - inline size_t temp_identifier() const { return idx_; } + inline size_t temp_identifier() const { return instance_->size() - idx_ - 1; } // preincrement tag_avl_array_iterator& operator++() diff --git a/inkcpp/collections/restorable.cpp b/inkcpp/collections/restorable.cpp index 961d21b5..e9b4b43d 100644 --- a/inkcpp/collections/restorable.cpp +++ b/inkcpp/collections/restorable.cpp @@ -2,16 +2,33 @@ #include "../stack.h" namespace ink::runtime::internal { + unsigned char* snap_base(unsigned char* ptr, bool write, size_t pos, size_t jump, size_t save, size_t& max) + { + ptr = snapshot_interface::snap_write(ptr, pos, write); + ptr = snapshot_interface::snap_write(ptr, jump, write); + ptr = snapshot_interface::snap_write(ptr, save, write); + max = pos; + if (jump > max) { max = jump; } + if (save > max) { max = save; } + return ptr; + } + const unsigned char* snap_load_base(const unsigned char* ptr, size_t& pos, size_t& jump, size_t& save, size_t& max) + { + ptr = snapshot_interface::snap_read(ptr, pos); + ptr = snapshot_interface::snap_read(ptr, jump); + ptr = snapshot_interface::snap_read(ptr, save); + max = pos; + if (jump > max) { max = jump; } + if (save > max) { max = save; } + return ptr; + } + template<> size_t restorable::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _pos, data); - ptr = snap_write(ptr, _jump, data); - ptr = snap_write(ptr, _save, data); - size_t max = _pos; - if (_jump > max) { max = _jump; } - if (_save > max) { max = _save; } + size_t max; + ptr = snap_base(ptr, data, _pos, _jump, _save, max); for(size_t i = 0; i < max; ++i) { ptr = snap_write(ptr, _buffer[i].name, data); ptr += _buffer[i].data.snap(data ? ptr : nullptr, snapper); @@ -22,12 +39,8 @@ namespace ink::runtime::internal { size_t restorable::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _pos, data); - ptr = snap_write(ptr, _jump, data); - ptr = snap_write(ptr, _save, data); - size_t max = _pos; - if (_jump > max) { max = _jump; } - if (_save > max) { max = _save; } + size_t max; + ptr = snap_base(ptr, data, _pos, _jump, _save, max); for(size_t i = 0; i < max; ++i) { ptr += _buffer[i].snap(data ? ptr : nullptr, snapper); } @@ -37,15 +50,47 @@ namespace ink::runtime::internal { size_t restorable::snap(unsigned char* data, const snapper&) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _pos, data); - ptr = snap_write(ptr, _jump, data); - ptr = snap_write(ptr, _save, data); - size_t max = _pos; - if (_jump > max) { max = _jump; } - if (_save > max) { max = _save; } + size_t max; + ptr = snap_base(ptr, data, _pos, _jump, _save, max); for(size_t i = 0; i < max; ++i) { ptr = snap_write(ptr, _buffer[i], data); } return ptr - data; } + + template<> + const unsigned char* restorable::snap_load(const unsigned char* ptr, const loader& loader) + { + size_t max; + ptr = snap_load_base(ptr, _pos, _jump, _save, max); + while(_size < max) { overflow(_buffer, _size); } + for(size_t i = 0; i < max; ++i) { + ptr = snap_read(ptr, _buffer[i].name); + ptr = _buffer[i].data.snap_load(ptr, loader); + } + return ptr; + } + template<> + const unsigned char* restorable::snap_load(const unsigned char* ptr, const loader& loader) + { + size_t max; + ptr = snap_load_base(ptr, _pos, _jump, _save, max); + while(_size < max) { overflow(_buffer, _size); } + for(size_t i = 0; i < max; ++i) { + ptr = _buffer[i].snap_load(ptr, loader); + } + return ptr; + } + template<> + const unsigned char* restorable::snap_load(const unsigned char* ptr, const loader& loader) + { + size_t max; + ptr = snap_load_base(ptr, _pos, _jump, _save, max); + while(_size < max) { overflow(_buffer, _size); } + for(size_t i = 0; i < max; ++i) { + ptr = snap_read(ptr, _buffer[i]); + } + return ptr; + } + } diff --git a/inkcpp/collections/restorable.h b/inkcpp/collections/restorable.h index 0ddb8a77..7656fe91 100644 --- a/inkcpp/collections/restorable.h +++ b/inkcpp/collections/restorable.h @@ -334,7 +334,7 @@ namespace ink::runtime::internal // snapshot interface virtual size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; protected: // Called when we run out of space in buffer. @@ -390,4 +390,11 @@ namespace ink::runtime::internal size_t restorable::snap(unsigned char* data, const snapper& snapper) const; template<> size_t restorable::snap(unsigned char* data, const snapper&) const; + + template<> + const unsigned char* restorable::snap_load(const unsigned char* data, const loader&); + template<> + const unsigned char* restorable::snap_load(const unsigned char* data, const loader&); + template<> + const unsigned char* restorable::snap_load(const unsigned char* data, const loader&); } diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index c1544b24..e54f3587 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -235,6 +235,7 @@ namespace ink::runtime::internal ptr += _visit_counts.snap( data ? ptr : nullptr, snapper ); ptr += _strings.snap( data ? ptr : nullptr, snapper ); ptr += _lists.snap( data ? ptr : nullptr, snapper ); + ptr += _variables.snap(data ? ptr : nullptr, snapper ); return ptr - data; } @@ -245,6 +246,7 @@ namespace ink::runtime::internal inkAssert(_num_containers == _visit_counts.size(), "errer when loading visit counts, story file dont match snapshot!"); ptr = _strings.snap_load(ptr, loader); ptr = _lists.snap_load(ptr, loader); + ptr = _variables.snap_load(ptr, loader); return ptr; } } diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 976faa54..8a76082f 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -499,7 +499,6 @@ namespace ink::runtime::internal size_t basic_stream::snap(unsigned char* data, const snapper& snapper) const { - const string_table& strings = snapper.strings; unsigned char* ptr = data; ptr = snap_write(ptr, _last_char, data); ptr = snap_write(ptr, _size, data); @@ -510,4 +509,17 @@ namespace ink::runtime::internal } return ptr - data; } + + const unsigned char* basic_stream::snap_load(const unsigned char* ptr, const loader& loader) + { + ptr = snap_read(ptr, _last_char); + ptr = snap_read(ptr, _size); + ptr = snap_read(ptr, _save); + inkAssert(_max >= _size, "output is to small to hold stored data"); + for(auto itr = _data; itr != _data + _size; ++itr) + { + ptr = itr->snap_load(ptr, loader); + } + return ptr; + } } diff --git a/inkcpp/output.h b/inkcpp/output.h index e4a07eaa..1185ee20 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -102,7 +102,7 @@ namespace ink // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; private: size_t find_start() const; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 7a509603..7e0bda49 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -540,7 +540,6 @@ namespace ink::runtime::internal ptr = snap_write(ptr, _rng.get_state(), data); ptr = snap_write(ptr, _evaluation_mode, data); ptr = snap_write(ptr, _saved_evaluation_mode, data); - ptr = snap_write(ptr, _eval, data); ptr = snap_write(ptr, _saved, data); ptr = snap_write(ptr, _is_falling, data); ptr += _output.snap(data ? ptr : nullptr, snapper); @@ -585,7 +584,6 @@ namespace ink::runtime::internal _rng.srand(seed); ptr = snap_read(ptr, _evaluation_mode); ptr = snap_read(ptr, _saved_evaluation_mode); - ptr = snap_read(ptr, _eval); ptr = snap_read(ptr, _saved); ptr = snap_read(ptr, _is_falling); ptr = _output.snap_load(ptr, loader); @@ -633,6 +631,12 @@ namespace ink::runtime::internal ptr += _threadDone.snap(data ? ptr : nullptr, snapper); return ptr - data; } + const unsigned char* runner_impl::threads::snap_load(const unsigned char* ptr, const loader& loader) + { + ptr = base::snap_load(ptr, loader); + ptr = _threadDone.snap_load(ptr, loader); + return ptr; + } #ifdef INK_ENABLE_CSTD char* runner_impl::getline_alloc() diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index 5de6f2c2..a1e64565 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -202,7 +202,7 @@ namespace ink::runtime::internal // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; protected: virtual void overflow(thread_t*& buffer, size_t& size) override final; diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index 1616e2a6..1d1a4a59 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -29,7 +29,7 @@ namespace ink::runtime::internal void forget(); virtual size_t snap(unsigned char* data, const snapper&) const override; - virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + virtual const unsigned char* snap_load(const unsigned char* data, const loader&) override; protected: virtual void overflow(T*& buffer, size_t& size) { @@ -244,4 +244,25 @@ namespace ink::runtime::internal } return ptr - data; } + + template + const unsigned char* simple_restorable_stack::snap_load(const unsigned char* ptr, const loader& loader) + { + static_assert(is_same{}() || is_same{}() || is_same{}()); + T null; + ptr = snap_read(ptr, null); + inkAssert(null == _null, "different null value compared to snapshot!"); + ptr = snap_read(ptr, _pos); + ptr = snap_read(ptr, _save); + ptr = snap_read(ptr, _jump); + size_t max = _pos; + if(_save > max) { max = _save; } + if(_jump > max) { max = _jump; } + while(_size < max) { overflow(_buffer, _size); } + for(size_t i = 0; i < max; ++i) + { + ptr = snap_read(ptr, _buffer[i]); + } + return ptr; + } } diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index e90cb480..ea13f002 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -36,6 +36,8 @@ namespace ink::runtime::internal return _file + get_offset(idx + 1); } + size_t num_runners() const { return _header.num_runners; } + private: // file information // only populated when loading snapshots diff --git a/inkcpp/snapshot_interface.h b/inkcpp/snapshot_interface.h index 7609991a..12c6436f 100644 --- a/inkcpp/snapshot_interface.h +++ b/inkcpp/snapshot_interface.h @@ -11,7 +11,7 @@ namespace ink::runtime::internal template class managed_array; class snapshot_interface { - protected: + public: static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) { if (write) { memcpy(ptr, data, length); } @@ -32,7 +32,7 @@ namespace ink::runtime::internal { return snap_read(ptr, &data, sizeof(data)); } - public: + struct snapper { const string_table& strings; const char* story_string_table; diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index 79dacf6c..bf722381 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -596,4 +596,12 @@ namespace ink::runtime::internal ptr += base::snap(data ? ptr : nullptr, snapper); return ptr - data; } + + const unsigned char* basic_stack::snap_load(const unsigned char* ptr, const loader& loader) + { + ptr = snap_read(ptr, _next_thread); + ptr = snap_read(ptr, _backup_next_thread); + ptr = base::snap_load(ptr, loader); + return ptr; + } } diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 8ef1764d..7c3c7b35 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -82,7 +82,7 @@ namespace ink // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; private: entry& add(hash_t name, const value& val); diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 0cc1a444..147903bb 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -197,6 +197,24 @@ namespace ink::runtime::internal return runner(new runner_impl(this, store), _block); } + runner story_impl::new_runner_from_snapshot(const snapshot& data, globals store, unsigned idx) + { + const snapshot_impl& snapshot = reinterpret_cast(data); + if (store == nullptr) + store = new_globals_from_snapshot(snapshot); + auto* run = new runner_impl(this, store); + auto end = run->snap_load(snapshot.get_runner_snap(idx), + snapshot_interface::loader{ + .string_table = snapshot.strings(), + .story_string_table = _string_table, + }); + inkAssert( + (idx + 1 < snapshot.num_runners() && end == snapshot.get_runner_snap(idx + 1)) + || end == snapshot.get_data() + snapshot.get_data_len(), "not all data were used for runner reconstruction" + ); + return runner(run, _block); + } + void story_impl::setup_pointers() { using header = ink::internal::header; diff --git a/inkcpp/story_impl.h b/inkcpp/story_impl.h index ef73264f..2bfa322e 100644 --- a/inkcpp/story_impl.h +++ b/inkcpp/story_impl.h @@ -42,11 +42,7 @@ namespace ink::runtime::internal virtual globals new_globals() override; virtual globals new_globals_from_snapshot(const snapshot&) override; virtual runner new_runner(globals store = nullptr) override; - virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) override { - inkAssert(false, "not implmented yet!"); - return nullptr; - } - + virtual runner new_runner_from_snapshot(const snapshot&, globals store = nullptr, unsigned idx = 0) override; const ink::internal::header& get_header() const { return _header; } private: diff --git a/inkcpp/string_table.cpp b/inkcpp/string_table.cpp index 3647c68d..a34459df 100644 --- a/inkcpp/string_table.cpp +++ b/inkcpp/string_table.cpp @@ -98,8 +98,13 @@ namespace ink::runtime::internal size_t string_table::snap(unsigned char* data, const snapper&) const { unsigned char* ptr = data; - for(auto itr = _table.begin(); itr != _table.end(); ++itr) { - ptr = snap_write(ptr, itr.key(), strlen(itr.key()) + 1, data); + for (size_t i = 0; i < _table.size(); ++i) { + for(auto itr = _table.begin(); itr != _table.end(); ++itr) { + if (itr.temp_identifier() == i) { + ptr = snap_write(ptr, itr.key(), strlen(itr.key()) + 1, data); + break; + } + } } ptr = snap_write(ptr, "\0", 1, data); return ptr - data; @@ -108,6 +113,7 @@ namespace ink::runtime::internal const unsigned char* string_table::snap_load(const unsigned char* data, const loader& loader) { auto* ptr = data; + int i = 0; while(*ptr) { size_t len = 0; for(;ptr[len];++len); diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index e1f6b756..38bd7ffb 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -92,4 +92,17 @@ namespace ink::runtime::internal } return ptr - data; } + const unsigned char* value::snap_load(const unsigned char* ptr, const loader& loader) + { + ptr = snap_read(ptr, _type); + ptr = snap_read(ptr, &bool_value, max_value_size); + if(_type == value_type::string) { + if(string_value.allocated) { + string_value.str = loader.string_table[(std::uintptr_t)(string_value.str)]; + } else { + string_value.str = loader.story_string_table +(std::uintptr_t)(string_value.str); + } + } + return ptr; + } } diff --git a/inkcpp/value.h b/inkcpp/value.h index c450b903..3415b55c 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -87,7 +87,7 @@ namespace ink::runtime::internal { public: // snapshot interface size_t snap(unsigned char* data, const snapper&) const override; - const unsigned char* snap_load(const unsigned char* data, const loader&) override { inkAssert(false, "not implemented yet!"); return nullptr; } + const unsigned char* snap_load(const unsigned char* data, const loader&) override; /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 92047431..1f87196e 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -148,9 +148,10 @@ int main(int argc, const char** argv) // Start runner runner thread; if (snapshotFile.size()) { - globals glob = myInk->new_globals_from_snapshot(*snapshot::from_file(snapshotFile.c_str())); + thread = myInk->new_runner_from_snapshot(*snapshot::from_file(snapshotFile.c_str())); + } else { + thread = myInk->new_runner(); } - thread = myInk->new_runner(); while (true) { From 0d659818d1f4331474b7c95fea13709234565b28 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 12 Jul 2022 00:06:30 +0200 Subject: [PATCH 34/67] Add some docu --- README.md | 2 -- inkcpp/include/snapshot.h | 1 + inkcpp/snapshot_impl.h | 2 +- inkcpp_cl/inkcpp_cl.cpp | 5 ++--- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 35195eaa..7fb9cffe 100644 --- a/README.md +++ b/README.md @@ -136,8 +136,6 @@ The big things we're missing right now are: * Fallback functions for externals. * Variable observers -* Lists and whatever cool, crazy stuff Ink has been adding recently. -* Robust tests using ink-proof. ## Dependencies The compiler depends on Nlohmann's JSON library and the C++ STL. diff --git a/inkcpp/include/snapshot.h b/inkcpp/include/snapshot.h index ad4de434..987a7564 100644 --- a/inkcpp/include/snapshot.h +++ b/inkcpp/include/snapshot.h @@ -12,6 +12,7 @@ namespace ink::runtime virtual const unsigned char* get_data() const = 0; virtual size_t get_data_len() const = 0; + virtual size_t num_runners() const = 0; #ifdef INK_ENABLE_STL static snapshot* from_file(const char* filename); diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index ea13f002..0cba327c 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -36,7 +36,7 @@ namespace ink::runtime::internal return _file + get_offset(idx + 1); } - size_t num_runners() const { return _header.num_runners; } + size_t num_runners() override const { return _header.num_runners; } private: // file information diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 1f87196e..89613ef1 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -20,7 +20,7 @@ void usage() cout << "Usage: inkcpp_cl \n" << "\t-o :\tOutput file name\n" - << "\t-p []:\tPlay mode\n\toptional snapshot file to load\n" + << "\t-p []:\tPlay mode\n\toptional snapshot file to load\n\tto create a snapshot file enter '-1' as choice\n" << endl; } @@ -181,8 +181,7 @@ int main(int argc, const char** argv) std::cin >> c; if (c == -1) { snapshot* snap = thread->create_snapshot(); - // snap->write_to_file("test.snap"); - snap->write_to_file("test.snap"); + snap->write_to_file(std::regex_replace(inputFilename, std::regex("\\.[^\\.]+$"), ".snap").c_str()); break; } thread->choose(c - 1); From aa21c1949a17a1b940b6f1824abb2c6d3ea081a5 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 13:06:26 +0200 Subject: [PATCH 35/67] fix wrong order of const ovrride --- inkcpp/snapshot_impl.h | 2 +- inkcpp_test/NewLines.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index 0cba327c..4dc5acda 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -36,7 +36,7 @@ namespace ink::runtime::internal return _file + get_offset(idx + 1); } - size_t num_runners() override const { return _header.num_runners; } + size_t num_runners() const override { return _header.num_runners; } private: // file information diff --git a/inkcpp_test/NewLines.cpp b/inkcpp_test/NewLines.cpp index adaa078c..910dfa88 100644 --- a/inkcpp_test/NewLines.cpp +++ b/inkcpp_test/NewLines.cpp @@ -2,6 +2,7 @@ #include "../inkcpp_cl/test.h" #include +#include #include #include From 1ad5406cc63305ea9afd69d952e7040de9ac270d Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 13:41:20 +0200 Subject: [PATCH 36/67] Fix access to union to conform with mvsc --- inkcpp/value.cpp | 6 +++--- inkcpp/value.h | 14 +------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index 38bd7ffb..ade47708 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -76,7 +76,7 @@ namespace ink::runtime::internal unsigned char* ptr = data; ptr = snap_write(ptr, _type, data); if (_type == value_type::string) { - unsigned char buf[max_value_size]; + unsigned char buf[max_value_size()]; string_type* res = reinterpret_cast(buf); auto str = get(); res->allocated = str.allocated; @@ -88,14 +88,14 @@ namespace ink::runtime::internal ptr = snap_write(ptr, buf, data); } else { // TODO more space efficent? - ptr = snap_write(ptr, &bool_value, max_value_size, data); + ptr = snap_write(ptr, &bool_value, max_value_size(), data); } return ptr - data; } const unsigned char* value::snap_load(const unsigned char* ptr, const loader& loader) { ptr = snap_read(ptr, _type); - ptr = snap_read(ptr, &bool_value, max_value_size); + ptr = snap_read(ptr, &bool_value, max_value_size()); if(_type == value_type::string) { if(string_value.allocated) { string_value.str = loader.string_table[(std::uintptr_t)(string_value.str)]; diff --git a/inkcpp/value.h b/inkcpp/value.h index 3415b55c..ef973519 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -165,19 +165,7 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size = - sizeof_largest_type< - decltype(bool_value), - decltype(int32_value), - decltype(string_value), - decltype(uint32_value), - decltype(float_value), - decltype(jump), - decltype(list_value), - decltype(list_flag_value), - decltype(frame_value), - decltype(pointer) - >(); + static constexpr size_t max_value_size() { return sizeof(value) - sizeof(value_type); } value_type _type; }; From 888c39a1728ea3f903d6ed6580740d734354ee9f Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 14:24:37 +0200 Subject: [PATCH 37/67] TEST commit --- inkcpp/value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkcpp/value.h b/inkcpp/value.h index ef973519..333f943b 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -165,7 +165,7 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size() { return sizeof(value) - sizeof(value_type); } + static constexpr size_t max_value_size() { return 128; } value_type _type; }; From 4e151805e6134a212bcb59170c88b7cd663fa6ff Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 14:34:31 +0200 Subject: [PATCH 38/67] Fiddling with msvc error --- inkcpp/value.cpp | 6 +++--- inkcpp/value.h | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index ade47708..38bd7ffb 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -76,7 +76,7 @@ namespace ink::runtime::internal unsigned char* ptr = data; ptr = snap_write(ptr, _type, data); if (_type == value_type::string) { - unsigned char buf[max_value_size()]; + unsigned char buf[max_value_size]; string_type* res = reinterpret_cast(buf); auto str = get(); res->allocated = str.allocated; @@ -88,14 +88,14 @@ namespace ink::runtime::internal ptr = snap_write(ptr, buf, data); } else { // TODO more space efficent? - ptr = snap_write(ptr, &bool_value, max_value_size(), data); + ptr = snap_write(ptr, &bool_value, max_value_size, data); } return ptr - data; } const unsigned char* value::snap_load(const unsigned char* ptr, const loader& loader) { ptr = snap_read(ptr, _type); - ptr = snap_read(ptr, &bool_value, max_value_size()); + ptr = snap_read(ptr, &bool_value, max_value_size); if(_type == value_type::string) { if(string_value.allocated) { string_value.str = loader.string_table[(std::uintptr_t)(string_value.str)]; diff --git a/inkcpp/value.h b/inkcpp/value.h index 333f943b..2c3b848e 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -92,7 +92,7 @@ namespace ink::runtime::internal { /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; - constexpr value() : _type{value_type::none}, uint32_value{0}{} + constexpr value() : _type{value_type::none}, bool_value{0}{} /// get value of the type (if possible) template @@ -165,7 +165,19 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size() { return 128; } + static constexpr size_t max_value_size = + sizeof_largest_type< + decltype(bool_value), + decltype(int32_value), + decltype(string_value), + decltype(uint32_value), + decltype(float_value), + decltype(jump), + decltype(list_value), + decltype(list_flag_value), + decltype(frame_value), + decltype(pointer) + >(); value_type _type; }; From 779e9350803c8ead5e9a4132169a33223936e055 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 14:42:07 +0200 Subject: [PATCH 39/67] Next try --- inkcpp/value.h | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/inkcpp/value.h b/inkcpp/value.h index 2c3b848e..e1a429b5 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -165,19 +165,7 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size = - sizeof_largest_type< - decltype(bool_value), - decltype(int32_value), - decltype(string_value), - decltype(uint32_value), - decltype(float_value), - decltype(jump), - decltype(list_value), - decltype(list_flag_value), - decltype(frame_value), - decltype(pointer) - >(); + static constexpr size_t max_value_size() { return 120; } value_type _type; }; From 8ac52e5c1c4d0094cf321c6b5044542f1ec25fe8 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 14:43:44 +0200 Subject: [PATCH 40/67] Next --- inkcpp/value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkcpp/value.h b/inkcpp/value.h index e1a429b5..f01c79d7 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -165,7 +165,7 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size() { return 120; } + static constexpr size_t max_value_size = 128; value_type _type; }; From bd5f48fa5c0a22a9966fa0b7a9ec15d5f67d7c13 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 13 Jul 2022 17:16:19 +0200 Subject: [PATCH 41/67] More try --- inkcpp/snapshot_interface.h | 2 ++ inkcpp/value.h | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/inkcpp/snapshot_interface.h b/inkcpp/snapshot_interface.h index 12c6436f..1408124b 100644 --- a/inkcpp/snapshot_interface.h +++ b/inkcpp/snapshot_interface.h @@ -10,8 +10,10 @@ namespace ink::runtime::internal class string_table; template class managed_array; + class snapshot_interface { public: + constexpr snapshot_interface(){}; static unsigned char* snap_write(unsigned char* ptr, const void* data, size_t length, bool write) { if (write) { memcpy(ptr, data, length); } diff --git a/inkcpp/value.h b/inkcpp/value.h index f01c79d7..212b212e 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -92,7 +92,7 @@ namespace ink::runtime::internal { /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; - constexpr value() : _type{value_type::none}, bool_value{0}{} + constexpr value() : snapshot_interface(), _type{value_type::none}, bool_value{0}{} /// get value of the type (if possible) template @@ -165,7 +165,19 @@ namespace ink::runtime::internal { char ci; } pointer; }; - static constexpr size_t max_value_size = 128; + static constexpr size_t max_value_size = + sizeof_largest_type< + decltype(bool_value), + decltype(int32_value), + decltype(string_value), + decltype(uint32_value), + decltype(float_value), + decltype(jump), + decltype(list_value), + decltype(list_flag_value), + decltype(frame_value), + decltype(pointer) + >(); value_type _type; }; From 045c300e0c3e975b677fe33c30f2c75d4bec60cb Mon Sep 17 00:00:00 2001 From: JBenda Date: Thu, 14 Jul 2022 17:19:11 +0200 Subject: [PATCH 42/67] Adapt to msvc for c++17 --- inkcpp/snapshot_impl.cpp | 4 ++-- inkcpp/story_impl.cpp | 9 ++++----- inkcpp/value.cpp | 4 ++-- inkcpp/value.h | 13 +++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/inkcpp/snapshot_impl.cpp b/inkcpp/snapshot_impl.cpp index 28c6c179..c819929b 100644 --- a/inkcpp/snapshot_impl.cpp +++ b/inkcpp/snapshot_impl.cpp @@ -61,8 +61,8 @@ namespace ink::runtime::internal : _managed{true} { snapshot_interface::snapper snapper{ - .strings = globals.strings(), - .story_string_table = globals._owner->string(0) + globals.strings(), + globals._owner->string(0) }; _length = globals.snap(nullptr, snapper); size_t runner_cnt = 0; diff --git a/inkcpp/story_impl.cpp b/inkcpp/story_impl.cpp index 147903bb..1904f08b 100644 --- a/inkcpp/story_impl.cpp +++ b/inkcpp/story_impl.cpp @@ -182,9 +182,8 @@ namespace ink::runtime::internal const snapshot_impl& snapshot = reinterpret_cast(data); auto* globs = new globals_impl(this); auto end = globs->snap_load(snapshot.get_globals_snap(), snapshot_interface::loader{ - .string_table = snapshot.strings(), - .story_string_table = _string_table, - + snapshot.strings(), + _string_table, }); inkAssert(end == snapshot.get_runner_snap(0), "not all data were used for global reconstruction"); return globals(globs, _block); @@ -205,8 +204,8 @@ namespace ink::runtime::internal auto* run = new runner_impl(this, store); auto end = run->snap_load(snapshot.get_runner_snap(idx), snapshot_interface::loader{ - .string_table = snapshot.strings(), - .story_string_table = _string_table, + snapshot.strings(), + _string_table, }); inkAssert( (idx + 1 < snapshot.num_runners() && end == snapshot.get_runner_snap(idx + 1)) diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index 38bd7ffb..0f8240db 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -81,9 +81,9 @@ namespace ink::runtime::internal auto str = get(); res->allocated = str.allocated; if (str.allocated) { - res->str = reinterpret_cast(snapper.strings.get_id(str.str)); + res->str = reinterpret_cast(static_cast(snapper.strings.get_id(str.str))); } else { - res->str = reinterpret_cast(str.str - snapper.story_string_table); + res->str = reinterpret_cast(static_cast(str.str - snapper.story_string_table)); } ptr = snap_write(ptr, buf, data); } else { diff --git a/inkcpp/value.h b/inkcpp/value.h index 212b212e..47a12a05 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -93,6 +93,7 @@ namespace ink::runtime::internal { template struct ret { using type = void; }; constexpr value() : snapshot_interface(), _type{value_type::none}, bool_value{0}{} + constexpr explicit value( value_type type ) : _type{ type }, bool_value{0} {} /// get value of the type (if possible) template @@ -434,11 +435,11 @@ namespace ink::runtime::internal { // static constexpr instantiations of flag values namespace values { - static constexpr value marker = value{}.set(); - static constexpr value glue = value{}.set(); - static constexpr value newline = value{}.set(); - static constexpr value func_start = value{}.set(); - static constexpr value func_end = value{}.set(); - static constexpr value null = value{}.set(); + static constexpr value marker = value( value_type::marker ); + static constexpr value glue = value( value_type::glue ); + static constexpr value newline = value( value_type::newline ); + static constexpr value func_start = value( value_type::func_start ); + static constexpr value func_end = value( value_type::func_end ); + static constexpr value null = value( value_type::null ); } } From 4933f7923b662a1f5616ea89883f70d2719b9516 Mon Sep 17 00:00:00 2001 From: JBenda Date: Wed, 16 Nov 2022 11:44:54 +0100 Subject: [PATCH 43/67] Fixes minore warnings --- inkcpp/array.h | 2 +- inkcpp/list_table.h | 6 +++--- inkcpp/stack.h | 2 +- shared/public/system.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/inkcpp/array.h b/inkcpp/array.h index 4750dae5..821f4e58 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -9,7 +9,7 @@ namespace ink::runtime::internal template class managed_array : snapshot_interface { public: - managed_array() : _capacity{initialCapacity}, _size{0}{ + managed_array() : _capacity{ initialCapacity }, _size{ 0 }, _static_data{0}{ if constexpr (dynamic) { _dynamic_data = new T[initialCapacity]; } diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index 3f7a6461..888e0c0a 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -64,7 +64,7 @@ namespace ink::runtime::internal // parse binary list meta data list_table(const char* data, const ink::internal::header&); - explicit list_table() : _valid{false} {} + explicit list_table() : _valid{ false }, _entrySize{0} {} size_t stringLen(const list_flag& e) const; const char* toString(const list_flag& e) const; @@ -158,10 +158,10 @@ namespace ink::runtime::internal return lid == 0 ? 0 : _list_end[lid-1]; } const data_t* getPtr(int eid) const { - return _data.begin() + _entrySize * eid; + return _data.begin() + static_cast(_entrySize) * static_cast(eid); } data_t* getPtr(int eid) { - return _data.begin() + _entrySize * eid; + return _data.begin() + static_cast(_entrySize) * static_cast(eid); } int numFlags() const { return _flag_names.size(); diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 7425c85d..652f6d3c 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -14,7 +14,7 @@ namespace ink class string_table; struct entry { - hash_t name; + hash_t name = 0; value data; }; diff --git a/shared/public/system.h b/shared/public/system.h index 46516da9..32721230 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -104,7 +104,7 @@ namespace ink case '\n': if (!includeNewline) return false; - case '\t': + case '\t': [[fallthrough]]; case ' ': return true; default: From 9f2c751b493e05441f1f5c1e66ff5349bd32f8a7 Mon Sep 17 00:00:00 2001 From: JBenda Date: Wed, 16 Nov 2022 12:45:18 +0100 Subject: [PATCH 44/67] Start implementing UE interface --- inkcpp_cl/inkcpp_cl.cpp | 5 ++++- .../inkcpp/Source/inkcpp/Private/InkRuntime.cpp | 8 ++++++++ unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h | 17 +++++++++++++++-- unreal/inkcpp/Source/inkcpp/Public/InkVar.h | 2 +- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index 0610fc21..ff6d77bb 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -148,7 +148,9 @@ int main(int argc, const char** argv) // Start runner runner thread; if (snapshotFile.size()) { - thread = myInk->new_runner_from_snapshot(*snapshot::from_file(snapshotFile.c_str())); + auto snap_ptr = snapshot::from_file( snapshotFile.c_str() ); + thread = myInk->new_runner_from_snapshot(*snap_ptr); + delete snap_ptr; } else { thread = myInk->new_runner(); } @@ -181,6 +183,7 @@ int main(int argc, const char** argv) if (c == -1) { snapshot* snap = thread->create_snapshot(); snap->write_to_file(std::regex_replace(inputFilename, std::regex("\\.[^\\.]+$"), ".snap").c_str()); + delete snap; break; } thread->choose(c - 1); diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index 59801dc4..f45bdcec 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -135,6 +135,14 @@ UInkThread* AInkRuntime::Start(TSubclassOf type, FString path, return StartExisting(pThread, path, startImmediately); } +FInkSnapshot Snapshot() +{ + ink::runtime::snapshot* inkSnapshot = mpGlobals->create_snapshot(); + FInkSnapshot snapshot(inkSnapshot.get_data(), inkSnapshot.get_data_len()); + delete inkSnapshot; + return snapshot; +} + UInkThread* AInkRuntime::StartExisting(UInkThread* thread, FString path, bool startImmediately /*= true*/) { if (mpRuntime == nullptr) diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index e8951d21..f20c5e1c 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -15,6 +15,7 @@ class UInkThread; struct FInkVar; +struct FInkSnapshot; namespace ink::runtime { class story; } UCLASS() @@ -28,10 +29,22 @@ class INKCPP_API AInkRuntime : public AActor ~AInkRuntime(); UFUNCTION(BlueprintCallable, Category="Ink") - UInkThread* Start(TSubclassOf type, FString path, bool runImmediately = true); + UInkThread* Start(TSubclassOf type, FString path = "", bool runImmediately = true); UFUNCTION(BlueprintCallable, Category="Ink") - UInkThread* StartExisting(UInkThread* thread, FString path, bool runImmediately = true); + UInkThread* StartExisting(UInkThread* thread, FString path = "", bool runImmediately = true); + + // only tested in choices moments + UFUNCTION(BlueprintCallable, Category="Ink") + FInkSnapshot* Snapshot(); + + AInkRuntime(FInkSnapshot* snapshot); + + UFUNCTION(BlueprintCallable, Category="Ink") + UInkThread* LoadThread(TSubclassOf type, int id = 0); + + UFUNCTION(BlueprintCallable, Category="Ink") + UInkThread* LoadInThreadExisting(UInkThread* thread, int id = 0); // Marks a thread as "exclusive". As long as it is running, no other threads will update. UFUNCTION(BlueprintCallable, Category="Ink") diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h index fc898f38..58916d2e 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkVar.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkVar.h @@ -10,7 +10,7 @@ #include "InkVar.generated.h" -// A wrapper for passing around ink vars to and fro ink itself +// A wrapper for passing around ink vars to and from ink itself // Not templated so it can be used in blueprints UENUM(BlueprintType) enum class EInkVarType : uint8 From 83d378393cebf792eaf6f0fe279a71b7eb64b5f4 Mon Sep 17 00:00:00 2001 From: JBenda Date: Thu, 17 Nov 2022 19:37:03 +0100 Subject: [PATCH 45/67] Translate Snapshot to UE --- inkcpp/array.h | 22 +++++----- inkcpp/collections/restorable.cpp | 10 ++--- inkcpp/list_table.h | 2 +- inkcpp/output.cpp | 43 ++----------------- inkcpp/output.h | 4 +- inkcpp/runner_impl.cpp | 37 ++++++++-------- inkcpp/simple_restorable_stack.h | 11 ++--- inkcpp/snapshot_impl.h | 2 +- inkcpp/stack.cpp | 5 ++- inkcpp/string_table.cpp | 5 ++- inkcpp/string_table.h | 2 +- inkcpp/value.cpp | 7 +-- inkcpp/value.h | 4 +- .../Source/inkcpp/Private/InkRuntime.cpp | 35 ++++++++++++--- .../Source/inkcpp/Private/InkThread.cpp | 9 ++-- .../inkcpp/Source/inkcpp/Public/InkRuntime.h | 31 +++++++++---- .../inkcpp/Source/inkcpp/Public/InkSnapshot.h | 14 ++++++ .../inkcpp_editor/Private/InkAssetFactory.cpp | 7 ++- 18 files changed, 139 insertions(+), 111 deletions(-) create mode 100644 unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h diff --git a/inkcpp/array.h b/inkcpp/array.h index 821f4e58..6594497b 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -9,7 +9,7 @@ namespace ink::runtime::internal template class managed_array : snapshot_interface { public: - managed_array() : _capacity{ initialCapacity }, _size{ 0 }, _static_data{0}{ + managed_array() : _static_data{}, _capacity{ initialCapacity }, _size{ 0 }{ if constexpr (dynamic) { _dynamic_data = new T[initialCapacity]; } @@ -55,7 +55,7 @@ namespace ink::runtime::internal extend(size); } } else { - ink_assert(size <= _size, "Only allow to reduce size"); + inkAssert(size <= _size, "Only allow to reduce size"); } _size = size; } @@ -66,9 +66,10 @@ namespace ink::runtime::internal { inkAssert(!is_pointer{}(), "here is a special case oversight"); unsigned char* ptr = data; - ptr = snap_write(ptr, _size, data); + bool should_write = data != nullptr; + ptr = snap_write(ptr, _size, should_write ); for(const T& e : *this) { - ptr = snap_write(ptr, e, data); + ptr = snap_write(ptr, e, should_write ); } return ptr - data; } @@ -187,12 +188,13 @@ namespace ink::runtime::internal inline size_t basic_restorable_array::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _saved, data); - ptr = snap_write(ptr, _capacity, data); - ptr = snap_write(ptr, _null, data); + bool should_write = data != nullptr; + ptr = snap_write(ptr, _saved, should_write ); + ptr = snap_write(ptr, _capacity, should_write ); + ptr = snap_write(ptr, _null, should_write ); for(size_t i = 0; i < _capacity; ++i) { - ptr = snap_write(ptr, _array[i], data); - ptr = snap_write(ptr, _temp[i], data); + ptr = snap_write(ptr, _array[i], should_write ); + ptr = snap_write(ptr, _temp[i], should_write ); } return ptr - data; } @@ -344,7 +346,7 @@ namespace ink::runtime::internal this->set_new_buffer(_buffer, new_capacity); } - ~allocated_restorable_array() + virtual ~allocated_restorable_array() final { if(_buffer) { delete[] _buffer; diff --git a/inkcpp/collections/restorable.cpp b/inkcpp/collections/restorable.cpp index e9b4b43d..b7bf8230 100644 --- a/inkcpp/collections/restorable.cpp +++ b/inkcpp/collections/restorable.cpp @@ -28,9 +28,9 @@ namespace ink::runtime::internal { { unsigned char* ptr = data; size_t max; - ptr = snap_base(ptr, data, _pos, _jump, _save, max); + ptr = snap_base(ptr, data != nullptr, _pos, _jump, _save, max); for(size_t i = 0; i < max; ++i) { - ptr = snap_write(ptr, _buffer[i].name, data); + ptr = snap_write(ptr, _buffer[i].name, data != nullptr); ptr += _buffer[i].data.snap(data ? ptr : nullptr, snapper); } return ptr - data; @@ -40,7 +40,7 @@ namespace ink::runtime::internal { { unsigned char* ptr = data; size_t max; - ptr = snap_base(ptr, data, _pos, _jump, _save, max); + ptr = snap_base(ptr, data != nullptr, _pos, _jump, _save, max); for(size_t i = 0; i < max; ++i) { ptr += _buffer[i].snap(data ? ptr : nullptr, snapper); } @@ -51,9 +51,9 @@ namespace ink::runtime::internal { { unsigned char* ptr = data; size_t max; - ptr = snap_base(ptr, data, _pos, _jump, _save, max); + ptr = snap_base(ptr, data != nullptr, _pos, _jump, _save, max); for(size_t i = 0; i < max; ++i) { - ptr = snap_write(ptr, _buffer[i], data); + ptr = snap_write(ptr, _buffer[i], data != nullptr); } return ptr - data; } diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index 888e0c0a..0737d1f3 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -64,7 +64,7 @@ namespace ink::runtime::internal // parse binary list meta data list_table(const char* data, const ink::internal::header&); - explicit list_table() : _valid{ false }, _entrySize{0} {} + explicit list_table() : _entrySize{0}, _valid{ false } {} size_t stringLen(const list_flag& e) const; const char* toString(const list_flag& e) const; diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 463a4696..0f399f4d 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -147,43 +147,8 @@ namespace ink::runtime::internal #ifdef INK_ENABLE_UNREAL FString basic_stream::get() { - size_t start = find_start(); - - // TODO: Slow! FString concatonation. - // Is there really no equivilent of stringstream in Unreal? Some kind of String Builder? - - // Move up from marker - bool hasGlue = false; + UE_LOG( InkCpp, Warning, TEXT("Basic stream::get is not implemented correctly and should not be used implemented correctly!" ) ); FString str; - for (size_t i = start; i < _size; i++) - { - if (should_skip(i, hasGlue)) - continue; - - switch (_data[i].type) - { - case value_type::int32: - str += FString::Printf(TEXT("%d"), _data[i].integer_value); - break; - case value_type::float32: - // TODO: Whitespace cleaning - str += FString::Printf(TEXT("%f"), _data[i].float_value); - break; - case value_type::string: - str += _data[i].string_val; - break; - case data_type::newline: - str += "\n"; - break; - default: - break; - } - } - - // Reset stream size to where we last held the marker - _size = start; - - // Return processed string return str; } #endif @@ -502,9 +467,9 @@ namespace ink::runtime::internal size_t basic_stream::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _last_char, data); - ptr = snap_write(ptr, _size, data); - ptr = snap_write(ptr, _save, data); + ptr = snap_write(ptr, _last_char, data != nullptr); + ptr = snap_write(ptr, _size, data != nullptr); + ptr = snap_write(ptr, _save, data != nullptr); for(auto itr = _data; itr != _data + _size; ++itr) { ptr += itr->snap(data ? ptr : nullptr, snapper); diff --git a/inkcpp/output.h b/inkcpp/output.h index f36fa11f..56b680e7 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -54,8 +54,8 @@ namespace ink #ifdef INK_ENABLE_STL // Extract into a string std::string get(); -#else - // will conflict with stl definition +#else defined INK_ENABLE_UNREAL + FString get(); #endif // Check if the stream is empty diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 6e7388f5..1052be8b 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -542,39 +542,40 @@ namespace ink::runtime::internal size_t runner_impl::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _ptr, data); - ptr = snap_write(ptr, _backup, data); - ptr = snap_write(ptr, _done, data); - ptr = snap_write(ptr, _rng.get_state(), data); - ptr = snap_write(ptr, _evaluation_mode, data); - ptr = snap_write(ptr, _saved_evaluation_mode, data); - ptr = snap_write(ptr, _saved, data); - ptr = snap_write(ptr, _is_falling, data); + bool should_write = data != nullptr; + ptr = snap_write(ptr, _ptr, should_write); + ptr = snap_write(ptr, _backup, should_write); + ptr = snap_write(ptr, _done, should_write); + ptr = snap_write(ptr, _rng.get_state(), should_write); + ptr = snap_write(ptr, _evaluation_mode, should_write); + ptr = snap_write(ptr, _saved_evaluation_mode, should_write); + ptr = snap_write(ptr, _saved, should_write); + ptr = snap_write(ptr, _is_falling, should_write); ptr += _output.snap(data ? ptr : nullptr, snapper); ptr += _stack.snap(data ? ptr : nullptr, snapper); ptr += _ref_stack.snap(data ? ptr : nullptr, snapper); ptr += _eval.snap(data ? ptr : nullptr, snapper); - ptr = snap_write(ptr, _tags.size(), data); + ptr = snap_write(ptr, _tags.size(), should_write); for (const auto& tag : _tags) { std::uintptr_t offset = tag - snapper.story_string_table; - ptr = snap_write(ptr, offset, data); + ptr = snap_write(ptr, offset, should_write); } ptr += _container.snap(data ? ptr : nullptr, snapper); ptr += _threads.snap(data ? ptr : nullptr, snapper); - ptr = snap_write(ptr, _backup_choice_len, data); - ptr = snap_write(ptr, _fallback_choice.has_value(), data); - auto snap_choice = [&snapper, write = static_cast(data)](unsigned char* data, const choice& c) -> size_t { + ptr = snap_write(ptr, _backup_choice_len, should_write); + ptr = snap_write(ptr, _fallback_choice.has_value(), should_write); + auto snap_choice = [&snapper, &should_write](unsigned char* data, const choice& c) -> size_t { unsigned char* ptr = data; - ptr = snap_write(ptr, c._index, write); - ptr = snap_write(ptr, c._path, write); - ptr = snap_write(ptr, c._thread, write); - ptr = snap_write(ptr, snapper.strings.get_id(c._text), write); + ptr = snap_write(ptr, c._index, should_write ); + ptr = snap_write(ptr, c._path, should_write ); + ptr = snap_write(ptr, c._thread, should_write ); + ptr = snap_write(ptr, snapper.strings.get_id(c._text), should_write ); return ptr - data; }; if (_fallback_choice) { ptr += snap_choice(ptr, _fallback_choice.value()); } - ptr = snap_write(ptr, _choices.size(), data); + ptr = snap_write(ptr, _choices.size(), should_write); for (const choice& c : _choices) { ptr += snap_choice(ptr, c); } diff --git a/inkcpp/simple_restorable_stack.h b/inkcpp/simple_restorable_stack.h index d5a5413e..cacb8806 100644 --- a/inkcpp/simple_restorable_stack.h +++ b/inkcpp/simple_restorable_stack.h @@ -232,16 +232,17 @@ namespace ink::runtime::internal { static_assert(is_same{}() || is_same{}() || is_same{}()); unsigned char* ptr = data; - ptr = snap_write(ptr, _null, data); - ptr = snap_write(ptr, _pos, data); - ptr = snap_write(ptr, _save, data); - ptr = snap_write(ptr, _jump, data); + bool should_write = data != nullptr; + ptr = snap_write(ptr, _null, should_write); + ptr = snap_write(ptr, _pos, should_write ); + ptr = snap_write(ptr, _save, should_write ); + ptr = snap_write(ptr, _jump, should_write ); size_t max = _pos; if (_save > max) { max = _save; } if (_jump > max) { max = _jump; } for(size_t i = 0; i < max; ++i) { - ptr = snap_write(ptr, _buffer[i], data); + ptr = snap_write(ptr, _buffer[i], should_write ); } return ptr - data; } diff --git a/inkcpp/snapshot_impl.h b/inkcpp/snapshot_impl.h index 4dc5acda..7eb7ebd2 100644 --- a/inkcpp/snapshot_impl.h +++ b/inkcpp/snapshot_impl.h @@ -53,7 +53,7 @@ namespace ink::runtime::internal } _header; size_t get_offset(size_t idx) const { - inkAssert(idx <= _header.num_runners); + inkAssert(idx <= _header.num_runners, "Out of Bound access for runner in snapshot."); return reinterpret_cast(_file + sizeof(header))[idx]; } diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index a28e864f..e041728f 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -591,8 +591,9 @@ namespace ink::runtime::internal size_t basic_stack::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _next_thread, data); - ptr = snap_write(ptr, _backup_next_thread, data); + bool should_write = data != nullptr; + ptr = snap_write(ptr, _next_thread, should_write ); + ptr = snap_write(ptr, _backup_next_thread, should_write ); ptr += base::snap(data ? ptr : nullptr, snapper); return ptr - data; } diff --git a/inkcpp/string_table.cpp b/inkcpp/string_table.cpp index a34459df..467a6e1a 100644 --- a/inkcpp/string_table.cpp +++ b/inkcpp/string_table.cpp @@ -98,15 +98,16 @@ namespace ink::runtime::internal size_t string_table::snap(unsigned char* data, const snapper&) const { unsigned char* ptr = data; + bool should_write = data != nullptr; for (size_t i = 0; i < _table.size(); ++i) { for(auto itr = _table.begin(); itr != _table.end(); ++itr) { if (itr.temp_identifier() == i) { - ptr = snap_write(ptr, itr.key(), strlen(itr.key()) + 1, data); + ptr = snap_write(ptr, itr.key(), strlen(itr.key()) + 1, should_write ); break; } } } - ptr = snap_write(ptr, "\0", 1, data); + ptr = snap_write(ptr, "\0", 1, should_write ); return ptr - data; } diff --git a/inkcpp/string_table.h b/inkcpp/string_table.h index 4fd0cbd2..94785f41 100644 --- a/inkcpp/string_table.h +++ b/inkcpp/string_table.h @@ -10,7 +10,7 @@ namespace ink::runtime::internal class string_table : public snapshot_interface { public: - ~string_table(); + virtual ~string_table() final; // Create a dynmaic string of a particular length char* create(size_t length); diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index ff01c75c..ab2549d5 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -115,7 +115,8 @@ namespace ink::runtime::internal size_t value::snap(unsigned char* data, const snapper& snapper) const { unsigned char* ptr = data; - ptr = snap_write(ptr, _type, data); + bool should_write = data != nullptr; + ptr = snap_write(ptr, _type, should_write ); if (_type == value_type::string) { unsigned char buf[max_value_size]; string_type* res = reinterpret_cast(buf); @@ -126,10 +127,10 @@ namespace ink::runtime::internal } else { res->str = reinterpret_cast(static_cast(str.str - snapper.story_string_table)); } - ptr = snap_write(ptr, buf, data); + ptr = snap_write(ptr, buf, should_write ); } else { // TODO more space efficent? - ptr = snap_write(ptr, &bool_value, max_value_size, data); + ptr = snap_write(ptr, &bool_value, max_value_size, should_write ); } return ptr - data; } diff --git a/inkcpp/value.h b/inkcpp/value.h index b192ed60..410ea4eb 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -93,8 +93,8 @@ namespace ink::runtime::internal { /// help struct to determine cpp type which represent the value_type template struct ret { using type = void; }; - constexpr value() : snapshot_interface(), _type{ value_type::none }, bool_value{ 0 }{} - constexpr explicit value( value_type type ) : _type{ type }, bool_value{ 0 } {} + constexpr value() : snapshot_interface(), bool_value{ 0 }, _type{ value_type::none } {} + constexpr explicit value( value_type type ) : bool_value{ 0 }, _type{ type } {} explicit value(const ink::runtime::value& val); bool set( const ink::runtime::value& val ); diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index f45bdcec..a94afdb5 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -12,6 +12,7 @@ // inkcpp includes #include "ink/story.h" #include "ink/globals.h" +#include "ink/snapshot.h" namespace ink { using value = runtime::value; } @@ -24,7 +25,8 @@ AInkRuntime::AInkRuntime() : mpRuntime(nullptr) AInkRuntime::~AInkRuntime() { - + if(mSnapshot) { delete mpSnapshot; } + mSnapshot.Reset(); } // Called when the game starts or when spawned @@ -38,7 +40,11 @@ void AInkRuntime::BeginPlay() UE_LOG(InkCpp, Display, TEXT("Loaded Ink asset")); // create globals - mpGlobals = mpRuntime->new_globals(); + if (mSnapshot) { + LoadSnapshot(*mSnapshot); + } else { + mpGlobals = mpRuntime->new_globals(); + } // initialize globals mpRuntime->new_runner(mpGlobals); } @@ -135,14 +141,20 @@ UInkThread* AInkRuntime::Start(TSubclassOf type, FString path, return StartExisting(pThread, path, startImmediately); } -FInkSnapshot Snapshot() +FInkSnapshot AInkRuntime::Snapshot() { ink::runtime::snapshot* inkSnapshot = mpGlobals->create_snapshot(); - FInkSnapshot snapshot(inkSnapshot.get_data(), inkSnapshot.get_data_len()); + FInkSnapshot snapshot(reinterpret_cast(inkSnapshot->get_data()), inkSnapshot->get_data_len()); delete inkSnapshot; return snapshot; } +void AInkRuntime::LoadSnapshot(const FInkSnapshot& snapshot) { + mSnapshot = snapshot; + mpSnapshot = ink::runtime::snapshot::from_binary(reinterpret_cast(mSnapshot->data.GetData()), mSnapshot->data.Num(), false); + mpGlobals = mpRuntime->new_globals_from_snapshot(*mpSnapshot); +} + UInkThread* AInkRuntime::StartExisting(UInkThread* thread, FString path, bool startImmediately /*= true*/) { if (mpRuntime == nullptr) @@ -150,9 +162,22 @@ UInkThread* AInkRuntime::StartExisting(UInkThread* thread, FString path, bool st UE_LOG(InkCpp, Warning, TEXT("Failed to start existing")); return nullptr; } + + // remove handle if it still exists + mThreads.Remove(thread); + mExclusiveStack.Remove(thread); // Initialize thread with new runner - ink::runtime::runner runner = mpRuntime->new_runner(mpGlobals); + ink::runtime::runner runner; + if (mSnapshot && path.IsEmpty()) { + if (mpSnapshot->num_runners() == mThreads.Num()) { + UE_LOG(InkCpp, Warning, TEXT("Already created all Threads from Snapshot!, will not create more. You can Still create new Threads with entering the starting Path.")); + return nullptr; + } + runner = mpRuntime->new_runner_from_snapshot(*mpSnapshot, mpGlobals, mThreads.Num()); + } else { + runner = mpRuntime->new_runner(mpGlobals); + } thread->Initialize(path, this, runner); // If we're not starting immediately, just queue diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index 488a4f6b..eb02db8e 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -46,16 +46,15 @@ void UInkThread::RegisterExternalEvent(const FString& functionName, const FExter void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::runner thread) { - if (!ensureMsgf(!mbInitialized, TEXT("Thread already initialized!"))) - { - return; - } - mStartPath = path; mpRuntime = runtime; mbInitialized = true; mpRunner = thread; mpTags = NewObject(); + mTagFunctions.Reset(); + mCurrentChoices.Reset(); + mbHasRun = false; + mbInChoice = false; OnStartup(); } diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index f20c5e1c..ae5ea637 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -4,8 +4,10 @@ #include "CoreMinimal.h" #include "GameFramework/Actor.h" +#include "Misc/Optional.h" #include "InkDelegates.h" +#include "InkSnapshot.h" #include "ink/types.h" #include "ink/globals.h" @@ -15,7 +17,7 @@ class UInkThread; struct FInkVar; -struct FInkSnapshot; + namespace ink::runtime { class story; } UCLASS() @@ -28,23 +30,29 @@ class INKCPP_API AInkRuntime : public AActor AInkRuntime(); ~AInkRuntime(); + /** + * Create a new Thread. If a Snapshot is set/loaded create Threads like there was before + * if you want to create a fresh Thread despite an existing LoadedSnapshot enter the starting path + */ UFUNCTION(BlueprintCallable, Category="Ink") UInkThread* Start(TSubclassOf type, FString path = "", bool runImmediately = true); + /** + * Create a new Thread in existing memory, for more details \see AInkRuntime::Start() + */ UFUNCTION(BlueprintCallable, Category="Ink") UInkThread* StartExisting(UInkThread* thread, FString path = "", bool runImmediately = true); // only tested in choices moments UFUNCTION(BlueprintCallable, Category="Ink") - FInkSnapshot* Snapshot(); - - AInkRuntime(FInkSnapshot* snapshot); + FInkSnapshot Snapshot(); + /** + * Loads a snapshot file, therfore deletes globals and invalidate all current Threads + * After this Start and StartExisting will load the corresponding Threads (on at a time) + */ UFUNCTION(BlueprintCallable, Category="Ink") - UInkThread* LoadThread(TSubclassOf type, int id = 0); - - UFUNCTION(BlueprintCallable, Category="Ink") - UInkThread* LoadInThreadExisting(UInkThread* thread, int id = 0); + void LoadSnapshot(const FInkSnapshot& snapshot); // Marks a thread as "exclusive". As long as it is running, no other threads will update. UFUNCTION(BlueprintCallable, Category="Ink") @@ -75,7 +83,7 @@ class INKCPP_API AInkRuntime : public AActor virtual void Tick(float DeltaTime) override; // Story asset used in this level - UPROPERTY(EditAnywhere, Category="Put in correct category") + UPROPERTY(EditAnywhere, Category="Ink") class UInkAsset* InkAsset; private: @@ -89,4 +97,9 @@ class INKCPP_API AInkRuntime : public AActor UPROPERTY() TArray mExclusiveStack; + + // UPROPERTY(EditDefaultsOnly, Category="Ink") + TOptional mSnapshot; + // snapshot generates data when re-constructing the globals to allow reconstructing the threads + ink::runtime::snapshot* mpSnapshot; }; diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h b/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h new file mode 100644 index 00000000..abe3d61b --- /dev/null +++ b/unreal/inkcpp/Source/inkcpp/Public/InkSnapshot.h @@ -0,0 +1,14 @@ +#pragma once + +#include "InkSnapshot.generated.h" + +USTRUCT(BlueprintType) +struct INKCPP_API FInkSnapshot +{ + GENERATED_BODY() + FInkSnapshot() {} + FInkSnapshot(const char* snap_data, size_t snap_len) + : data(snap_data, snap_len) + {} + TArray data; +}; \ No newline at end of file diff --git a/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp b/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp index 45c8aa3d..878dc589 100644 --- a/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp +++ b/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp @@ -57,7 +57,12 @@ UObject* UInkAssetFactory::FactoryCreateFile(UClass* InClass, UObject* InParent, path_bin /= path(inklecate_cmd, path::format::generic_format).make_preferred(); path story_path(cFilename, path::format::generic_format); story_path.make_preferred(); - cFilename = std::tmpnam(nullptr); + char filename[1024]; + if(tmpnam_s(filename, 1024)) { + UE_LOG(InkCpp, Error, TEXT("Failed to create temporary file")); + return nullptr; + } + cFilename = filename; path json_path(cFilename, path::format::generic_format); json_path.make_preferred(); cmd From 7e245c2da9812c0574f700b9ebf688522a5be228 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sun, 30 Apr 2023 17:13:43 +0200 Subject: [PATCH 46/67] fixed dynimac tag list --- inkcpp/include/choice.h | 3 +++ inkcpp/include/runner.h | 3 +++ inkcpp_test/Tags.cpp | 17 +++++++++++++++++ shared/public/config.h | 2 +- unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h | 2 +- 5 files changed, 25 insertions(+), 2 deletions(-) diff --git a/inkcpp/include/choice.h b/inkcpp/include/choice.h index 9441e6d6..00ba5b59 100644 --- a/inkcpp/include/choice.h +++ b/inkcpp/include/choice.h @@ -43,6 +43,9 @@ namespace ink * @returns choice text as a string */ const char* text() const { return _text; } + + choice() : choice(0) {} + choice(int) : _text{nullptr}, _index{~0}, _path{~0u}, _thread{~0u} {} private: friend class internal::runner_impl; diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index 627ff9b8..2efb9c41 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -156,7 +156,10 @@ namespace ink::runtime */ virtual void choose(size_t index) = 0; + /** check if since last choice selection tags have been added */ virtual bool has_tags() const = 0; + /** return the number of tags accumulated since last choice + * order of tags wont change, and new are added at the end */ virtual size_t num_tags() const = 0; virtual const char* get_tag(size_t index) const = 0; diff --git a/inkcpp_test/Tags.cpp b/inkcpp_test/Tags.cpp index b75e54a6..8e800046 100644 --- a/inkcpp_test/Tags.cpp +++ b/inkcpp_test/Tags.cpp @@ -9,6 +9,23 @@ using namespace ink::runtime; +SCENARIO("tags", "[tags]") +{ + inklecate("ink/AHF.ink", "AHF.tmp"); + ink::compiler::run("AHF.tmp", "AHF.bin"); + auto ink = story::from_file("AHF.bin"); + runner thread = ink->new_runner(); + thread->move_to(ink::hash_string("test_knot")); + while(thread->can_continue()) { + auto line = thread->getline(); + std::cout << line << std::endl; + for ( ink::size_t i = 0; i < thread->num_tags(); ++i) { + std::cout << "\t" << thread->get_tag(i) << std::endl; + } + } + REQUIRE(thread->can_continue() == false); +} + SCENARIO("run story with tags", "[tags]") { GIVEN("a story with tags") diff --git a/shared/public/config.h b/shared/public/config.h index 209e461e..5e789582 100644 --- a/shared/public/config.h +++ b/shared/public/config.h @@ -19,7 +19,7 @@ namespace ink::config { static constexpr int limitEvalStackDepth = -20; static constexpr int limitContainerDepth = -20; /// number of simultaneous active tags - static constexpr int limitActiveTags = 10; + static constexpr int limitActiveTags = -10; // temporary variables and callstack; static constexpr int limitRuntimeStack = -20; // references and callstack diff --git a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h index ae5ea637..aeaad139 100644 --- a/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h +++ b/unreal/inkcpp/Source/inkcpp/Public/InkRuntime.h @@ -17,7 +17,6 @@ class UInkThread; struct FInkVar; - namespace ink::runtime { class story; } UCLASS() @@ -54,6 +53,7 @@ class INKCPP_API AInkRuntime : public AActor UFUNCTION(BlueprintCallable, Category="Ink") void LoadSnapshot(const FInkSnapshot& snapshot); + // Marks a thread as "exclusive". As long as it is running, no other threads will update. UFUNCTION(BlueprintCallable, Category="Ink") void PushExclusiveThread(UInkThread* Thread); From 0f873599ce91e6769cab63e7976c43ece7ac5ac9 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sun, 30 Apr 2023 17:14:00 +0200 Subject: [PATCH 47/67] add missing test file --- inkcpp_test/ink/AHF.ink | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 inkcpp_test/ink/AHF.ink diff --git a/inkcpp_test/ink/AHF.ink b/inkcpp_test/ink/AHF.ink new file mode 100644 index 00000000..eca26203 --- /dev/null +++ b/inkcpp_test/ink/AHF.ink @@ -0,0 +1,7 @@ +->test_knot +===test_knot +line 110#loc:loc-line-1#in#op:1#cl:1 +line 2#loc:loc-line-2#en#op:1#cl:1 +line 3#loc:loc-line-3#in#op:1#cl:1 +->DONE + From 43d08a5e62c09f6f5182be74280d276662afbe20 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Sun, 30 Apr 2023 17:55:21 +0200 Subject: [PATCH 48/67] Fixes PRNG and implement seed interface for runner --- inkcpp/include/runner.h | 6 ++++++ inkcpp/numeric_operations.h | 2 +- inkcpp/random.h | 12 +++++++----- inkcpp/runner_impl.cpp | 3 ++- inkcpp/runner_impl.h | 4 +++- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/inkcpp/include/runner.h b/inkcpp/include/runner.h index 2efb9c41..5863e8ec 100644 --- a/inkcpp/include/runner.h +++ b/inkcpp/include/runner.h @@ -33,6 +33,12 @@ namespace ink::runtime virtual ~runner_interface(){}; #pragma region Interface Methods + /** + * Sets seed for PRNG used in runner. + * Else runner is started with the current time as seed. + * @param seed seed to use for PRNG + */ + virtual void set_rng_seed(uint32_t seed) = 0; /** * Moves the runner to the specified path diff --git a/inkcpp/numeric_operations.h b/inkcpp/numeric_operations.h index ac5e3bdc..ba110aba 100644 --- a/inkcpp/numeric_operations.h +++ b/inkcpp/numeric_operations.h @@ -402,7 +402,7 @@ namespace ink::runtime::internal { void operator()(basic_eval_stack& stack, value* vals) { int min = casting::numeric_cast(vals[0]); int max = casting::numeric_cast(vals[0]); - stack.push(value{}.set(_prng.rand(max - min + 1) + min)); + stack.push(value{}.set(static_cast(_prng.rand(max - min + 1) + min))); } }; } diff --git a/inkcpp/random.h b/inkcpp/random.h index 81b590ef..518e3e27 100644 --- a/inkcpp/random.h +++ b/inkcpp/random.h @@ -4,27 +4,29 @@ namespace ink::runtime::internal { /** - * @brief pseudo random number generator based on Linear Congruential Generator. + * @brief pseudo random number generator based on Linear Congruential Generator (using glibc default values). */ class prng { static constexpr uint64_t C = 12345; static constexpr uint64_t A = 1103515245; - static constexpr uint64_t M = 1<<31; + static constexpr uint64_t M = 1ul<<31; public: - void srand(int32_t seed) { + void srand(uint32_t seed) { _x = seed; } uint32_t rand() { _x = static_cast((A*_x+ C) % M); return _x; } - int32_t rand(int32_t max) { + uint32_t rand(uint32_t max) { uint64_t prod = rand(); prod *= max; return static_cast(prod / M); } uint32_t get_state() const { return _x; } + prng(uint32_t seed) : _x{seed}{} + prng() : prng(1337) {} private: - uint32_t _x = 1337; + uint32_t _x; }; } diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 1052be8b..680ad25d 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -318,6 +318,7 @@ namespace ink::runtime::internal runner_impl::runner_impl(const story_impl* data, globals global) : _story(data), _globals(global.cast()), + _rng(time(NULL)), _operations( global.cast()->strings(), global.cast()->lists(), @@ -1215,7 +1216,7 @@ namespace ink::runtime::internal int sequenceLength = _eval.pop().get(); int index = _eval.pop().get(); - _eval.push(value{}.set(_rng.rand(sequenceLength))); + _eval.push(value{}.set(static_cast(_rng.rand(sequenceLength)))); } break; case Command::SEED: { diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index d4480d17..d200e31c 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -38,6 +38,8 @@ namespace ink::runtime::internal void mark_strings(string_table&) const; #pragma region runner Implementation + // sets seed for prng in runner + virtual void set_rng_seed(uint32_t seed) override { _rng.srand(seed); } // Checks that the runner can continue virtual bool can_continue() const override; @@ -258,7 +260,7 @@ namespace ink::runtime::internal bool _saved = false; - prng _rng{}; + prng _rng; }; template From 06ead58f6f82fd251e3bf177ea4ffc2ba8966066 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 1 May 2023 16:23:50 +0200 Subject: [PATCH 49/67] Make list available to modifications --- inkcpp/include/globals.h | 13 ++++++++ inkcpp/include/list.h | 65 ++++++++++++++++++++++++++++++++++++++ inkcpp/include/types.h | 8 +++-- inkcpp/list_impl.h | 67 ++++++++++++++++++++++++++++++++++++++++ inkcpp/list_table.h | 29 ++++++++++++++++- inkcpp/value.cpp | 9 +++++- inkcpp/value.h | 2 +- 7 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 inkcpp/include/list.h create mode 100644 inkcpp/list_impl.h diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index eb23d286..dfd01fa7 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -124,4 +124,17 @@ namespace ink::runtime inline bool globals_interface::set(const char* name, const char* const& val) { return set_var(hash_string(name), value(val)); } + + template<> + inline optional globals_interface::get(const char* name) const { + auto var = get_var(hash_string(name)); + if (var && var->type == value::Type::List) { + return {var->v_list}; + } + return nullopt; + } + template<> + inline bool globals_interface::set(const char* name, const list_interface& val) { + return set_var(hash_string(name), value(val)); + } } diff --git a/inkcpp/include/list.h b/inkcpp/include/list.h new file mode 100644 index 00000000..a5bd9e62 --- /dev/null +++ b/inkcpp/include/list.h @@ -0,0 +1,65 @@ +#pragma once + +#include "system.h" + +namespace ink::runtime { + namespace internal { + class list_table; + } + class list_interface { + public: + list_interface() : _list_table{nullptr}, _list{-1} {} + + class iterator { + const char* _flag_name; + const list_interface& _list; + int _i; + friend list_interface; + protected: + iterator(const char* flag_name, const list_interface& list, size_t i) + : _flag_name(flag_name), _list(list), _i(i) {} + public: + const char* operator*() const { return _flag_name; }; + iterator& operator++() { _list.next(_flag_name, _i); return *this; } + bool operator!=(const iterator& itr) const { return itr._i != _i; } + }; + + /** checks if a flag is contained in the list */ + virtual bool contains(const char* flag) const { + ink_assert(false, "Not implemented function from interfaces is called!"); return false; + }; + + /** add a flag to list */ + virtual void add(const char* flag) { + ink_assert(false, "Not implemented function from interface is called!"); + }; + + /** removes a flag from list */ + virtual void remove(const char* flag) { + ink_assert(false, "Not implemented function from interface is called!"); + }; + + /** begin iterator for contained flags in list */ + virtual iterator begin() const { + ink_assert(false, "Not implemented function from interface is called!"); + return new_iterator(nullptr, -1); + }; + /** end iterator for contained flags in list */ + virtual iterator end() const { + ink_assert(false, "Not implemented function from interface is called!"); + return new_iterator(nullptr, -1); }; + + private: + friend iterator; + virtual void next(const char*& flag_name, const int& i) const { + ink_assert(false, "Not implemented funciton from interface is called!"); + }; + protected: + iterator new_iterator(const char* flag_name, int i) const { + return iterator(flag_name, *this, i); + } + list_interface(internal::list_table& table, int list) : _list_table {&table}, _list{list} {} + internal::list_table* _list_table; + int _list; + }; +} diff --git a/inkcpp/include/types.h b/inkcpp/include/types.h index 0580df68..a14fe35c 100644 --- a/inkcpp/include/types.h +++ b/inkcpp/include/types.h @@ -1,5 +1,6 @@ #pragma once +#include "list.h" #include "story_ptr.h" namespace ink @@ -11,11 +12,12 @@ namespace ink::runtime { class globals_interface; class runner_interface; + class list_interface; class snapshot; typedef story_ptr globals; typedef story_ptr runner; - + struct value { union { bool v_bool; @@ -23,9 +25,10 @@ namespace ink::runtime int32_t v_int32; const char* v_string; float v_float; + list_interface v_list; }; enum class Type { - Bool, Uint32, Int32, String, Float + Bool, Uint32, Int32, String, Float, List } type; value() : v_uint32{0}, type{Type::Int32} {} value(bool v) : v_bool{v}, type{Type::Bool} {} @@ -33,5 +36,6 @@ namespace ink::runtime value(int32_t v) : v_int32{v}, type{Type::Int32} {} value(const char* v) : v_string{v}, type{Type::String} {} value(float v) : v_float{v}, type{Type::Float} {} + value(list_interface list) : v_list{list}, type{Type::List} {} }; } diff --git a/inkcpp/list_impl.h b/inkcpp/list_impl.h new file mode 100644 index 00000000..5c0f4bab --- /dev/null +++ b/inkcpp/list_impl.h @@ -0,0 +1,67 @@ +#pragma once + +#include "list.h" +#include "list_table.h" + +namespace ink::runtime::internal { + class list_impl : public list_interface { + public: + list_impl(list_table& table, list_table::list list) : list_interface(table, list.lid) {} + + bool contains(const char* flag_name) const override final { + auto flag = _list_table->toFlag(flag_name); + ink_assert(flag.has_value(), ("No flag with name found! '" + std::string(flag_name) + "'").c_str()); + return _list_table->has(list_table::list{_list}, *flag); + } + + void add(const char* flag_name) override final { + auto flag = _list_table->toFlag(flag_name); + ink_assert(flag.has_value(), ("No flag with name found to add! '" + std::string(flag_name) + "'").c_str()); + _list = _list_table->add(list_table::list{_list}, *flag).lid; + } + + void remove(const char* flag_name) override final { + auto flag = _list_table->toFlag(flag_name); + ink_assert(flag.has_value(), ("No flag with name found to remove! '" + std::string(flag_name) + "'").c_str()); + _list = _list_table->sub(list_table::list{_list}, *flag).lid; + } + + list_interface::iterator begin() const override final { + return ++new_iterator(nullptr, 0); + } + + list_interface::iterator end() const override final { + return new_iterator(nullptr, -1); + } + + private: + /// @todo wrong iteration order, first lists then flags + void next(const char*& flag_name, int& i) const { + if (i == -1) { return; } + + list_flag flag{.list_id = static_cast(i >> 16), .flag = static_cast(i & 0xFF)}; + if(flag_name != nullptr) { + ++flag.flag; + } + if (flag.flag >= _list_table->_list_end[flag.list_id]) { + next_list: + while(!_list_table->hasList(_list_table->getPtr(_list), flag.list_id)) { + ++flag.list_id; + if(flag.list_id >= _list_table->_list_end.size()) { + i = -1; + return; + } + } + } + while(!_list_table->has(list_table::list{_list}, flag)) { + ++flag.flag; + if(flag.flag >= _list_table->_list_end[flag.list_id]) { + goto next_list; + } + } + flag_name = _list_table->_flag_names[_list_table->toFid(flag)]; + i = (flag.list_id << 16) | flag.flag; + } + + }; +} diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index 0737d1f3..eef7ba79 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -13,6 +13,7 @@ namespace ink::internal { } namespace ink::runtime::internal { + class list_impl; class prng; // TODO: move to utils @@ -74,10 +75,35 @@ namespace ink::runtime::internal /** converts list to string representation * @param out char array with minimu size of stringLen(l) * @param l list to stringify - * @return pointer to end of insierted string + * @return pointer to end of inserted string */ char* toString(char* out, const list& l) const; + /** Finds flag id to flag name + * currently used a simple O(n) serach, for the expected number of flags should this be no problem + * @param flag_name null terminated string contaning the flag name + * @return list_flag with corresponding name + * @retval nullopt if no flag was found + */ + optional toFlag(const char* flag_name) const { + for(auto itr = _flag_names.begin(); itr != _flag_names.end(); ++itr) { + if (strcmp(*itr, flag_name) == 0) { + int fid = itr - _flag_names.begin(); + int lid = 0; + int begin = 0; + for(auto itr = _list_end.begin(); itr != _list_end.end(); ++itr) { + if(*itr > fid) { + lid = itr - _list_end.begin(); + break; + } + begin = *itr; + } + return {list_flag{.list_id = static_cast(lid), .flag = static_cast(fid - begin)}}; + } + } + return nullopt; + } + // snapshot interface implementation size_t snap(unsigned char* data, const snapper&) const override; const unsigned char* snap_load(const unsigned char* data, const loader&) override; @@ -235,6 +261,7 @@ namespace ink::runtime::internal bool _valid; public: friend class name_flag_itr; + friend list_impl; class named_flag_itr { const list_table& _list; const data_t* _data; diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index ab2549d5..a861a6b6 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -1,4 +1,6 @@ #include "value.h" + +#include "list_impl.h" #include "output.h" #include "list_table.h" #include "string_utils.h" @@ -101,13 +103,18 @@ namespace ink::runtime::internal return false; } - ink::runtime::value value::to_interface_value() const { + ink::runtime::value value::to_interface_value(list_table& table) const { using val = ink::runtime::value; if(type() == value_type::boolean) { return val(get()); } else if(type() == value_type::uint32) { return val(get()); } else if(type() == value_type::int32) { return val(get()); } else if(type() == value_type::string) { return val(get().str); } else if(type() == value_type::float32) { return val(get()); } + else if(type() == value_type::list_flag) { + auto lid = table.create(); + lid = table.add(lid, get()); + return val(new list_impl(table, lid)); + } else if(type() == value_type::list) { return val(new list_impl(table, get())) } inkFail("No valid type to convert to interface value!"); return val(); } diff --git a/inkcpp/value.h b/inkcpp/value.h index 410ea4eb..277c5916 100644 --- a/inkcpp/value.h +++ b/inkcpp/value.h @@ -98,7 +98,7 @@ namespace ink::runtime::internal { explicit value(const ink::runtime::value& val); bool set( const ink::runtime::value& val ); - ink::runtime::value to_interface_value() const; + ink::runtime::value to_interface_value(list_table&) const; /// get value of the type (if possible) template From 9abd00ec83e48268537566f318e87b51aa4ce1e2 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 1 May 2023 17:34:51 +0200 Subject: [PATCH 50/67] Access and modify lists, WITH memory leak --- inkcpp/globals_impl.cpp | 2 +- inkcpp/include/globals.h | 4 +-- inkcpp/include/list.h | 5 ++- inkcpp/include/types.h | 22 +++++++++---- inkcpp/list_impl.h | 12 ++++--- inkcpp/value.cpp | 6 +++- inkcpp_test/Lists.cpp | 62 ++++++++++++++++++++++++++++++++++- inkcpp_test/Tags.cpp | 4 --- inkcpp_test/ink/ListStory.ink | 4 ++- 9 files changed, 100 insertions(+), 21 deletions(-) diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index de63cd58..54abdf56 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -117,7 +117,7 @@ namespace ink::runtime::internal optional globals_impl::get_var(hash_t name) const { auto* var = get_variable(name); if (!var) { return nullopt; } - return {var->to_interface_value()}; + return {var->to_interface_value(_lists)}; } bool globals_impl::set_var(hash_t name, const ink::runtime::value& val) { diff --git a/inkcpp/include/globals.h b/inkcpp/include/globals.h index dfd01fa7..445d5d30 100644 --- a/inkcpp/include/globals.h +++ b/inkcpp/include/globals.h @@ -126,7 +126,7 @@ namespace ink::runtime } template<> - inline optional globals_interface::get(const char* name) const { + inline optional globals_interface::get(const char* name) const { auto var = get_var(hash_string(name)); if (var && var->type == value::Type::List) { return {var->v_list}; @@ -134,7 +134,7 @@ namespace ink::runtime return nullopt; } template<> - inline bool globals_interface::set(const char* name, const list_interface& val) { + inline bool globals_interface::set(const char* name, const list& val) { return set_var(hash_string(name), value(val)); } } diff --git a/inkcpp/include/list.h b/inkcpp/include/list.h index a5bd9e62..fd3310a3 100644 --- a/inkcpp/include/list.h +++ b/inkcpp/include/list.h @@ -22,6 +22,9 @@ namespace ink::runtime { const char* operator*() const { return _flag_name; }; iterator& operator++() { _list.next(_flag_name, _i); return *this; } bool operator!=(const iterator& itr) const { return itr._i != _i; } + bool operator==(const iterator& itr) const { + return itr._i == _i; + } }; /** checks if a flag is contained in the list */ @@ -51,7 +54,7 @@ namespace ink::runtime { private: friend iterator; - virtual void next(const char*& flag_name, const int& i) const { + virtual void next(const char*& flag_name, int& i) const { ink_assert(false, "Not implemented funciton from interface is called!"); }; protected: diff --git a/inkcpp/include/types.h b/inkcpp/include/types.h index a14fe35c..bff97dbf 100644 --- a/inkcpp/include/types.h +++ b/inkcpp/include/types.h @@ -12,11 +12,11 @@ namespace ink::runtime { class globals_interface; class runner_interface; - class list_interface; class snapshot; - typedef story_ptr globals; - typedef story_ptr runner; + using globals = story_ptr; + using runner = story_ptr; + using list = list_interface*; struct value { union { @@ -25,17 +25,27 @@ namespace ink::runtime int32_t v_int32; const char* v_string; float v_float; - list_interface v_list; + list v_list; }; enum class Type { Bool, Uint32, Int32, String, Float, List } type; - value() : v_uint32{0}, type{Type::Int32} {} + value() : v_int32{0}, type{Type::Int32} {} value(bool v) : v_bool{v}, type{Type::Bool} {} value(uint32_t v) : v_uint32{v}, type{Type::Uint32} {} value(int32_t v) : v_int32{v}, type{Type::Int32} {} value(const char* v) : v_string{v}, type{Type::String} {} value(float v) : v_float{v}, type{Type::Float} {} - value(list_interface list) : v_list{list}, type{Type::List} {} + value(list_interface* list) : v_list{list}, type{Type::List} {} + value(const value& v) : type{v.type} { + switch(type) { + case Type::Bool: v_bool = v.v_bool; break; + case Type::Uint32: v_uint32 = v.v_uint32; break; + case Type::Int32: v_int32 = v.v_int32; break; + case Type::String: v_string = v.v_string; break; + case Type::Float: v_float = v.v_float; break; + case Type::List: v_list = v.v_list; break; + } + } }; } diff --git a/inkcpp/list_impl.h b/inkcpp/list_impl.h index 5c0f4bab..a81fa081 100644 --- a/inkcpp/list_impl.h +++ b/inkcpp/list_impl.h @@ -35,8 +35,11 @@ namespace ink::runtime::internal { } private: + friend ink::runtime::internal::value; + list_table::list get_list() const { return list_table::list(_list); } + /// @todo wrong iteration order, first lists then flags - void next(const char*& flag_name, int& i) const { + void next(const char*& flag_name, int& i) const override final{ if (i == -1) { return; } list_flag flag{.list_id = static_cast(i >> 16), .flag = static_cast(i & 0xFF)}; @@ -45,17 +48,18 @@ namespace ink::runtime::internal { } if (flag.flag >= _list_table->_list_end[flag.list_id]) { next_list: - while(!_list_table->hasList(_list_table->getPtr(_list), flag.list_id)) { + flag.flag = 0; + do { ++flag.list_id; if(flag.list_id >= _list_table->_list_end.size()) { i = -1; return; } - } + } while(!_list_table->hasList(_list_table->getPtr(_list), flag.list_id)); } while(!_list_table->has(list_table::list{_list}, flag)) { ++flag.flag; - if(flag.flag >= _list_table->_list_end[flag.list_id]) { + if(flag.flag >= _list_table->_list_end[flag.list_id] - _list_table->listBegin(flag.list_id)) { goto next_list; } } diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index a861a6b6..b6803a38 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -91,6 +91,8 @@ namespace ink::runtime::internal case types::Float: set(val.v_float); break; + case types::List: + set(static_cast(val.v_list)->get_list()); } } @@ -114,7 +116,9 @@ namespace ink::runtime::internal auto lid = table.create(); lid = table.add(lid, get()); return val(new list_impl(table, lid)); - } else if(type() == value_type::list) { return val(new list_impl(table, get())) } + } else if(type() == value_type::list) { + return val(new list_impl(table, get())); + } inkFail("No valid type to convert to interface value!"); return val(); } diff --git a/inkcpp_test/Lists.cpp b/inkcpp_test/Lists.cpp index 8113b0f6..7123037b 100644 --- a/inkcpp_test/Lists.cpp +++ b/inkcpp_test/Lists.cpp @@ -7,6 +7,8 @@ #include #include +#include + using namespace ink::runtime; SCENARIO("run a story with lists", "[lists]") @@ -16,16 +18,74 @@ SCENARIO("run a story with lists", "[lists]") inklecate("ink/ListStory.ink", "ListStory.tmp"); ink::compiler::run("ListStory.tmp", "ListStory.bin"); auto ink = story::from_file("ListStory.bin"); - runner thread = ink->new_runner(); + globals globals = ink->new_globals(); + runner thread = ink->new_runner(globals); WHEN("just run") { std::string out = thread->getall(); std::string choice1 = thread->get_choice(0)->text(); + thread->choose(0); + std::string out2 = thread->getall(); THEN("should output expected") { REQUIRE(out == "cat, snake\n"); REQUIRE(choice1 == "list: bird, red, yellow"); + REQUIRE(out2 == "list: bird, red, yellow\ncat, snake\nbird, red, yellow\n"); + } + } + + WHEN("modify") + { + std::string out = thread->getall(); + std::string choice1 = thread->get_choice(0)->text(); + thread->choose(0); + + list l1 = *globals->get("list"); + l1->add("dog"); + l1->remove("red"); + REQUIRE(globals->set("list", l1)); + + l1 = *globals->get("animals"); + l1->add("bird"); + l1->add("dog"); + l1->remove("snake"); + l1->remove("cat"); + l1->add("cat"); + REQUIRE(globals->set("animals", l1)); + + std::string out2 = thread->getall(); + THEN("should output change") + { + REQUIRE(out == "cat, snake\n"); + REQUIRE(choice1 == "list: bird, red, yellow"); + // changing the list will also change the text of the repeated choice + REQUIRE(out2 == "list: bird, dog, yellow\nbird, cat, dog\nbird, dog, yellow\n"); + } + + THEN("list should contain things") + { + l1 = *globals->get("list"); + REQUIRE(l1->contains("bird")); + REQUIRE(l1->contains("dog")); + REQUIRE(l1->contains("yellow")); + + REQUIRE_FALSE(l1->contains("cat")); + REQUIRE_FALSE(l1->contains("snake")); + REQUIRE_FALSE(l1->contains("blue")); + REQUIRE_FALSE(l1->contains("white")); + } + + THEN("should iterate all contained flags") + { + l1 = *globals->get("list"); + for(const char* flag : *l1) { + INFO(flag); + REQUIRE((strcmp(flag, "bird") == 0 + ||strcmp(flag, "dog") == 0 + || strcmp(flag, "yellow") == 0 + )); + } } } } diff --git a/inkcpp_test/Tags.cpp b/inkcpp_test/Tags.cpp index 8e800046..166abe6f 100644 --- a/inkcpp_test/Tags.cpp +++ b/inkcpp_test/Tags.cpp @@ -18,10 +18,6 @@ SCENARIO("tags", "[tags]") thread->move_to(ink::hash_string("test_knot")); while(thread->can_continue()) { auto line = thread->getline(); - std::cout << line << std::endl; - for ( ink::size_t i = 0; i < thread->num_tags(); ++i) { - std::cout << "\t" << thread->get_tag(i) << std::endl; - } } REQUIRE(thread->can_continue() == false); } diff --git a/inkcpp_test/ink/ListStory.ink b/inkcpp_test/ink/ListStory.ink index 12ad6585..16d59599 100644 --- a/inkcpp_test/ink/ListStory.ink +++ b/inkcpp_test/ink/ListStory.ink @@ -2,5 +2,7 @@ LIST colors = blue, white, red, yellow LIST animals = bird, (cat), dog, (snake) VAR list = (bird, red, yellow) {animals} -* list: {list} +* list: {list} + {animals} + {list} * other From f64bb1516212a039fcaae032ee787771d63979d3 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 2 May 2023 09:52:40 +0200 Subject: [PATCH 51/67] Add GC for lists_table and for lists accessed with get_var --- inkcpp/CMakeLists.txt | 1 + inkcpp/globals_impl.cpp | 6 +++-- inkcpp/globals_impl.h | 1 + inkcpp/list_impl.cpp | 50 +++++++++++++++++++++++++++++++++++++ inkcpp/list_impl.h | 55 ++++++----------------------------------- inkcpp/list_table.cpp | 13 +++++++++- inkcpp/list_table.h | 7 ++++-- inkcpp/output.cpp | 4 ++- inkcpp/output.h | 4 +-- inkcpp/runner_impl.cpp | 10 ++++---- inkcpp/runner_impl.h | 2 +- inkcpp/stack.cpp | 12 ++++++--- inkcpp/stack.h | 6 ++--- inkcpp/value.cpp | 10 ++++---- shared/public/config.h | 5 ++++ 15 files changed, 113 insertions(+), 73 deletions(-) create mode 100644 inkcpp/list_impl.cpp diff --git a/inkcpp/CMakeLists.txt b/inkcpp/CMakeLists.txt index 86fdff12..d8d0b883 100644 --- a/inkcpp/CMakeLists.txt +++ b/inkcpp/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND SOURCES tuple.hpp string_table.h string_table.cpp avl_array.h list_table.h list_table.cpp + list_impl.h list_impl.cpp operations.h operation_bases.h list_operations.h list_operations.cpp container_operations.h container_operations.cpp diff --git a/inkcpp/globals_impl.cpp b/inkcpp/globals_impl.cpp index 54abdf56..d72bf37b 100644 --- a/inkcpp/globals_impl.cpp +++ b/inkcpp/globals_impl.cpp @@ -160,20 +160,22 @@ namespace ink::runtime::internal { // Mark all strings as unused _strings.clear_usage(); + _lists.clear_usage(); // Iterate runners and mark their strings auto iter = _runners_start; while (iter != nullptr) { - iter->object->mark_strings(_strings); + iter->object->mark_used(_strings, _lists); iter = iter->next; } // Mark our own strings - _variables.mark_strings(_strings); + _variables.mark_used(_strings, _lists); // run garbage collection _strings.gc(); + _lists.gc(); } void globals_impl::save() diff --git a/inkcpp/globals_impl.h b/inkcpp/globals_impl.h index 8bd5b963..1dbba732 100644 --- a/inkcpp/globals_impl.h +++ b/inkcpp/globals_impl.h @@ -5,6 +5,7 @@ #include "globals.h" #include "string_table.h" #include "list_table.h" +#include "list_impl.h" #include "stack.h" #include "snapshot_impl.h" diff --git a/inkcpp/list_impl.cpp b/inkcpp/list_impl.cpp new file mode 100644 index 00000000..c21c9a9b --- /dev/null +++ b/inkcpp/list_impl.cpp @@ -0,0 +1,50 @@ +#include "list_impl.h" +#include "list_table.h" + +namespace ink::runtime::internal { + bool list_impl::contains(const char* flag_name) const { + auto flag = _list_table->toFlag(flag_name); + ink_assert(flag.has_value(), ("No flag with name found! '" + std::string(flag_name) + "'").c_str()); + return _list_table->has(list_table::list{_list}, *flag); + } + + void list_impl::add(const char* flag_name) { + auto flag = _list_table->toFlag(flag_name); + ink_assert(flag.has_value(), ("No flag with name found to add! '" + std::string(flag_name) + "'").c_str()); + _list = _list_table->add(list_table::list{_list}, *flag).lid; + } + + void list_impl::remove(const char* flag_name) { + auto flag = _list_table->toFlag(flag_name); + ink_assert(flag.has_value(), ("No flag with name found to remove! '" + std::string(flag_name) + "'").c_str()); + _list = _list_table->sub(list_table::list{_list}, *flag).lid; + } + + void list_impl::next(const char*& flag_name, int& i) const { + if (i == -1) { return; } + + list_flag flag{.list_id = static_cast(i >> 16), .flag = static_cast(i & 0xFF)}; + if(flag_name != nullptr) { + ++flag.flag; + } + if (flag.flag >= _list_table->_list_end[flag.list_id]) { + next_list: + flag.flag = 0; + do { + ++flag.list_id; + if(flag.list_id >= _list_table->_list_end.size()) { + i = -1; + return; + } + } while(!_list_table->hasList(_list_table->getPtr(_list), flag.list_id)); + } + while(!_list_table->has(list_table::list{_list}, flag)) { + ++flag.flag; + if(flag.flag >= _list_table->_list_end[flag.list_id] - _list_table->listBegin(flag.list_id)) { + goto next_list; + } + } + flag_name = _list_table->_flag_names[_list_table->toFid(flag)]; + i = (flag.list_id << 16) | flag.flag; + } +} diff --git a/inkcpp/list_impl.h b/inkcpp/list_impl.h index a81fa081..ac53f735 100644 --- a/inkcpp/list_impl.h +++ b/inkcpp/list_impl.h @@ -1,30 +1,18 @@ #pragma once #include "list.h" -#include "list_table.h" namespace ink::runtime::internal { + class list_table; + class value; class list_impl : public list_interface { public: - list_impl(list_table& table, list_table::list list) : list_interface(table, list.lid) {} + list_impl(list_table& table, int lid) : list_interface(table, lid) {} + int get_lid() const { return _list; } - bool contains(const char* flag_name) const override final { - auto flag = _list_table->toFlag(flag_name); - ink_assert(flag.has_value(), ("No flag with name found! '" + std::string(flag_name) + "'").c_str()); - return _list_table->has(list_table::list{_list}, *flag); - } - - void add(const char* flag_name) override final { - auto flag = _list_table->toFlag(flag_name); - ink_assert(flag.has_value(), ("No flag with name found to add! '" + std::string(flag_name) + "'").c_str()); - _list = _list_table->add(list_table::list{_list}, *flag).lid; - } - - void remove(const char* flag_name) override final { - auto flag = _list_table->toFlag(flag_name); - ink_assert(flag.has_value(), ("No flag with name found to remove! '" + std::string(flag_name) + "'").c_str()); - _list = _list_table->sub(list_table::list{_list}, *flag).lid; - } + bool contains(const char* flag_name) const override final; + void add(const char* flag_name) override final; + void remove(const char* flag_name) override final; list_interface::iterator begin() const override final { return ++new_iterator(nullptr, 0); @@ -36,36 +24,9 @@ namespace ink::runtime::internal { private: friend ink::runtime::internal::value; - list_table::list get_list() const { return list_table::list(_list); } /// @todo wrong iteration order, first lists then flags - void next(const char*& flag_name, int& i) const override final{ - if (i == -1) { return; } - - list_flag flag{.list_id = static_cast(i >> 16), .flag = static_cast(i & 0xFF)}; - if(flag_name != nullptr) { - ++flag.flag; - } - if (flag.flag >= _list_table->_list_end[flag.list_id]) { - next_list: - flag.flag = 0; - do { - ++flag.list_id; - if(flag.list_id >= _list_table->_list_end.size()) { - i = -1; - return; - } - } while(!_list_table->hasList(_list_table->getPtr(_list), flag.list_id)); - } - while(!_list_table->has(list_table::list{_list}, flag)) { - ++flag.flag; - if(flag.flag >= _list_table->_list_end[flag.list_id] - _list_table->listBegin(flag.list_id)) { - goto next_list; - } - } - flag_name = _list_table->_flag_names[_list_table->toFid(flag)]; - i = (flag.list_id << 16) | flag.flag; - } + void next(const char*& flag_name, int& i) const override final; }; } diff --git a/inkcpp/list_table.cpp b/inkcpp/list_table.cpp index 76304f9f..a988ab0a 100644 --- a/inkcpp/list_table.cpp +++ b/inkcpp/list_table.cpp @@ -3,6 +3,7 @@ #include "header.h" #include "random.h" #include "string_utils.h" +#include "list_impl.h" #ifdef INK_ENABLE_STL #include @@ -78,7 +79,9 @@ namespace ink::runtime::internal } void list_table::mark_used(list l) { - _entry_state[l.lid] = state::used; + if (_entry_state[l.lid] == state::unused) { + _entry_state[l.lid] = state::used; + } } void list_table::gc() { @@ -91,6 +94,7 @@ namespace ink::runtime::internal } } } + _list_handouts.clear(); } int list_table::toFid(list_flag e) const { @@ -620,6 +624,13 @@ namespace ink::runtime::internal return res; } + list_interface* list_table::handout_list(list l) { + static_assert(sizeof(list_interface) == sizeof(list_impl)); + auto& res = _list_handouts.push(); + new(&res) list_impl(*this, l.lid); + return &res; + } + #ifdef INK_ENABLE_STL std::ostream& list_table::write(std::ostream& os, list l) const { bool first = true; diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index eef7ba79..de205dd5 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -13,7 +13,6 @@ namespace ink::internal { } namespace ink::runtime::internal { - class list_impl; class prng; // TODO: move to utils @@ -177,6 +176,7 @@ namespace ink::runtime::internal } list range(list l, int min, int max); + list_interface* handout_list(list); private: void copy_lists(const data_t* src, data_t* dst); static constexpr int bits_per_data = sizeof(data_t) * 8; @@ -257,11 +257,13 @@ namespace ink::runtime::internal managed_array _list_end; managed_array _flag_names; managed_array _list_names; + /// keep track over lists accessed with get_var, and clear then at gc time + managed_array _list_handouts; bool _valid; public: friend class name_flag_itr; - friend list_impl; + friend class list_impl; class named_flag_itr { const list_table& _list; const data_t* _data; @@ -332,6 +334,7 @@ namespace ink::runtime::internal named_flag_itr(*this, f)}; return res; } + #ifdef INK_ENABLE_STL std::ostream& write(std::ostream&,list) const; #endif diff --git a/inkcpp/output.cpp b/inkcpp/output.cpp index 0f399f4d..de70ce5a 100644 --- a/inkcpp/output.cpp +++ b/inkcpp/output.cpp @@ -429,7 +429,7 @@ namespace ink::runtime::internal _size = 0; } - void basic_stream::mark_strings(string_table& strings) const + void basic_stream::mark_used(string_table& strings, list_table& lists) const { // Find all allocated strings and mark them as used for (size_t i = 0; i < _size; i++) @@ -439,6 +439,8 @@ namespace ink::runtime::internal if (str.allocated) { strings.mark_used(str.str); } + } else if (_data[i].type() == value_type::list) { + lists.mark_used(_data[i].get()); } } } diff --git a/inkcpp/output.h b/inkcpp/output.h index 56b680e7..9d9c7e3c 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -77,8 +77,8 @@ namespace ink // Clears the whole stream void clear(); - // Marks strings that are in use - void mark_strings(string_table&) const; + // Marks strings and lists that are in use + void mark_used(string_table&, list_table&) const; // = Save/Restore void save(); diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 680ad25d..0fdb3a63 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -1306,13 +1306,13 @@ namespace ink::runtime::internal _container.clear(); } - void runner_impl::mark_strings(string_table& strings) const + void runner_impl::mark_used(string_table& strings, list_table& lists) const { // Find strings in output and stacks - _output.mark_strings(strings); - _stack.mark_strings(strings); - // ref_stack has no strings! - _eval.mark_strings(strings); + _output.mark_used(strings, lists); + _stack.mark_used(strings, lists); + // ref_stack has no strings and lists! + _eval.mark_used(strings, lists); // Take into account choice text for (size_t i = 0; i < _choices.size(); i++) diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index d200e31c..aa804f79 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -35,7 +35,7 @@ namespace ink::runtime::internal virtual ~runner_impl(); // used by the globals object to do garbage collection - void mark_strings(string_table&) const; + void mark_used(string_table&, list_table&) const; #pragma region runner Implementation // sets seed for prng in runner diff --git a/inkcpp/stack.cpp b/inkcpp/stack.cpp index e041728f..ef5bcde2 100644 --- a/inkcpp/stack.cpp +++ b/inkcpp/stack.cpp @@ -368,13 +368,15 @@ namespace ink::runtime::internal base::clear(); } - void basic_stack::mark_strings(string_table& strings) const + void basic_stack::mark_used(string_table& strings, list_table& lists) const { // Mark all strings base::for_each_all( - [&strings](const entry& elem) { + [&strings, &lists](const entry& elem) { if (elem.data.type() == value_type::string) { strings.mark_used(elem.data.get()); + } else if (elem.data.type() == value_type::list) { + lists.mark_used(elem.data.get()); } }); } @@ -532,15 +534,17 @@ namespace ink::runtime::internal base::clear(); } - void basic_eval_stack::mark_strings(string_table& strings) const + void basic_eval_stack::mark_used(string_table& strings, list_table& lists) const { // Iterate everything (including what we have saved) and mark strings - base::for_each_all([&strings](const value& elem) { + base::for_each_all([&strings,&lists](const value& elem) { if (elem.type() == value_type::string) { string_type str = elem.get(); if (str.allocated) { strings.mark_used(str.str); } + } else if (elem.type() == value_type::list) { + lists.mark_used(elem.get()); } }); } diff --git a/inkcpp/stack.h b/inkcpp/stack.h index 652f6d3c..f7f418f4 100644 --- a/inkcpp/stack.h +++ b/inkcpp/stack.h @@ -57,7 +57,7 @@ namespace ink void clear(); // Garbage collection - void mark_strings(string_table&) const; + void mark_used(string_table&, list_table&) const; // == Threading == @@ -163,8 +163,8 @@ namespace ink // Clear stack void clear(); - // Garbage collection - void mark_strings(string_table&) const; + /** Mark used strings and lists for garbage collection */ + void mark_used(string_table&, list_table&) const; // == Save/Restore == void save(); diff --git a/inkcpp/value.cpp b/inkcpp/value.cpp index b6803a38..3a8b9af5 100644 --- a/inkcpp/value.cpp +++ b/inkcpp/value.cpp @@ -92,7 +92,7 @@ namespace ink::runtime::internal set(val.v_float); break; case types::List: - set(static_cast(val.v_list)->get_list()); + set(list_table::list{static_cast(val.v_list)->get_lid()}); } } @@ -113,11 +113,11 @@ namespace ink::runtime::internal else if(type() == value_type::string) { return val(get().str); } else if(type() == value_type::float32) { return val(get()); } else if(type() == value_type::list_flag) { - auto lid = table.create(); - lid = table.add(lid, get()); - return val(new list_impl(table, lid)); + auto v = table.create(); + v = table.add(v, get()); + return val(table.handout_list(v)); } else if(type() == value_type::list) { - return val(new list_impl(table, get())); + return val(table.handout_list(get())); } inkFail("No valid type to convert to interface value!"); return val(); diff --git a/shared/public/config.h b/shared/public/config.h index 5e789582..1263e63d 100644 --- a/shared/public/config.h +++ b/shared/public/config.h @@ -18,6 +18,11 @@ namespace ink::config { static constexpr int limitThreadDepth = -10; static constexpr int limitEvalStackDepth = -20; static constexpr int limitContainerDepth = -20; + /** number of lists which can be accessed with get_var + * before the story must continue + * @attention list vars are only valid until the story continous! + */ + static constexpr int limitEditableLists = -5; /// number of simultaneous active tags static constexpr int limitActiveTags = -10; // temporary variables and callstack; From 0ac3a96a654d55799a4e5ad018ed0aca3bbea878 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Tue, 2 May 2023 10:03:33 +0200 Subject: [PATCH 52/67] Bump C++ version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 59541441..ef3116bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ enable_testing() # Project setup project(inkcpp VERSION 0.1) -SET(CMAKE_CXX_STANDARD 17) +SET(CMAKE_CXX_STANDARD 20) SET(CMAKE_CXX_STANDARD_REQUIRED ON) SET(CMAKE_INSTALL_LIBRARY_DIR lib) SET(CMAKE_INSTALL_INCLUDE_DIR include) From 9a70126558dbcace50ba997f747c4a86ddcd37b5 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 26 Jun 2023 15:13:50 +0200 Subject: [PATCH 53/67] Add support for tagged choices and new tag syntax --- inkcpp/choice.cpp | 71 ++++++++++++++++--------------- inkcpp/include/choice.h | 21 ++++++++- inkcpp/runner_impl.cpp | 41 ++++++++++++++++-- inkcpp/runner_impl.h | 2 + inkcpp_cl/inkcpp_cl.cpp | 9 +++- inkcpp_compiler/command.cpp | 4 +- inkcpp_compiler/json_compiler.cpp | 9 +++- inkcpp_compiler/json_compiler.h | 1 + inkcpp_test/Tags.cpp | 65 +++++++++++++++++++--------- inkcpp_test/ink/TagsStory.ink | 7 +-- shared/private/command.h | 2 + shared/public/system.h | 1 + 12 files changed, 168 insertions(+), 65 deletions(-) diff --git a/inkcpp/choice.cpp b/inkcpp/choice.cpp index 735b0c3d..005bb166 100644 --- a/inkcpp/choice.cpp +++ b/inkcpp/choice.cpp @@ -1,45 +1,48 @@ #include "choice.h" + #include "output.h" -#include "string_utils.h" #include "string_table.h" +#include "string_utils.h" -namespace ink -{ - namespace runtime +namespace ink { +namespace runtime { + choice& choice::setup( internal::basic_stream& in, internal::string_table& strings, internal::list_table& lists, int index, uint32_t path, thread_t thread, const char* const* tags ) { - choice& choice::setup(internal::basic_stream& in, internal::string_table& strings, internal::list_table& lists, int index, uint32_t path, thread_t thread) + char* text = nullptr; + // if we only have one item in our output stream + if ( in.queued() == 2 ) { - char* text = nullptr; - // if we only have one item in our output stream - if (in.queued() == 2) - { - // If it's a string, just grab it. Otherwise, use allocation - const internal::value& data = in.peek(); - switch (data.type()) - { - case internal::value_type::string: - text = strings.duplicate(data.get()); - in.discard(2); - break; - default: - text = in.get_alloc(strings, lists); - } - } - else + // If it's a string, just grab it. Otherwise, use allocation + const internal::value& data = in.peek(); + switch ( data.type() ) { - // Non-string. Must allocate - text = in.get_alloc(strings, lists); + case internal::value_type::string: + text = strings.duplicate( data.get() ); + in.discard( 2 ); + break; + default: + text = in.get_alloc( strings, lists ); } - char* end = text; - while(*end) { ++end; } - end = ink::runtime::internal::clean_string(text, end); - *end = 0; - _text = text; - // Index/path - _index = index; - _path = path; - _thread = thread; - return *this; } + else + { + // Non-string. Must allocate + text = in.get_alloc( strings, lists ); + } + char* end = text; + while ( *end ) + { + ++end; + } + end = ink::runtime::internal::clean_string( text, end ); + *end = 0; + _text = text; + // Index/path + _index = index; + _path = path; + _thread = thread; + _tags = tags; + return *this; } } +} // namespace ink::runtime diff --git a/inkcpp/include/choice.h b/inkcpp/include/choice.h index 00ba5b59..3d9ccf83 100644 --- a/inkcpp/include/choice.h +++ b/inkcpp/include/choice.h @@ -45,13 +45,30 @@ namespace ink const char* text() const { return _text; } choice() : choice(0) {} - choice(int) : _text{nullptr}, _index{~0}, _path{~0u}, _thread{~0u} {} + choice(int) : _tags{nullptr}, _text{nullptr}, _index{~0}, _path{~0u}, _thread{~0u} {} + + bool has_tags() const { return _tags != nullptr; } + size_t num_tags() const + { + size_t i = 0; + if (has_tags()) while ( _tags[i] != nullptr ) + { + ++i; + }; + return i; + } + const char* get_tag(size_t index) const { + return _tags[index]; + } + private: friend class internal::runner_impl; uint32_t path() const { return _path; } - choice& setup(internal::basic_stream&, internal::string_table& strings, internal::list_table& lists, int index, uint32_t path, thread_t thread); + choice& setup( internal::basic_stream&, internal::string_table& strings, internal::list_table& lists, int index, uint32_t path, thread_t thread, const char* const* tags ); + private: + const char* const* _tags; const char* _text; int _index; uint32_t _path; diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 123c4d67..27d89ed8 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -6,6 +6,7 @@ #include "header.h" #include "string_utils.h" #include "snapshot_impl.h" +#include "value.h" namespace ink::runtime { @@ -164,6 +165,7 @@ namespace ink::runtime::internal void runner_impl::clear_tags() { _tags.clear(); + _choice_tags_begin = -1; } void runner_impl::jump(ip_t dest, bool record_visits) @@ -330,6 +332,7 @@ namespace ink::runtime::internal { _ptr = _story->instructions(); _evaluation_mode = false; + _choice_tags_begin = -1; // register with globals _globals->add_runner(this); @@ -526,7 +529,7 @@ namespace ink::runtime::internal size_t runner_impl::num_tags() const { - return _tags.size(); + return _choice_tags_begin < 0 ? _tags.size() : _choice_tags_begin; } const char* runner_impl::get_tag(size_t index) const @@ -549,6 +552,7 @@ namespace ink::runtime::internal ptr = snap_write(ptr, _done, should_write); ptr = snap_write(ptr, _rng.get_state(), should_write); ptr = snap_write(ptr, _evaluation_mode, should_write); + ptr = snap_write(ptr, _string_mode, should_write); ptr = snap_write(ptr, _saved_evaluation_mode, should_write); ptr = snap_write(ptr, _saved, should_write); ptr = snap_write(ptr, _is_falling, should_write); @@ -556,6 +560,7 @@ namespace ink::runtime::internal ptr += _stack.snap(data ? ptr : nullptr, snapper); ptr += _ref_stack.snap(data ? ptr : nullptr, snapper); ptr += _eval.snap(data ? ptr : nullptr, snapper); + ptr = snap_write(ptr, _choice_tags_begin, should_write); ptr = snap_write(ptr, _tags.size(), should_write); for (const auto& tag : _tags) { std::uintptr_t offset = tag - snapper.story_string_table; @@ -600,6 +605,9 @@ namespace ink::runtime::internal ptr = _stack.snap_load(ptr, loader); ptr = _ref_stack.snap_load(ptr, loader); ptr = _eval.snap_load(ptr, loader); + int choice_tags_begin; + ptr = snap_read(ptr, choice_tags_begin); + _choice_tags_begin = choice_tags_begin; size_t num_tags; ptr = snap_read(ptr, num_tags); for(size_t i = 0; i < num_tags; ++i) { @@ -1082,6 +1090,7 @@ namespace ink::runtime::internal case Command::START_STR: { inkAssert(_evaluation_mode, "Can not enter string mode while not in evaluation mode!"); + _string_mode = true; _evaluation_mode = false; _output << values::marker; } break; @@ -1089,6 +1098,7 @@ namespace ink::runtime::internal { // TODO: Assert we really had a marker on there? inkAssert(!_evaluation_mode, "Must be in evaluation mode"); + _string_mode = false; _evaluation_mode = true; // Load value from output stream @@ -1098,6 +1108,20 @@ namespace ink::runtime::internal _globals->lists()))); } break; + case Command::START_TAG: + { + _output << values::marker; + } break; + + case Command::END_TAG: + { + auto tag = _output.get_alloc(_globals->strings(), _globals->lists()); + if(_string_mode && _choice_tags_begin < 0) { + _choice_tags_begin = _tags.size(); + } + _tags.push() = tag; + } break; + // == Choice commands case Command::CHOICE: { @@ -1141,12 +1165,19 @@ namespace ink::runtime::internal } for(;sc;--sc) { _output << stack[sc-1]; } + // fetch relevant tags + const char* const* tags = nullptr; + if (_choice_tags_begin >= 0 && _tags[_tags.size()-1] != nullptr) { + for(tags = _tags.end() - 1; *(tags-1) != nullptr && (tags - _tags.begin()) > _choice_tags_begin; --tags); + _tags.push() = nullptr; + } + // Create choice and record it if (flag & CommandFlag::CHOICE_IS_INVISIBLE_DEFAULT) { _fallback_choice - = choice{}.setup(_output, _globals->strings(), _globals->lists(), _choices.size(), path, current_thread()); + = choice{}.setup(_output, _globals->strings(), _globals->lists(), _choices.size(), path, current_thread(), tags); } else { - add_choice().setup(_output, _globals->strings(), _globals->lists(), _choices.size(), path, current_thread()); + add_choice().setup(_output, _globals->strings(), _globals->lists(), _choices.size(), path, current_thread(), tags); } // save stack at last choice if(_saved) { forget(); } @@ -1314,6 +1345,10 @@ namespace ink::runtime::internal // ref_stack has no strings and lists! _eval.mark_used(strings, lists); + // Take into account tags + for (size_t i = 0; i < _tags.size(); ++i) { + strings.mark_used(_tags[i]); + } // Take into account choice text for (size_t i = 0; i < _choices.size(); i++) strings.mark_used(_choices[i]._text); diff --git a/inkcpp/runner_impl.h b/inkcpp/runner_impl.h index aa804f79..590d4945 100644 --- a/inkcpp/runner_impl.h +++ b/inkcpp/runner_impl.h @@ -237,6 +237,7 @@ namespace ink::runtime::internal // Evaluation stack bool _evaluation_mode = false; + bool _string_mode = false; internal::eval_stack _eval; bool _saved_evaluation_mode = false; @@ -250,6 +251,7 @@ namespace ink::runtime::internal // Tag list managed_array _tags; + int _choice_tags_begin; // TODO: Move to story? Both? functions _functions; diff --git a/inkcpp_cl/inkcpp_cl.cpp b/inkcpp_cl/inkcpp_cl.cpp index ff6d77bb..d493988b 100644 --- a/inkcpp_cl/inkcpp_cl.cpp +++ b/inkcpp_cl/inkcpp_cl.cpp @@ -175,7 +175,14 @@ int main(int argc, const char** argv) int index = 1; for (const ink::runtime::choice& c : *thread) { - std::cout << index++ << ": " << c.text() << std::endl; + std::cout << index++ << ": " << c.text(); + if(c.has_tags()) { + std::cout << "\n\t"; + for(size_t i = 0; i < c.num_tags(); ++i) { + std::cout << "# " << c.get_tag(i) << " "; + } + } + std::cout << std::endl; } int c = 0; diff --git a/inkcpp_compiler/command.cpp b/inkcpp_compiler/command.cpp index 496734da..47a2b114 100644 --- a/inkcpp_compiler/command.cpp +++ b/inkcpp_compiler/command.cpp @@ -14,7 +14,7 @@ namespace ink "\n", "<>", "void", - "#", + "inkcpp_TAG", "inkcpp_DIVERT", "inkcpp_DIVERT_TO_VARIABLE", "inkcpp_TUNNEL", @@ -40,6 +40,8 @@ namespace ink "str", "/str", + "#", + "/#", "inkcpp_CHOICE", "thread", diff --git a/inkcpp_compiler/json_compiler.cpp b/inkcpp_compiler/json_compiler.cpp index 77c4c080..5f28fced 100644 --- a/inkcpp_compiler/json_compiler.cpp +++ b/inkcpp_compiler/json_compiler.cpp @@ -1,6 +1,8 @@ #include "json_compiler.h" #include "list_data.h" +#include "system.h" +#include "version.h" #include #include @@ -19,7 +21,7 @@ namespace ink::compiler::internal void json_compiler::compile(const nlohmann::json& input, emitter* output, compilation_results* results) { // Get the runtime version - int inkVersion = input["inkVersion"]; + _ink_version = input["inkVersion"]; // TODO: Do something with version number // Start the output @@ -27,7 +29,7 @@ namespace ink::compiler::internal _emitter = output; // Initialize emitter - _emitter->start(inkVersion, results); + _emitter->start(_ink_version, results); if(auto itr = input.find("listDefs"); itr != input.end()) { compile_lists_definition(*itr); @@ -396,6 +398,9 @@ namespace ink::compiler::internal else if (get(command, "#", val)) { + if (_ink_version > 20) { + ink_exception("with inkVerison 21 the tag system chages, and the '#: ' is deprecated now"); + } _emitter->write_string(Command::TAG, CommandFlag::NO_FLAGS, val); } diff --git a/inkcpp_compiler/json_compiler.h b/inkcpp_compiler/json_compiler.h index 199a6965..5179ea40 100644 --- a/inkcpp_compiler/json_compiler.h +++ b/inkcpp_compiler/json_compiler.h @@ -51,5 +51,6 @@ namespace ink::compiler::internal container_t _next_container_index; list_data _list_meta; + int _ink_version; }; } diff --git a/inkcpp_test/Tags.cpp b/inkcpp_test/Tags.cpp index 166abe6f..ffbf222c 100644 --- a/inkcpp_test/Tags.cpp +++ b/inkcpp_test/Tags.cpp @@ -56,26 +56,34 @@ SCENARIO("run story with tags", "[tags]") { auto itr = thread->begin(); std::string choices[2] = { - (itr++)->text(), - (itr++)->text() + itr[0].text(), + itr[1].text() }; - THEN("choices won't print tags, tags are still the same") + THEN("choices won't print tags, tags are still the same, but they can contain tags") { REQUIRE(choices[0] == "a"); REQUIRE(choices[1] == "b"); - REQUIRE(thread->has_tags() == true); + REQUIRE(thread->has_tags()); REQUIRE(thread->num_tags() == 4); REQUIRE(std::string(thread->get_tag(0)) == "global_tag"); REQUIRE(std::string(thread->get_tag(1)) == "knot_tag_start"); REQUIRE(std::string(thread->get_tag(2)) == "second_knot_tag_start"); REQUIRE(std::string(thread->get_tag(3)) == "output_tag_h"); + + REQUIRE_FALSE(itr[0].has_tags()); + REQUIRE(itr[0].num_tags() == 0); + REQUIRE(itr[1].has_tags()); + + REQUIRE(itr[1].num_tags() == 2); + REQUIRE(std::string(itr[1].get_tag(0)) == "choice_tag_b"); + REQUIRE(std::string(itr[1].get_tag(1)) == "choice_tag_b_2"); } WHEN("choose divert") { thread->choose(1); THEN("choosing won't add tags!") { - REQUIRE(thread->has_tags() == false); + REQUIRE_FALSE(thread->has_tags()); REQUIRE(thread->num_tags() == 0); } WHEN("proceed") @@ -83,22 +91,41 @@ SCENARIO("run story with tags", "[tags]") std::string line = thread->getall(); THEN("new knot tag and now line tag, also choice tag. AND dont print tag in choice") { - REQUIRE(line == "bKnot2\n"); - REQUIRE(thread->has_tags() == true); - REQUIRE(thread->num_tags() == 3); - REQUIRE(std::string(thread->get_tag(0)) == "choice_tag_b"); - REQUIRE(std::string(thread->get_tag(1)) == "knot_tag_2"); - REQUIRE(std::string(thread->get_tag(2)) == "output_tag_k"); + REQUIRE(line == "Knot2\n"); + REQUIRE(thread->has_tags()); + REQUIRE(thread->num_tags() == 2); + REQUIRE(std::string(thread->get_tag(0)) == "knot_tag_2"); + REQUIRE(std::string(thread->get_tag(1)) == "output_tag_k"); + + auto itr = thread->begin(); + REQUIRE(std::string(itr[0].text()) == "e"); + REQUIRE(std::string(itr[1].text()) == "f with detail"); + REQUIRE(std::string(itr[2].text()) == "g"); + + REQUIRE_FALSE(itr[0].has_tags()); + REQUIRE(itr[0].num_tags() == 0); + REQUIRE(itr[1].has_tags()); + REQUIRE(itr[1].num_tags() == 4); + REQUIRE(std::string(itr[1].get_tag(0)) == "shared_tag"); + REQUIRE(std::string(itr[1].get_tag(1)) == "shared_tag_2"); + REQUIRE(std::string(itr[1].get_tag(2)) == "choice_tag"); + REQUIRE(std::string(itr[1].get_tag(3)) == "choice_tag_2"); + REQUIRE(itr[2].has_tags()); + REQUIRE(itr[2].num_tags() == 1); } - WHEN("choose choice without tag, and proceed to end") + WHEN("choose choice with tag, and proceed to end") { - thread->choose(0); - thread->getall(); - THEN("no tags, tags behind END are ignored") - { - REQUIRE(thread->has_tags() == false); - REQUIRE(thread->num_tags() == 0); - } + thread->choose(1); + auto line = thread->getall(); + + REQUIRE(line == "f and content\nout"); + REQUIRE(thread->has_tags()); + REQUIRE(thread->num_tags() == 5); + REQUIRE(std::string(thread->get_tag(0)) == "shared_tag"); + REQUIRE(std::string(thread->get_tag(1)) == "shared_tag_2"); + REQUIRE(std::string(thread->get_tag(2)) == "content_tag"); + REQUIRE(std::string(thread->get_tag(3)) == "content_tag_2"); + REQUIRE(std::string(thread->get_tag(4)) == "close_tag"); } } } diff --git a/inkcpp_test/ink/TagsStory.ink b/inkcpp_test/ink/TagsStory.ink index f15afc31..e4169eac 100644 --- a/inkcpp_test/ink/TagsStory.ink +++ b/inkcpp_test/ink/TagsStory.ink @@ -6,7 +6,7 @@ Hello # output_tag_h * a -* b->knot2 # choice_tag_b +* [b # choice_tag_b # choice_tag_b_2]->knot2 - World! # output_tag_w * c # choice_tag_c * d # choice_tag_d @@ -17,5 +17,6 @@ Hello # output_tag_h Knot2 # output_tag_k * e -* f # choice_tag_f -- out->END # close_tag +* f #shared_tag # shared_tag_2 [ with detail #choice_tag #choice_tag_2] and content # content_tag # content_tag_2 +* g # choice_tag_g +- out # close_tag->END diff --git a/shared/private/command.h b/shared/private/command.h index 15465377..ac266791 100644 --- a/shared/private/command.h +++ b/shared/private/command.h @@ -49,6 +49,8 @@ namespace ink // == String stack START_STR, END_STR, + START_TAG, + END_TAG, // == Choice commands CHOICE, diff --git a/shared/public/system.h b/shared/public/system.h index 32721230..84004ed5 100644 --- a/shared/public/system.h +++ b/shared/public/system.h @@ -14,6 +14,7 @@ #include #include #include +#include #endif namespace ink From 49aa9cf8c24cff718a3ac9929f7ea0ad02b12bbe Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Mon, 26 Jun 2023 15:19:57 +0200 Subject: [PATCH 54/67] bump inklecate version --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b4f77be8..262dde8e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,17 +18,17 @@ jobs: - os: macos-latest artifact: macos name: MacOSX - inklecate_url: https://github.com/inkle/ink/releases/download/v1.0.0/inklecate_mac.zip + inklecate_url: https://github.com/inkle/ink/releases/download/v1.1.1/inklecate_mac.zip proof: false - os: windows-latest artifact: win64 name: Windows x64 - inklecate_url: https://github.com/inkle/ink/releases/download/v1.0.0/inklecate_windows.zip + inklecate_url: https://github.com/inkle/ink/releases/download/v1.1.1/inklecate_windows.zip proof: false - os: "ubuntu-20.04" artifact: linux name: Linux x64 - inklecate_url: https://github.com/inkle/ink/releases/download/v1.0.0/inklecate_linux.zip + inklecate_url: https://github.com/inkle/ink/releases/download/v1.1.1/inklecate_linux.zip proof: true steps: From 0df2392481d3fe4ffa251a741a6f6abc73a70915 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Fri, 7 Jul 2023 17:13:21 +0200 Subject: [PATCH 55/67] Fixes for UE5.2 * choices tags not yet implemented in UE * Lists not tested in UE * fixes wrong FString to UTF8 conversion --- inkcpp/array.h | 4 +- inkcpp/functional.cpp | 12 +- inkcpp/functions.cpp | 4 +- inkcpp/functions.h | 2 +- inkcpp/include/functional.h | 35 +- inkcpp/include/list.h | 37 +- inkcpp/include/traits.h | 9 +- inkcpp/list_impl.cpp | 10 +- inkcpp/list_impl.h | 2 +- inkcpp/list_table.h | 14 +- inkcpp/output.h | 2 +- inkcpp/runner_impl.cpp | 5 +- inkcpp/string_table.h | 4 +- inkcpp/system.cpp | 7 - inkcpp_compiler/binary_emitter.cpp | 2 +- inkcpp_compiler/json.hpp | 25374 +++++++++------- inkcpp_test/Lists.cpp | 9 +- shared/public/system.h | 40 +- .../inkcpp/Source/inkcpp/Private/Choice.cpp | 5 + .../inkcpp/Source/inkcpp/Private/InkVar.cpp | 11 +- .../inkcpp/Source/inkcpp/Private/TagList.cpp | 2 +- unreal/inkcpp/Source/inkcpp/Public/Choice.h | 9 +- unreal/inkcpp/Source/inkcpp/Public/InkList.h | 35 + unreal/inkcpp/Source/inkcpp/Public/InkVar.h | 8 +- .../inkcpp_editor/Private/InkAssetFactory.cpp | 4 +- 25 files changed, 13828 insertions(+), 11818 deletions(-) create mode 100644 unreal/inkcpp/Source/inkcpp/Public/InkList.h diff --git a/inkcpp/array.h b/inkcpp/array.h index 6594497b..005cc285 100644 --- a/inkcpp/array.h +++ b/inkcpp/array.h @@ -309,7 +309,7 @@ namespace ink::runtime::internal }; template - class allocated_restorable_array : public basic_restorable_array + class allocated_restorable_array final : public basic_restorable_array { using base = basic_restorable_array; public: @@ -346,7 +346,7 @@ namespace ink::runtime::internal this->set_new_buffer(_buffer, new_capacity); } - virtual ~allocated_restorable_array() final + virtual ~allocated_restorable_array() { if(_buffer) { delete[] _buffer; diff --git a/inkcpp/functional.cpp b/inkcpp/functional.cpp index 36cbff1b..b4849651 100644 --- a/inkcpp/functional.cpp +++ b/inkcpp/functional.cpp @@ -11,7 +11,7 @@ namespace ink::runtime::internal { template<> - int32_t function_base::pop(basic_eval_stack* stack) + int32_t function_base::pop(basic_eval_stack* stack, list_table& lists) { value val = stack->pop(); inkAssert(val.type() == value_type::int32, "Type missmatch!"); @@ -19,7 +19,7 @@ namespace ink::runtime::internal } template<> - const char* function_base::pop(basic_eval_stack* stack) + const char* function_base::pop(basic_eval_stack* stack, list_table& lists) { value val = stack->pop(); inkAssert(val.type() == value_type::string, "Type missmatch!"); @@ -52,15 +52,15 @@ namespace ink::runtime::internal #ifdef INK_ENABLE_STL template<> - std::string function_base::pop(basic_eval_stack* stack) { - return std::string(pop(stack)); + std::string function_base::pop(basic_eval_stack* stack, list_table& lists) { + return std::string(pop(stack, lists)); } #endif #ifdef INK_ENABLE_UNREAL template<> - FInkVar function_base::pop(basic_eval_stack* stack) + FInkVar function_base::pop(basic_eval_stack* stack, list_table& lists) { - return FInkVar(stack->pop().to_interface_value()); + return FInkVar(stack->pop().to_interface_value(lists)); } template<> diff --git a/inkcpp/functions.cpp b/inkcpp/functions.cpp index 3c76479b..0c923183 100644 --- a/inkcpp/functions.cpp +++ b/inkcpp/functions.cpp @@ -39,7 +39,7 @@ namespace ink::runtime::internal } } - bool functions::call(hash_t name, basic_eval_stack* stack, size_t num_arguments, string_table& strings) + bool functions::call(hash_t name, basic_eval_stack* stack, size_t num_arguments, string_table& strings, list_table& lists) { // find entry entry* iter = _list; @@ -51,7 +51,7 @@ namespace ink::runtime::internal return false; // call - iter->value->call(stack, num_arguments, strings); + iter->value->call(stack, num_arguments, strings, lists); return true; } } \ No newline at end of file diff --git a/inkcpp/functions.h b/inkcpp/functions.h index 7a47884c..8e45e21f 100644 --- a/inkcpp/functions.h +++ b/inkcpp/functions.h @@ -18,7 +18,7 @@ namespace ink::runtime::internal void add(hash_t name, function_base* func); // Calls a function (if available) - bool call(hash_t name, basic_eval_stack* stack, size_t num_arguments, string_table& strings); + bool call(hash_t name, basic_eval_stack* stack, size_t num_arguments, string_table& strings, list_table& lists); private: struct entry diff --git a/inkcpp/include/functional.h b/inkcpp/include/functional.h index 574b2235..f046e030 100644 --- a/inkcpp/include/functional.h +++ b/inkcpp/include/functional.h @@ -10,6 +10,7 @@ namespace ink::runtime::internal { class basic_eval_stack; class string_table; + class list_table; // base function container with virtual callback methods class function_base @@ -19,15 +20,15 @@ namespace ink::runtime::internal // calls the underlying function object taking parameters from a stack #ifdef INK_ENABLE_UNREAL - virtual void call(basic_eval_stack* stack, size_t length, string_table& strings) = 0; + virtual void call(basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists) = 0; #else - virtual void call(basic_eval_stack* stack, size_t length, string_table& strings) = 0; + virtual void call(basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists) = 0; #endif protected: // used to hide basic_eval_stack and value definitions template - static T pop(basic_eval_stack* stack); + static T pop(basic_eval_stack* stack, list_table& lists); // used to hide basic_eval_stack and value definitions template @@ -50,9 +51,9 @@ namespace ink::runtime::internal function(F functor) : functor(functor) { } // calls the underlying function using arguments on the stack - virtual void call(basic_eval_stack* stack, size_t length, string_table& strings) override + virtual void call(basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists) override { - call(stack, length, strings, GenSeq()); + call(stack, length, strings, lists, GenSeq()); } private: @@ -68,15 +69,15 @@ namespace ink::runtime::internal // pops an argument from the stack using the function-type template - arg_type pop_arg(basic_eval_stack* stack) + arg_type pop_arg(basic_eval_stack* stack, list_table& lists) { // todo - type assert? - return pop>(stack); + return pop>(stack, lists); } template - void call(basic_eval_stack* stack, size_t length, string_table& strings, seq) + void call(basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists, seq) { // Make sure the argument counts match inkAssert(sizeof...(Is) == length, "Attempting to call functor with too few/many arguments"); @@ -86,7 +87,7 @@ namespace ink::runtime::internal if constexpr (is_same::value) { // Just evaluevaluatelate - functor(pop_arg(stack)...); + functor(pop_arg(stack, lists)...); // Ink expects us to push something // TODO -- Should be a special "void" value @@ -97,20 +98,14 @@ namespace ink::runtime::internal // SPECIAL: The result of the functor is a string type // in order to store it in the inkcpp interpreter we // need to store it in our allocated string table - auto string_result = functor(pop_arg(stack)...); + auto string_result = functor(pop_arg(stack, lists)...); // Get string length size_t len = string_handler::length(string_result); // Get source and allocate buffer - const char* src = string_handler::src(string_result); char* buffer = allocate(strings, len + 1); - - // Copy - char* ptr = buffer; - while (*src != '\0') - *(ptr++) = *(src++); - *ptr = 0; + string_handler::src_copy(string_result, buffer); // push string result push_string(stack, buffer); @@ -118,7 +113,7 @@ namespace ink::runtime::internal else { // Evaluate and push the result onto the stack - push(stack, functor(pop_arg(stack)...)); + push(stack, functor(pop_arg(stack, lists)...)); } } }; @@ -131,7 +126,7 @@ namespace ink::runtime::internal function_array_delegate(const D& del) : invocableDelegate(del) { } // calls the underlying delegate using arguments on the stack - virtual void call(basic_eval_stack* stack, size_t length, string_table& strings) override + virtual void call(basic_eval_stack* stack, size_t length, string_table& strings, list_table& lists) override { constexpr bool RET_VOID = is_same::return_type, @@ -140,7 +135,7 @@ namespace ink::runtime::internal TArray variables; for (size_t i = 0; i < length; i++) { - variables.Add(pop(stack)); + variables.Add(pop(stack, lists)); } if constexpr (RET_VOID) { diff --git a/inkcpp/include/list.h b/inkcpp/include/list.h index fd3310a3..7e97b15d 100644 --- a/inkcpp/include/list.h +++ b/inkcpp/include/list.h @@ -2,6 +2,10 @@ #include "system.h" +#ifdef INK_ENABLE_STL +#include +#endif + namespace ink::runtime { namespace internal { class list_table; @@ -12,6 +16,7 @@ namespace ink::runtime { class iterator { const char* _flag_name; + const char* _list_name; const list_interface& _list; int _i; friend list_interface; @@ -19,8 +24,22 @@ namespace ink::runtime { iterator(const char* flag_name, const list_interface& list, size_t i) : _flag_name(flag_name), _list(list), _i(i) {} public: - const char* operator*() const { return _flag_name; }; - iterator& operator++() { _list.next(_flag_name, _i); return *this; } + struct Flag { + const char* flag_name; + const char* list_name; +#ifdef INK_ENABLE_STL + friend std::ostream& operator<<(std::ostream& os, const Flag& flag) { + os << flag.list_name << "(" << flag.flag_name << ")"; + return os; + } +#endif + }; + Flag operator*() const { return Flag{ .flag_name = _flag_name, .list_name = _list_name }; }; + iterator& operator++() + { + _list.next( _flag_name, _list_name, _i ); + return *this; + } bool operator!=(const iterator& itr) const { return itr._i != _i; } bool operator==(const iterator& itr) const { return itr._i == _i; @@ -29,33 +48,33 @@ namespace ink::runtime { /** checks if a flag is contained in the list */ virtual bool contains(const char* flag) const { - ink_assert(false, "Not implemented function from interfaces is called!"); return false; + inkAssert(false, "Not implemented function from interfaces is called!"); return false; }; /** add a flag to list */ virtual void add(const char* flag) { - ink_assert(false, "Not implemented function from interface is called!"); + inkAssert(false, "Not implemented function from interface is called!"); }; /** removes a flag from list */ virtual void remove(const char* flag) { - ink_assert(false, "Not implemented function from interface is called!"); + inkAssert(false, "Not implemented function from interface is called!"); }; /** begin iterator for contained flags in list */ virtual iterator begin() const { - ink_assert(false, "Not implemented function from interface is called!"); + inkAssert(false, "Not implemented function from interface is called!"); return new_iterator(nullptr, -1); }; /** end iterator for contained flags in list */ virtual iterator end() const { - ink_assert(false, "Not implemented function from interface is called!"); + inkAssert(false, "Not implemented function from interface is called!"); return new_iterator(nullptr, -1); }; private: friend iterator; - virtual void next(const char*& flag_name, int& i) const { - ink_assert(false, "Not implemented funciton from interface is called!"); + virtual void next(const char*& flag_name, const char*& list_name, int& i) const { + inkAssert(false, "Not implemented funciton from interface is called!"); }; protected: iterator new_iterator(const char* flag_name, int i) const { diff --git a/inkcpp/include/traits.h b/inkcpp/include/traits.h index d7733f27..43f805b5 100644 --- a/inkcpp/include/traits.h +++ b/inkcpp/include/traits.h @@ -79,8 +79,13 @@ namespace ink::runtime::internal #define MARK_AS_STRING(TYPE, LEN, SRC) template<> struct is_string : constant { }; \ template<> struct string_handler { \ static size_t length(const TYPE& x) { return LEN; } \ - static const char* src(const TYPE& x) { return SRC; } \ - }; + static void src_copy(const TYPE& x, char* output) { \ + [&output](const char* src){\ + while(*src != '\0') *(output++) = *(src++); \ + *output = 0; \ + }(SRC);\ + } \ + } inline size_t c_str_len(const char* c) { const char* i = c; diff --git a/inkcpp/list_impl.cpp b/inkcpp/list_impl.cpp index c21c9a9b..559d0744 100644 --- a/inkcpp/list_impl.cpp +++ b/inkcpp/list_impl.cpp @@ -4,23 +4,23 @@ namespace ink::runtime::internal { bool list_impl::contains(const char* flag_name) const { auto flag = _list_table->toFlag(flag_name); - ink_assert(flag.has_value(), ("No flag with name found! '" + std::string(flag_name) + "'").c_str()); + inkAssert(flag.has_value(), "No flag with name found! '%s'", flag_name); return _list_table->has(list_table::list{_list}, *flag); } void list_impl::add(const char* flag_name) { auto flag = _list_table->toFlag(flag_name); - ink_assert(flag.has_value(), ("No flag with name found to add! '" + std::string(flag_name) + "'").c_str()); + inkAssert(flag.has_value(), "No flag with name found to add! '%s'", flag_name); _list = _list_table->add(list_table::list{_list}, *flag).lid; } void list_impl::remove(const char* flag_name) { auto flag = _list_table->toFlag(flag_name); - ink_assert(flag.has_value(), ("No flag with name found to remove! '" + std::string(flag_name) + "'").c_str()); + inkAssert(flag.has_value(), "No flag with name found to remove! '%s'", flag_name); _list = _list_table->sub(list_table::list{_list}, *flag).lid; } - void list_impl::next(const char*& flag_name, int& i) const { + void list_impl::next(const char*& flag_name, const char*& list_name, int& i) const { if (i == -1) { return; } list_flag flag{.list_id = static_cast(i >> 16), .flag = static_cast(i & 0xFF)}; @@ -45,6 +45,8 @@ namespace ink::runtime::internal { } } flag_name = _list_table->_flag_names[_list_table->toFid(flag)]; + list_name = _list_table->_list_names[flag.list_id]; + i = (flag.list_id << 16) | flag.flag; } } diff --git a/inkcpp/list_impl.h b/inkcpp/list_impl.h index ac53f735..2aabe135 100644 --- a/inkcpp/list_impl.h +++ b/inkcpp/list_impl.h @@ -26,7 +26,7 @@ namespace ink::runtime::internal { friend ink::runtime::internal::value; /// @todo wrong iteration order, first lists then flags - void next(const char*& flag_name, int& i) const override final; + void next(const char*& flag_name, const char*& list_name, int& i) const override final; }; } diff --git a/inkcpp/list_table.h b/inkcpp/list_table.h index de205dd5..ec95538e 100644 --- a/inkcpp/list_table.h +++ b/inkcpp/list_table.h @@ -85,17 +85,17 @@ namespace ink::runtime::internal * @retval nullopt if no flag was found */ optional toFlag(const char* flag_name) const { - for(auto itr = _flag_names.begin(); itr != _flag_names.end(); ++itr) { - if (strcmp(*itr, flag_name) == 0) { - int fid = itr - _flag_names.begin(); + for(auto flag_itr = _flag_names.begin(); flag_itr != _flag_names.end(); ++flag_itr) { + if (strcmp(*flag_itr, flag_name) == 0) { + int fid = flag_itr - _flag_names.begin(); int lid = 0; int begin = 0; - for(auto itr = _list_end.begin(); itr != _list_end.end(); ++itr) { - if(*itr > fid) { - lid = itr - _list_end.begin(); + for(auto list_itr = _list_end.begin(); list_itr != _list_end.end(); ++list_itr) { + if(*list_itr > fid) { + lid = list_itr - _list_end.begin(); break; } - begin = *itr; + begin = *list_itr; } return {list_flag{.list_id = static_cast(lid), .flag = static_cast(fid - begin)}}; } diff --git a/inkcpp/output.h b/inkcpp/output.h index 9d9c7e3c..1cb48cb5 100644 --- a/inkcpp/output.h +++ b/inkcpp/output.h @@ -54,7 +54,7 @@ namespace ink #ifdef INK_ENABLE_STL // Extract into a string std::string get(); -#else defined INK_ENABLE_UNREAL +#elif defined(INK_ENABLE_UNREAL) FString get(); #endif diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index 27d89ed8..02e31147 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -320,7 +320,6 @@ namespace ink::runtime::internal runner_impl::runner_impl(const story_impl* data, globals global) : _story(data), _globals(global.cast()), - _rng(time(NULL)), _operations( global.cast()->strings(), global.cast()->lists(), @@ -328,7 +327,7 @@ namespace ink::runtime::internal *global.cast(), *data, static_cast(*this)), - _backup(nullptr), _done(nullptr), _choices(), _container(~0) + _backup(nullptr), _done(nullptr), _choices(), _container(~0), _rng(time(NULL)) { _ptr = _story->instructions(); _evaluation_mode = false; @@ -1046,7 +1045,7 @@ namespace ink::runtime::internal int numArguments = (int)flag; // find and execute. will automatically push a valid if applicable - bool success = _functions.call(functionName, &_eval, numArguments, _globals->strings()); + bool success = _functions.call(functionName, &_eval, numArguments, _globals->strings(), _globals->lists()); // If we failed, notify a potential fallback function if (!success) diff --git a/inkcpp/string_table.h b/inkcpp/string_table.h index 94785f41..6b7ea5a6 100644 --- a/inkcpp/string_table.h +++ b/inkcpp/string_table.h @@ -7,10 +7,10 @@ namespace ink::runtime::internal { // hash tree sorted by string pointers - class string_table : public snapshot_interface + class string_table final : public snapshot_interface { public: - virtual ~string_table() final; + virtual ~string_table(); // Create a dynmaic string of a particular length char* create(size_t length); diff --git a/inkcpp/system.cpp b/inkcpp/system.cpp index a2adda4b..b3580a09 100644 --- a/inkcpp/system.cpp +++ b/inkcpp/system.cpp @@ -25,13 +25,6 @@ namespace ink for (size_t i = 0; i < length; i++) *(buf++) = 0; } - - void ink_assert(bool condition, const char* msg /*= nullptr*/) - { - if (!condition) - throw ink_exception(msg); - } - } #endif \ No newline at end of file diff --git a/inkcpp_compiler/binary_emitter.cpp b/inkcpp_compiler/binary_emitter.cpp index 716a646a..05c86371 100644 --- a/inkcpp_compiler/binary_emitter.cpp +++ b/inkcpp_compiler/binary_emitter.cpp @@ -374,7 +374,7 @@ namespace ink::compiler::internal // Otherwise, write container address if (container == nullptr) { _containers.set(position, 0); - inkAssert(optional, ("Was not able to resolve a not optional path! '" + path + "'").c_str()); + inkAssert(optional, "Was not able to resolve a not optional path! '%s'", path.c_str()); } else { _containers.set(position, container->offset); } diff --git a/inkcpp_compiler/json.hpp b/inkcpp_compiler/json.hpp index 2a32a829..4d1a37ad 100644 --- a/inkcpp_compiler/json.hpp +++ b/inkcpp_compiler/json.hpp @@ -1,46 +1,30 @@ -/* - __ _____ _____ _____ - __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.7.0 -|_____|_____|_____|_|___| https://github.com/nlohmann/json - -Licensed under the MIT License . -SPDX-License-Identifier: MIT -Copyright (c) 2013-2019 Niels Lohmann . - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ #ifndef INCLUDE_NLOHMANN_JSON_HPP_ #define INCLUDE_NLOHMANN_JSON_HPP_ -#define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 7 -#define NLOHMANN_JSON_VERSION_PATCH 0 - #include // all_of, find, for_each -#include // assert -#include // and, not, or #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list -#include // istream, ostream +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO #include // random_access_iterator_tag #include // unique_ptr #include // accumulate @@ -49,16 +33,133 @@ SOFTWARE. #include // vector // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + #include +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // transform #include // array -#include // and, not #include // forward_list #include // inserter, front_inserter, end #include // map @@ -70,63 +171,169 @@ SOFTWARE. #include // valarray // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include // nullptr_t #include // exception #include // runtime_error #include // to_string +#include // vector -// #include +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include // array #include // size_t +#include // uint8_t +#include // string + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + -namespace nlohmann +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template struct make_void { + using type = void; +}; +template using void_t = typename make_void::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { -/// struct to capture the start position of the current token -struct position_t + +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch { - /// the total number of characters read - std::size_t chars_read_total = 0; - /// the number of characters read in the current line - std::size_t chars_read_current_line = 0; - /// the number of lines read - std::size_t lines_read = 0; + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; - /// conversion to size_t to preserve SAX interface - constexpr operator size_t() const - { - return chars_read_total; - } +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; }; -} // namespace detail -} // namespace nlohmann +template class Op, class... Args> +using is_detected = typename detector::value_t; -// #include +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END -#include // pair // #include + + +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + /* Hedley - https://nemequ.github.io/hedley * Created by Evan Nemerson - * - * To the extent possible under law, the author(s) have dedicated all - * copyright and related and neighboring rights to this software to - * the public domain worldwide. This software is distributed without - * any warranty. - * - * For details, see . - * SPDX-License-Identifier: CC0-1.0 */ -#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 9) +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) #if defined(JSON_HEDLEY_VERSION) #undef JSON_HEDLEY_VERSION #endif -#define JSON_HEDLEY_VERSION 9 +#define JSON_HEDLEY_VERSION 15 #if defined(JSON_HEDLEY_STRINGIFY_EX) #undef JSON_HEDLEY_STRINGIFY_EX @@ -148,6 +355,16 @@ struct position_t #endif #define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + #if defined(JSON_HEDLEY_VERSION_ENCODE) #undef JSON_HEDLEY_VERSION_ENCODE #endif @@ -189,18 +406,18 @@ struct position_t #if defined(JSON_HEDLEY_MSVC_VERSION) #undef JSON_HEDLEY_MSVC_VERSION #endif -#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) -#elif defined(_MSC_FULL_VER) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) && !defined(__ICL) #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) #endif #if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) #undef JSON_HEDLEY_MSVC_VERSION_CHECK #endif -#if !defined(_MSC_VER) +#if !defined(JSON_HEDLEY_MSVC_VERSION) #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) #elif defined(_MSC_VER) && (_MSC_VER >= 1400) #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) @@ -213,9 +430,9 @@ struct position_t #if defined(JSON_HEDLEY_INTEL_VERSION) #undef JSON_HEDLEY_INTEL_VERSION #endif -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) -#elif defined(__INTEL_COMPILER) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) #endif @@ -228,6 +445,22 @@ struct position_t #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) #endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + #if defined(JSON_HEDLEY_PGI_VERSION) #undef JSON_HEDLEY_PGI_VERSION #endif @@ -323,9 +556,17 @@ struct position_t #if defined(JSON_HEDLEY_TI_VERSION) #undef JSON_HEDLEY_TI_VERSION #endif -#if defined(__TI_COMPILER_VERSION__) +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif +#endif #if defined(JSON_HEDLEY_TI_VERSION_CHECK) #undef JSON_HEDLEY_TI_VERSION_CHECK @@ -336,6 +577,102 @@ struct position_t #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) #endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + #if defined(JSON_HEDLEY_CRAY_VERSION) #undef JSON_HEDLEY_CRAY_VERSION #endif @@ -363,7 +700,7 @@ struct position_t #if __VER__ > 1000 #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) #else - #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) #endif #endif @@ -440,6 +777,22 @@ struct position_t #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) #endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + #if defined(JSON_HEDLEY_GCC_VERSION) #undef JSON_HEDLEY_GCC_VERSION #endif @@ -449,8 +802,16 @@ struct position_t !defined(JSON_HEDLEY_INTEL_VERSION) && \ !defined(JSON_HEDLEY_PGI_VERSION) && \ !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ !defined(JSON_HEDLEY_TI_VERSION) && \ - !defined(__COMPCERT__) + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION #endif @@ -466,17 +827,21 @@ struct position_t #if defined(JSON_HEDLEY_HAS_ATTRIBUTE) #undef JSON_HEDLEY_HAS_ATTRIBUTE #endif -#if defined(__has_attribute) - #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) #else - #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) #endif #if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE #endif #if defined(__has_attribute) - #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) #else #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif @@ -485,7 +850,7 @@ struct position_t #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE #endif #if defined(__has_attribute) - #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) #else #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif @@ -493,12 +858,30 @@ struct position_t #if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE #endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) #else #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) #endif +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + #if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE #endif @@ -660,7 +1043,13 @@ struct position_t JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ @@ -687,13 +1076,21 @@ struct position_t #elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) #elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") -#elif JSON_HEDLEY_TI_VERSION_CHECK(8,1,0) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") #elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) @@ -704,54 +1101,209 @@ struct position_t #define JSON_HEDLEY_DIAGNOSTIC_POP #endif -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ #endif -#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) -#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x #endif -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST #endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") #elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") #elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) -#elif JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") #else #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS #endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + #if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL #endif @@ -765,40 +1317,74 @@ struct position_t #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL #endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + #if defined(JSON_HEDLEY_DEPRECATED) #undef JSON_HEDLEY_DEPRECATED #endif #if defined(JSON_HEDLEY_DEPRECATED_FOR) #undef JSON_HEDLEY_DEPRECATED_FOR #endif -#if defined(__cplusplus) && (__cplusplus >= 201402L) - #define JSON_HEDLEY_DEPRECATED(since) [[deprecated("Since " #since)]] - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) [[deprecated("Since " #since "; use " #replacement)]] +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) #elif \ - JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,3,0) + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) #elif \ JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) - #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) - #define JSON_HEDLEY_DEPRECATED(since) _declspec(deprecated) + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") @@ -814,7 +1400,8 @@ struct position_t #if \ JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) #else #define JSON_HEDLEY_UNAVAILABLE(available_since) @@ -823,21 +1410,41 @@ struct position_t #if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) #undef JSON_HEDLEY_WARN_UNUSED_RESULT #endif -#if defined(__cplusplus) && (__cplusplus >= 201703L) - #define JSON_HEDLEY_WARN_UNUSED_RESULT [[nodiscard]] -#elif \ +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) #elif defined(_Check_return_) /* SAL */ #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ #else #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) #endif #if defined(JSON_HEDLEY_SENTINEL) @@ -847,7 +1454,8 @@ struct position_t JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) #else #define JSON_HEDLEY_SENTINEL(position) @@ -858,24 +1466,40 @@ struct position_t #endif #if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_NO_RETURN __noreturn -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L #define JSON_HEDLEY_NO_RETURN _Noreturn #elif defined(__cplusplus) && (__cplusplus >= 201103L) - #define JSON_HEDLEY_NO_RETURN [[noreturn]] + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) #elif \ JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(18,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(17,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") #elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) @@ -885,67 +1509,82 @@ struct position_t #define JSON_HEDLEY_NO_RETURN #endif +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + #if defined(JSON_HEDLEY_UNREACHABLE) #undef JSON_HEDLEY_UNREACHABLE #endif #if defined(JSON_HEDLEY_UNREACHABLE_RETURN) #undef JSON_HEDLEY_UNREACHABLE_RETURN #endif -#if \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) - #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) - #define JSON_HEDLEY_UNREACHABLE() __assume(0) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) - #if defined(__cplusplus) - #define JSON_HEDLEY_UNREACHABLE() std::_nassert(0) - #else - #define JSON_HEDLEY_UNREACHABLE() _nassert(0) - #endif - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return value -#elif defined(EXIT_FAILURE) - #define JSON_HEDLEY_UNREACHABLE() abort() -#else - #define JSON_HEDLEY_UNREACHABLE() - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return value -#endif -#if !defined(JSON_HEDLEY_UNREACHABLE_RETURN) - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() -#endif - #if defined(JSON_HEDLEY_ASSUME) #undef JSON_HEDLEY_ASSUME #endif #if \ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_ASSUME(expr) __assume(expr) #elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) #if defined(__cplusplus) #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) #else #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) #endif -#elif \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && !defined(JSON_HEDLEY_ARM_VERSION)) || \ +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) - #define JSON_HEDLEY_ASSUME(expr) ((void) ((expr) ? 1 : (__builtin_unreachable(), 1))) + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif #else - #define JSON_HEDLEY_ASSUME(expr) ((void) (expr)) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) #endif - JSON_HEDLEY_DIAGNOSTIC_PUSH -#if \ - JSON_HEDLEY_HAS_WARNING("-Wvariadic-macros") || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) #if defined(__clang__) #pragma clang diagnostic ignored "-Wvariadic-macros" #elif defined(JSON_HEDLEY_GCC_VERSION) @@ -979,8 +1618,18 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) #elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) @@ -993,7 +1642,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif #if defined(__cplusplus) #if __cplusplus >= 201103L - #define JSON_HEDLEY_CONSTEXPR constexpr + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) #endif #endif #if !defined(JSON_HEDLEY_CONSTEXPR) @@ -1013,44 +1662,50 @@ JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_UNPREDICTABLE #endif #if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) - #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable(!!(expr)) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) #endif #if \ - JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) -# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(expr, value, probability) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1, probability) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0, probability) -# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) -# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) -#if !defined(JSON_HEDLEY_BUILTIN_UNPREDICTABLE) - #define JSON_HEDLEY_BUILTIN_UNPREDICTABLE(expr) __builtin_expect_with_probability(!!(expr), 1, 0.5) -#endif + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) #elif \ - JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define JSON_HEDLEY_PREDICT(expr, expected, probability) \ - (((probability) >= 0.9) ? __builtin_expect(!!(expr), (expected)) : (((void) (expected)), !!(expr))) + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) # define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ (__extension__ ({ \ - JSON_HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ + double hedley_probability_ = (probability); \ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ })) # define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ (__extension__ ({ \ - JSON_HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ + double hedley_probability_ = (probability); \ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ })) # define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) # define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) #else -# define JSON_HEDLEY_PREDICT(expr, expected, probability) (((void) (expected)), !!(expr)) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) # define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) # define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) # define JSON_HEDLEY_LIKELY(expr) (!!(expr)) @@ -1070,10 +1725,24 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_MALLOC __declspec(restrict) #else #define JSON_HEDLEY_MALLOC @@ -1083,20 +1752,37 @@ JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_PURE #endif #if \ - JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_PURE __attribute__((__pure__)) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) - #define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") #else - #define JSON_HEDLEY_PURE +# define JSON_HEDLEY_PURE #endif #if defined(JSON_HEDLEY_CONST) @@ -1109,10 +1795,23 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") #else #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE #endif @@ -1126,13 +1825,18 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ - defined(__clang__) + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_RESTRICT __restrict #elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) #define JSON_HEDLEY_RESTRICT _Restrict @@ -1153,8 +1857,15 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_HEDLEY_INLINE __inline__ #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_INLINE __inline #else #define JSON_HEDLEY_INLINE @@ -1164,23 +1875,44 @@ JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_ALWAYS_INLINE #endif #if \ - JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) - #define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) - #define JSON_HEDLEY_ALWAYS_INLINE __forceinline -#elif JSON_HEDLEY_TI_VERSION_CHECK(7,0,0) && defined(__cplusplus) - #define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") #else - #define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE #endif #if defined(JSON_HEDLEY_NEVER_INLINE) @@ -1193,14 +1925,27 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) #elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") -#elif JSON_HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") #elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") @@ -1222,26 +1967,32 @@ JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_IMPORT #endif #if defined(_WIN32) || defined(__CYGWIN__) - #define JSON_HEDLEY_PRIVATE - #define JSON_HEDLEY_PUBLIC __declspec(dllexport) - #define JSON_HEDLEY_IMPORT __declspec(dllimport) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) #else - #if \ - JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_EABI__) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) - #define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) - #define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) - #else - #define JSON_HEDLEY_PRIVATE - #define JSON_HEDLEY_PUBLIC - #endif - #define JSON_HEDLEY_IMPORT extern +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern #endif #if defined(JSON_HEDLEY_NO_THROW) @@ -1250,10 +2001,12 @@ JSON_HEDLEY_DIAGNOSTIC_POP #if \ JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) #elif \ JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) #define JSON_HEDLEY_NO_THROW __declspec(nothrow) #else @@ -1264,27 +2017,18 @@ JSON_HEDLEY_DIAGNOSTIC_POP #undef JSON_HEDLEY_FALL_THROUGH #endif #if \ - defined(__cplusplus) && \ - (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ - !defined(JSON_HEDLEY_PGI_VERSION) - #if \ - (__cplusplus >= 201703L) || \ - ((__cplusplus >= 201103L) && JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)) - #define JSON_HEDLEY_FALL_THROUGH [[fallthrough]] - #elif (__cplusplus >= 201103L) && JSON_HEDLEY_HAS_CPP_ATTRIBUTE(clang::fallthrough) - #define JSON_HEDLEY_FALL_THROUGH [[clang::fallthrough]] - #elif (__cplusplus >= 201103L) && JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) - #define JSON_HEDLEY_FALL_THROUGH [[gnu::fallthrough]] - #endif -#endif -#if !defined(JSON_HEDLEY_FALL_THROUGH) - #if JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(fallthrough,7,0,0) && !defined(JSON_HEDLEY_PGI_VERSION) - #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) - #elif defined(__fallthrough) /* SAL */ - #define JSON_HEDLEY_FALL_THROUGH __fallthrough - #else - #define JSON_HEDLEY_FALL_THROUGH - #endif + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH #endif #if defined(JSON_HEDLEY_RETURNS_NON_NULL) @@ -1292,7 +2036,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif #if \ JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) #elif defined(_Ret_notnull_) /* SAL */ #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ @@ -1320,12 +2065,11 @@ JSON_HEDLEY_DIAGNOSTIC_POP #if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) #undef JSON_HEDLEY_REQUIRE_CONSTEXPR #endif -/* Note the double-underscore. For internal use only; no API - * guarantees! */ -#if defined(JSON_HEDLEY__IS_CONSTEXPR) - #undef JSON_HEDLEY__IS_CONSTEXPR +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ #endif - #if \ JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ @@ -1333,9 +2077,10 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) #endif #if !defined(__cplusplus) @@ -1348,31 +2093,40 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) #if defined(__INTPTR_TYPE__) - #define JSON_HEDLEY__IS_CONSTEXPR(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) #else #include - #define JSON_HEDLEY__IS_CONSTEXPR(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) #endif # elif \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(JSON_HEDLEY_SUNPRO_VERSION) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ - JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) #if defined(__INTPTR_TYPE__) - #define JSON_HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) #else #include - #define JSON_HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) #endif # elif \ defined(JSON_HEDLEY_GCC_VERSION) || \ defined(JSON_HEDLEY_INTEL_VERSION) || \ defined(JSON_HEDLEY_TINYC_VERSION) || \ - defined(JSON_HEDLEY_TI_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ defined(__clang__) -# define JSON_HEDLEY__IS_CONSTEXPR(expr) ( \ +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ sizeof(void) != \ sizeof(*( \ 1 ? \ @@ -1383,11 +2137,11 @@ JSON_HEDLEY_DIAGNOSTIC_POP ) # endif #endif -#if defined(JSON_HEDLEY__IS_CONSTEXPR) +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) #if !defined(JSON_HEDLEY_IS_CONSTANT) - #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY__IS_CONSTEXPR(expr) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) #endif - #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY__IS_CONSTEXPR(expr) ? (expr) : (-1)) + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) #else #if !defined(JSON_HEDLEY_IS_CONSTANT) #define JSON_HEDLEY_IS_CONSTANT(expr) (0) @@ -1420,67 +2174,36 @@ JSON_HEDLEY_DIAGNOSTIC_POP #if \ !defined(__cplusplus) && ( \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ - JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ defined(_Static_assert) \ ) # define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) #elif \ - (defined(__cplusplus) && (__cplusplus >= 201703L)) || \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ - (defined(__cplusplus) && JSON_HEDLEY_TI_VERSION_CHECK(8,3,0)) -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr, message) -#elif defined(__cplusplus) && (__cplusplus >= 201103L) -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr) + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) #else # define JSON_HEDLEY_STATIC_ASSERT(expr, message) #endif -#if defined(JSON_HEDLEY_CONST_CAST) - #undef JSON_HEDLEY_CONST_CAST -#endif -#if defined(__cplusplus) -# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) -#elif \ - JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ - ((T) (expr)); \ - JSON_HEDLEY_DIAGNOSTIC_POP \ - })) -#else -# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_REINTERPRET_CAST) - #undef JSON_HEDLEY_REINTERPRET_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) -#else - #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (*((T*) &(expr))) -#endif - -#if defined(JSON_HEDLEY_STATIC_CAST) - #undef JSON_HEDLEY_STATIC_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) -#else - #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_CPP_CAST) - #undef JSON_HEDLEY_CPP_CAST +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL #endif #if defined(__cplusplus) - #define JSON_HEDLEY_CPP_CAST(T, expr) static_cast(expr) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL #else - #define JSON_HEDLEY_CPP_CAST(T, expr) (expr) + #define JSON_HEDLEY_NULL ((void*) 0) #endif #if defined(JSON_HEDLEY_MESSAGE) @@ -1517,41 +2240,51 @@ JSON_HEDLEY_DIAGNOSTIC_POP JSON_HEDLEY_DIAGNOSTIC_POP #elif \ JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) #else # define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) #endif +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif #if defined(JSON_HEDLEY_REQUIRE_MSG) #undef JSON_HEDLEY_REQUIRE_MSG #endif #if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) # if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") -# define JSON_HEDLEY_REQUIRE_MSG(expr, msg) \ +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ JSON_HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ - __attribute__((__diagnose_if__(!(expr), msg, "error"))) \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ JSON_HEDLEY_DIAGNOSTIC_POP # else -# define JSON_HEDLEY_REQUIRE_MSG(expr, msg) __attribute__((__diagnose_if__(!(expr), msg, "error"))) +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) # endif #else -# define JSON_HEDLEY_REQUIRE_MSG(expr, msg) -#endif - -#if defined(JSON_HEDLEY_REQUIRE) - #undef JSON_HEDLEY_REQUIRE +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) #endif -#define JSON_HEDLEY_REQUIRE(expr) JSON_HEDLEY_REQUIRE_MSG(expr, #expr) #if defined(JSON_HEDLEY_FLAGS) #undef JSON_HEDLEY_FLAGS #endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS #endif #if defined(JSON_HEDLEY_FLAGS_CAST) @@ -1568,6 +2301,17 @@ JSON_HEDLEY_DIAGNOSTIC_POP # define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) #endif +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + /* Remaining macros are deprecated. */ #if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) @@ -1617,10 +2361,13 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ -// This file contains all internal macro definitions +// This file contains all internal macro definitions (except those affecting ABI) // You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them -// exclude unsupported compilers +// #include + + +// exclude unsupported compilers #if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) #if defined(__clang__) #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 @@ -1634,26 +2381,128 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif // C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 -#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) - #define JSON_HAS_CPP_14 +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE #endif -// disable float-equal warnings on GCC/clang -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wfloat-equal" +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS #endif // disable documentation warnings on clang #if defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" #endif -// allow to disable exceptions +// allow disabling exceptions #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) #define JSON_THROW(exception) throw exception #define JSON_TRY try @@ -1687,35 +2536,48 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER #endif +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + /*! @brief macro to briefly define a mapping between an enum and JSON @def NLOHMANN_JSON_SERIALIZE_ENUM @since version 3.4.0 */ -#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ - template \ - inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [e](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.first == e; \ - }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ - } \ - template \ - inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [j](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.second == j; \ - }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ } // Ugly macros to avoid uglier copy-paste when specializing basic_json. They @@ -1727,413 +2589,603 @@ JSON_HEDLEY_DIAGNOSTIC_POP class StringType, class BooleanType, class NumberIntegerType, \ class NumberUnsignedType, class NumberFloatType, \ template class AllocatorType, \ - template class JSONSerializer> + template class JSONSerializer, \ + class BinaryType> #define NLOHMANN_BASIC_JSON_TPL \ basic_json + AllocatorType, JSONSerializer, BinaryType> + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } -namespace nlohmann -{ -namespace detail -{ -//////////////// -// exceptions // -//////////////// +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } /*! -@brief general exception of the @ref basic_json class +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } -This class is an extension of `std::exception` objects with a member @a id for -exception ids. It is used as the base class for all exceptions thrown by the -@ref basic_json class. This class can hence be used as "wildcard" to catch -exceptions. +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } -Subclasses: -- @ref parse_error for exceptions indicating a parse error -- @ref invalid_iterator for exceptions indicating errors with iterators -- @ref type_error for exceptions indicating executing a member function with - a wrong type -- @ref out_of_range for exceptions indicating access out of the defined range -- @ref other_error for exceptions indicating other library errors -@internal -@note To have nothrow-copy-constructible exceptions, we internally use - `std::runtime_error` which can cope with arbitrary-length error messages. - Intermediate strings are built with static functions and then passed to - the actual constructor. -@endinternal +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif -@liveexample{The following code shows how arbitrary library exceptions can be -caught.,exception} +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif -@since version 3.0.0 -*/ -class exception : public std::exception +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 +#endif + +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif + +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail { - public: - /// returns the explanatory string - JSON_HEDLEY_RETURNS_NON_NULL - const char* what() const noexcept override - { - return m.what(); - } - /// the id of the exception - const int id; +/////////////////////////// +// JSON type enumeration // +/////////////////////////// - protected: - JSON_HEDLEY_NON_NULL(3) - exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} +/*! +@brief the JSON type enumeration - static std::string name(const std::string& ename, int id_) - { - return "[json.exception." + ename + "." + std::to_string(id_) + "] "; - } +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. - private: - /// an exception object as storage for error messages - std::runtime_error m; +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function }; /*! -@brief exception indicating a parse error - -This exception is thrown by the library when a parse error occurs. Parse errors -can occur during the deserialization of JSON text, CBOR, MessagePack, as well -as when using JSON Patch. - -Member @a byte holds the byte index of the last read character in the input -file. - -Exceptions have ids 1xx. - -name / id | example message | description ------------------------------- | --------------- | ------------------------- -json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. -json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. -json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. -json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. -json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. -json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. -json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. -json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. -json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. -json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. -json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. -json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. -json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). - -@note For an input with n bytes, 1 is the index of the first character and n+1 - is the index of the terminating null byte or the end of file. This also - holds true when reading a byte vector (CBOR or MessagePack). - -@liveexample{The following code shows how a `parse_error` exception can be -caught.,parse_error} - -@sa - @ref exception for the base class of the library exceptions -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 */ -class parse_error : public exception +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#endif { - public: - /*! - @brief create a parse error exception - @param[in] id_ the id of the exception - @param[in] pos the position where the error occurred (or with - chars_read_total=0 if the position cannot be - determined) - @param[in] what_arg the explanatory string - @return parse_error object - */ - static parse_error create(int id_, const position_t& pos, const std::string& what_arg) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - position_string(pos) + ": " + what_arg; - return parse_error(id_, pos.chars_read_total, w.c_str()); - } + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) { - std::string w = exception::name("parse_error", id_) + "parse error" + - (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + - ": " + what_arg; - return parse_error(id_, byte_, w.c_str()); + return order[l_index] <=> order[r_index]; // *NOPAD* } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} - /*! - @brief byte index of the parse error +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif - The byte index of the last read character in the input file. +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END - @note For an input with n bytes, 1 is the index of the first character and - n+1 is the index of the terminating null byte or the end of file. - This also holds true when reading a byte vector (CBOR or MessagePack). - */ - const std::size_t byte; +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT - private: - parse_error(int id_, std::size_t byte_, const char* what_arg) - : exception(id_, what_arg), byte(byte_) {} - static std::string position_string(const position_t& pos) - { - return " at line " + std::to_string(pos.lines_read + 1) + - ", column " + std::to_string(pos.chars_read_current_line); - } -}; -/*! -@brief exception indicating errors with iterators - -This exception is thrown if iterators passed to a library function do not match -the expected semantics. - -Exceptions have ids 2xx. - -name / id | example message | description ------------------------------------ | --------------- | ------------------------- -json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. -json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. -json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. -json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. -json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. -json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. -json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. -json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. -json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. -json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). - -@liveexample{The following code shows how an `invalid_iterator` exception can be -caught.,invalid_iterator} - -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 -*/ -class invalid_iterator : public exception -{ - public: - static invalid_iterator create(int id_, const std::string& what_arg) - { - std::string w = exception::name("invalid_iterator", id_) + what_arg; - return invalid_iterator(id_, w.c_str()); - } +// #include - private: - JSON_HEDLEY_NON_NULL(3) - invalid_iterator(int id_, const char* what_arg) - : exception(id_, what_arg) {} -}; + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ /*! -@brief exception indicating executing a member function with a wrong type - -This exception is thrown in case of a type error; that is, a library function is -executed on a JSON value whose type does not match the expected semantics. - -Exceptions have ids 3xx. - -name / id | example message | description ------------------------------ | --------------- | ------------------------- -json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. -json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. -json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. -json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. -json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. -json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. -json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. -json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. -json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. -json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. -json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. -json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. -json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. -json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. -json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. -json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | -json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | - -@liveexample{The following code shows how a `type_error` exception can be -caught.,type_error} - -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 */ -class type_error : public exception +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) { - public: - static type_error create(int id_, const std::string& what_arg) - { - std::string w = exception::name("type_error", id_) + what_arg; - return type_error(id_, w.c_str()); - } + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != StringType::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} - private: - JSON_HEDLEY_NON_NULL(3) - type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +template +inline StringType escape(StringType s) +{ + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); + return s; +} /*! -@brief exception indicating access out of the defined range - -This exception is thrown in case a library function is called on an input -parameter that exceeds the expected range, for instance in case of array -indices or nonexisting object keys. - -Exceptions have ids 4xx. - -name / id | example message | description -------------------------------- | --------------- | ------------------------- -json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. -json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. -json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. -json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. -json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. -json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. -json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. | -json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | -json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | - -@liveexample{The following code shows how an `out_of_range` exception can be -caught.,out_of_range} - -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref other_error for exceptions indicating other library errors - -@since version 3.0.0 -*/ -class out_of_range : public exception + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +template +static void unescape(StringType& s) { - public: - static out_of_range create(int id_, const std::string& what_arg) - { - std::string w = exception::name("out_of_range", id_) + what_arg; - return out_of_range(id_, w.c_str()); - } + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); +} - private: - JSON_HEDLEY_NON_NULL(3) - out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END -/*! -@brief exception indicating other library errors +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT -This exception is thrown in case of errors that cannot be classified with the -other exception types. -Exceptions have ids 5xx. -name / id | example message | description ------------------------------- | --------------- | ------------------------- -json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. +#include // size_t -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range +// #include -@liveexample{The following code shows how an `other_error` exception can be -caught.,other_error} -@since version 3.0.0 -*/ -class other_error : public exception +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail { - public: - static other_error create(int id_, const std::string& what_arg) + +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const { - std::string w = exception::name("other_error", id_) + what_arg; - return other_error(id_, w.c_str()); + return chars_read_total; } - - private: - JSON_HEDLEY_NON_NULL(3) - other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; + } // namespace detail -} // namespace nlohmann +NLOHMANN_JSON_NAMESPACE_END // #include // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT -#include // not + +#include // array #include // size_t #include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for -namespace nlohmann -{ +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { -// alias templates to reduce boilerplate -template -using enable_if_t = typename std::enable_if::type; template using uncvref_t = typename std::remove_cv::type>::type; -// implementation of C++14 index_sequence and affiliates -// source: https://stackoverflow.com/a/32223343 -template -struct index_sequence -{ - using type = index_sequence; - using value_type = std::size_t; +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; static constexpr std::size_t size() noexcept { return sizeof...(Ints); } }; -template -struct merge_and_renumber; +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal -template -struct merge_and_renumber, index_sequence> - : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; +// Compile-time sequences of integers -template -struct make_index_sequence - : merge_and_renumber < typename make_index_sequence < N / 2 >::type, - typename make_index_sequence < N - N / 2 >::type > {}; +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; -template<> struct make_index_sequence<0> : index_sequence<> {}; -template<> struct make_index_sequence<1> : index_sequence<0> {}; +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; -template +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template using index_sequence_for = make_index_sequence; +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + // dispatch utility (taken from ranges-v3) template struct priority_tag : priority_tag < N - 1 > {}; template<> struct priority_tag<0> {}; @@ -2142,53 +3194,67 @@ template<> struct priority_tag<0> {}; template struct static_const { - static constexpr T value{}; + static JSON_INLINE_VARIABLE constexpr T value{}; }; -template -constexpr T static_const::value; +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif + +template +inline constexpr std::array make_array(Args&& ... args) +{ + return std::array {{static_cast(std::forward(args))...}}; +} + } // namespace detail -} // namespace nlohmann +NLOHMANN_JSON_NAMESPACE_END // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + -#include // not #include // numeric_limits #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval +#include // tuple // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT -#include // random_access_iterator_tag -// #include +#include // random_access_iterator_tag +// #include -namespace nlohmann -{ -namespace detail -{ -template struct make_void -{ - using type = void; -}; -template using void_t = typename make_void::type; -} // namespace detail -} // namespace nlohmann +// #include // #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { -template + +template struct iterator_types {}; -template +template struct iterator_types < It, void_t +template struct iterator_traits { }; -template +template struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> : iterator_types { }; -template +template struct iterator_traits::value>> { using iterator_category = std::random_access_iterator_tag; @@ -2223,143 +3289,135 @@ struct iterator_traits::value>> using pointer = T*; using reference = T&; }; -} // namespace detail -} // namespace nlohmann + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END // #include -// #include +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT -// #include -#include +// #include -// #include +NLOHMANN_JSON_NAMESPACE_BEGIN -// http://en.cppreference.com/w/cpp/experimental/is_detected -namespace nlohmann -{ -namespace detail -{ -struct nonesuch -{ - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - nonesuch(nonesuch const&&) = delete; - void operator=(nonesuch const&) = delete; - void operator=(nonesuch&&) = delete; -}; +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); -template class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; +NLOHMANN_JSON_NAMESPACE_END -template class Op, class... Args> -struct detector>, Op, Args...> -{ - using value_t = std::true_type; - using type = Op; -}; +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT -template