From 8ff5848ff79a8df2070495eebc8b86fb8f4c1131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B8=D1=85=D0=B0=D0=B8=D0=BB=20=D0=9A=D0=B0=D0=BF?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BA=D0=BE?= Date: Wed, 5 Jun 2024 22:14:22 +0300 Subject: [PATCH] d --- v4/1 | 553 ++++++++++++++++++++++++++++++++++++++++++ v4/any.h | 20 +- v4/ctx.h | 1 + v4/ctx_test2.cpp | 2 + v4/memory_Context.cpp | 86 +++++++ v4/memory_Context.h | 97 +------- 6 files changed, 662 insertions(+), 97 deletions(-) create mode 100644 v4/1 diff --git a/v4/1 b/v4/1 new file mode 100644 index 0000000..3fc4ac0 --- /dev/null +++ b/v4/1 @@ -0,0 +1,553 @@ +/** + * Implementation of N4562 std::experimental::any (merged into C++17 as std::any) + * for C++11 compilers. + * + * See also: + * + http://en.cppreference.com/w/cpp/any + * + http://en.cppreference.com/w/cpp/experimental/any + * + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any + * + https://cplusplus.github.io/LWG/lwg-active.html#2509 + * + * Copyright (c) 2016 Denilson das Merc?s Amorim + * Copyright (c) 2018 Claudio Fantacci + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE.md or copy at http://www.boost.org/LICENSE_1_0.txt) + */ + +#ifndef ANY_H +#define ANY_H + +#include +#include +#include + + +namespace libany +{ + +class bad_any_cast : public std::bad_cast +{ +public: + const char* what() const noexcept override + { + return "bad any_cast"; + } +}; + + +class any final +{ +public: + /** + * Constructs an object of type any with an empty state. + */ + any() : + vtable(nullptr) + { } + + + /** + * Constructs an object of type any with an equivalent state as other. + */ + any(const any& rhs) : + vtable(rhs.vtable) + { + if(rhs.has_value()) + { + rhs.vtable->copy(rhs.storage, this->storage); + } + } + + + /** + * Constructs an object of type any with a state equivalent to the original state of other. + * rhs is left in a valid but otherwise unspecified state. + */ + any(any&& rhs) noexcept : + vtable(rhs.vtable) + { + if(rhs.has_value()) + { + rhs.vtable->move(rhs.storage, this->storage); + rhs.vtable = nullptr; + } + } + + + /** + * Same effect as this->clear(). + */ + ~any() + { + this->reset(); + } + + + /** + * Constructs an object of type any that contains an object of type T direct-initialized with std::forward(value). + * T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. + * This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. + */ + template::type, any>::value>::type> + any(ValueType&& value) + { + static_assert(std::is_copy_constructible::type>::value, + "T shall satisfy the CopyConstructible requirements."); + this->construct(std::forward(value)); + } + + + /** + * Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown. + */ + any& operator=(const any& rhs) + { + any(rhs).swap(*this); + return *this; + } + + + /** + * Has the same effect as any(std::move(rhs)).swap(*this). + * The state of *this is equivalent to the original state of rhs and rhs is left in a valid + * but otherwise unspecified state. + */ + any& operator=(any&& rhs) noexcept + { + any(std::move(rhs)).swap(*this); + return *this; + } + + + /** + * Has the same effect as any(std::forward(value)).swap(*this). No effect if a exception is thrown. + * T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. + * This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. + */ + template::type, any>::value>::type> + any& operator=(ValueType&& value) + { + static_assert(std::is_copy_constructible::type>::value, "T shall satisfy the CopyConstructible requirements."); + any(std::forward(value)).swap(*this); + return *this; + } + + + /** + * If not empty, destroys the contained object. + */ + void reset() noexcept + { + if(has_value()) + { + this->vtable->destroy(storage); + this->vtable = nullptr; + } + } + + + /** + * Returns true if *this has no contained object, otherwise false. + */ + bool has_value() const noexcept + { + return this->vtable != nullptr; + } + + + /** + * If *this has a contained object of type T, typeid(T); otherwise typeid(void). + */ + const std::type_info& type() const noexcept + { + return has_value()? this->vtable->type() : typeid(void); + } + + + /** + * Exchange the states of *this and rhs. + */ + void swap(any& other) noexcept + { + if(this->vtable != other.vtable) + { + any tmp(std::move(other)); + + other.vtable = this->vtable; + if(this->vtable != nullptr) + this->vtable->move(this->storage, other.storage); + + this->vtable = tmp.vtable; + if(tmp.vtable != nullptr) + { + tmp.vtable->move(tmp.storage, this->storage); + tmp.vtable = nullptr; + } + } + else + { + if(this->vtable != nullptr) + this->vtable->swap(this->storage, other.storage); + } + } + + +private: + union storage_union + { + using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of::value>::type; + + void* dynamic; + + stack_storage_t stack; + }; + + + /** + * Base VTable specification. + * + * Note: The caller is responsible for doing .vtable = nullptr after destructful operations + * such as destroy() and/or move(). + */ + struct vtable_type + { + /** + * The type of the object this vtable is for. + */ + const std::type_info& (*type)() noexcept; + + + /** + * Destroys the object in the union. + * The state of the union after this call is unspecified, caller must ensure not to use src anymore. + */ + void(*destroy)(storage_union&) noexcept; + + + /** + * Copies the **inner** content of the src union into the yet unitialized dest union. + * As such, both inner objects will have the same state, but on separate memory locations. + */ + void(*copy)(const storage_union& src, storage_union& dest); + + + /** + * Moves the storage from src to the yet unitialized dest union. + * The state of src after this call is unspecified, caller must ensure not to use src anymore. + */ + void(*move)(storage_union& src, storage_union& dest) noexcept; + + + /** + * Exchanges the storage between lhs and rhs. + */ + void(*swap)(storage_union& lhs, storage_union& rhs) noexcept; + }; + + + /** + * VTable for dynamically allocated storage. + */ + template + struct vtable_dynamic + { + static const std::type_info& type() noexcept + { + return typeid(T); + } + + + static void destroy(storage_union& storage) noexcept + { + delete reinterpret_cast(storage.dynamic); + } + + + static void copy(const storage_union& src, storage_union& dest) + { + dest.dynamic = new T(*reinterpret_cast(src.dynamic)); + } + + + static void move(storage_union& src, storage_union& dest) noexcept + { + dest.dynamic = src.dynamic; + src.dynamic = nullptr; + } + + + static void swap(storage_union& lhs, storage_union& rhs) noexcept + { + std::swap(lhs.dynamic, rhs.dynamic); + } + }; + + + /** + * VTable for stack allocated storage. + */ + template + struct vtable_stack + { + static const std::type_info& type() noexcept + { + return typeid(T); + } + + + static void destroy(storage_union& storage) noexcept + { + reinterpret_cast(&storage.stack)->~T(); + } + + + static void copy(const storage_union& src, storage_union& dest) + { + new (&dest.stack) T(reinterpret_cast(src.stack)); + } + + + static void move(storage_union& src, storage_union& dest) noexcept + { + /** + * One of the conditions for using vtable_stack is a nothrow move constructor, + * so this move constructor will never throw a exception. + */ + new (&dest.stack) T(std::move(reinterpret_cast(src.stack))); + destroy(src); + } + + + static void swap(storage_union& lhs, storage_union& rhs) noexcept + { + storage_union tmp_storage; + move(rhs, tmp_storage); + move(lhs, rhs); + move(tmp_storage, lhs); + } + }; + + + /** + * Whether the type T must be dynamically allocated or can be stored on the stack. + */ + template + struct requires_allocation : + std::integral_constant::value // N4562 6.3/3 [any.class] + && sizeof(T) <= sizeof(storage_union::stack) + && std::alignment_of::value <= std::alignment_of::value)> + { }; + + + /** + * Returns the pointer to the vtable of the type T. + */ + template + static vtable_type* vtable_for_type() + { + using VTableType = typename std::conditional::value, vtable_dynamic, vtable_stack>::type; + static vtable_type table = { VTableType::type, VTableType::destroy, VTableType::copy, VTableType::move, VTableType::swap }; + return &table; + } + + +protected: + template + friend const T* any_cast(const any* operand) noexcept; + + + template + friend T* any_cast(any* operand) noexcept; + + + /** + * Same effect as is_same(this->type(), t); + */ + bool is_typed(const std::type_info& t) const + { + return is_same(this->type(), t); + } + + + /** + * Checks if two type infos are the same. + * If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the + * type infos, otherwise does an actual comparision. Checking addresses is + * only a valid approach when there's no interaction with outside sources + * (other shared libraries and such). + */ + static bool is_same(const std::type_info& a, const std::type_info& b) + { +#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE + return &a == &b; +#else + return a == b; +#endif + } + + + /** + * Casts (with no type_info checks) the storage pointer as const T*. + */ + template + const T* cast() const noexcept + { + return requires_allocation::type>::value ? reinterpret_cast(storage.dynamic) : reinterpret_cast(&storage.stack); + } + + + /** + * Casts (with no type_info checks) the storage pointer as T*. + */ + template + T* cast() noexcept + { + return requires_allocation::type>::value ? reinterpret_cast(storage.dynamic) : reinterpret_cast(&storage.stack); + } + + +private: + storage_union storage; // On offset(0) so no padding for align + + vtable_type* vtable; + + + template + typename std::enable_if::value>::type do_construct(ValueType&& value) + { + storage.dynamic = new T(std::forward(value)); + } + + + template + typename std::enable_if::value>::type do_construct(ValueType&& value) + { + new (&storage.stack) T(std::forward(value)); + } + + + /** + * Chooses between stack and dynamic allocation for the type decay_t, + * assigns the correct vtable, and constructs the object on our storage. + */ + template + void construct(ValueType&& value) + { + using T = typename std::decay::type; + + this->vtable = vtable_for_type(); + + do_construct(std::forward(value)); + } +}; + + +namespace detail +{ + template + inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::true_type) + { + return std::move(*p); + } + + + template + inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::false_type) + { + return *p; + } +} + + +/** + * Performs *any_cast>>(&operand), or throws bad_any_cast on failure. + */ +template +inline ValueType any_cast(const any& operand) +{ + auto p = any_cast::type>::type>(&operand); + if(p == nullptr) { + std::cout << "libany err-01\n"; + + throw bad_any_cast(); + } + return *p; +} + + +/** + * Performs *any_cast>(&operand), or throws bad_any_cast on failure. + */ +template +inline ValueType any_cast(any& operand) +{ + auto p = any_cast::type>(&operand); + if(p == nullptr) throw bad_any_cast(); + return *p; +} + + +/** + * If ANY_IMPL_ANYCAST_MOVEABLE is not defined, does as N4562 specifies: + * Performs *any_cast>(&operand), or throws bad_any_cast on failure. + * + * If ANY_IMPL_ANYCAST_MOVEABLE is defined, does as LWG Defect 2509 specifies [1]: + * If ValueType is MoveConstructible and isn't a lvalue reference, performs + * std::move(*any_cast>(&operand)), otherwise + * *any_cast>(&operand). + * Throws bad_any_cast on failure. + * + * [1] https://cplusplus.github.io/LWG/lwg-active.html#2509 + */ +template +inline ValueType any_cast(any&& operand) +{ +#ifdef ANY_IMPL_ANY_CAST_MOVEABLE + using can_move = std::integral_constant::value && !std::is_lvalue_reference::value>; +#else + using can_move = std::false_type; +#endif + + auto p = any_cast::type>(&operand); + if(p == nullptr) throw bad_any_cast(); + return detail::any_cast_move_if_true(p, can_move()); +} + + +/** + * If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object + * contained by operand, otherwise nullptr. + */ +template +inline const T* any_cast(const any* operand) noexcept +{ + if(operand == nullptr || !operand->is_typed(typeid(T))) + return nullptr; + else + return operand->cast(); +} + + +/** + * If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object + * contained by operand, otherwise nullptr. + */ +template +inline T* any_cast(any* operand) noexcept +{ + if(operand == nullptr || !operand->is_typed(typeid(T))) + return nullptr; + else + return operand->cast(); +} + + +inline void swap(any& lhs, any& rhs) noexcept +{ + lhs.swap(rhs); +} + +} + +#endif /* ANY_H */ diff --git a/v4/any.h b/v4/any.h index 0e6f6fb..1ba4b84 100644 --- a/v4/any.h +++ b/v4/any.h @@ -8,13 +8,15 @@ * + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any * + https://cplusplus.github.io/LWG/lwg-active.html#2509 * - * Copyright (c) 2016 Denilson das Mercês Amorim + * Copyright (c) 2016 Denilson das Merc?s Amorim * Copyright (c) 2018 Claudio Fantacci * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE.md or copy at http://www.boost.org/LICENSE_1_0.txt) */ +#include + #ifndef ANY_H #define ANY_H @@ -467,7 +469,10 @@ template inline ValueType any_cast(const any& operand) { auto p = any_cast::type>::type>(&operand); - if(p == nullptr) throw bad_any_cast(); + if(p == nullptr) { + std::cout << "libany err-01\n"; + throw bad_any_cast(); + } return *p; } @@ -479,7 +484,11 @@ template inline ValueType any_cast(any& operand) { auto p = any_cast::type>(&operand); - if(p == nullptr) throw bad_any_cast(); + if(p == nullptr) { + std::cout << "libany err-02\n"; + throw bad_any_cast(); + } + return *p; } @@ -506,7 +515,10 @@ inline ValueType any_cast(any&& operand) #endif auto p = any_cast::type>(&operand); - if(p == nullptr) throw bad_any_cast(); + if(p == nullptr) { + std::cout << "libany err-03\n"; + throw bad_any_cast(); + } return detail::any_cast_move_if_true(p, can_move()); } diff --git a/v4/ctx.h b/v4/ctx.h index a6f27cd..5cd47eb 100644 --- a/v4/ctx.h +++ b/v4/ctx.h @@ -1,6 +1,7 @@ #include #include #include +#include "any.h" #ifndef ctx_HEADER #define ctx_HEADER diff --git a/v4/ctx_test2.cpp b/v4/ctx_test2.cpp index e22c7bc..58b360f 100644 --- a/v4/ctx_test2.cpp +++ b/v4/ctx_test2.cpp @@ -9,8 +9,10 @@ std::string ctx_test_Controller_executeFunctions_set() { ctx_Controller ctrl(c); // Disable automatic invocation of executeFunctions. ctrl.isProcessingQueue = true; + std::cout << "01\n"; ctrl.set("input", "123"); std::cout << "input: '" << ctrl.context.input << "'" << std::endl; + std::cout << "02\n"; auto processInput = [](memory_Context c) -> memory_Context { if (c.recentField == "input") { diff --git a/v4/memory_Context.cpp b/v4/memory_Context.cpp index d21fb82..4b49cd9 100644 --- a/v4/memory_Context.cpp +++ b/v4/memory_Context.cpp @@ -1,5 +1,91 @@ +#include +#include "any.h" #include "memory_Context.h" +#include + +libany::any memory_Context::field(const std::string &fieldName) { + if (fieldName == "didLaunch") { + return didLaunch; + } else if (fieldName == "exit") { + return exit; + } else if (fieldName == "hiddenItems") { + return hiddenItems; + } else if (fieldName == "input") { + return input; + } else if (fieldName == "mismatchedItems") { + return mismatchedItems; + } else if (fieldName == "outputGoOn") { + return outputGoOn; + } else if (fieldName == "outputGreeting") { + return outputGreeting; + } else if (fieldName == "outputHelp") { + return outputHelp; + } else if (fieldName == "outputMatchedItems") { + return outputMatchedItems; + } else if (fieldName == "outputMismatchedItems") { + return outputMismatchedItems; + } else if (fieldName == "outputPromptSelection") { + return outputPromptSelection; + } else if (fieldName == "outputVictory") { + return outputVictory; + } else if (fieldName == "playfieldItems") { + return playfieldItems; + } else if (fieldName == "playfieldSize") { + return playfieldSize; + } else if (fieldName == "selectedId") { + return selectedId; + } else if (fieldName == "selectedItems") { + return selectedItems; + } + // victory + return victory; +} + +void memory_Context::setField(const std::string &fieldName, libany::any value) { + if (fieldName == "didLaunch") { + didLaunch = libany::any_cast(value); + } else if (fieldName == "exit") { + exit = libany::any_cast(value); + } else if (fieldName == "hiddenItems") { + hiddenItems = libany::any_cast >(value); + } else if (fieldName == "input") { + std::cout << "memory_Context-01\n"; + try { + input = libany::any_cast(value); + } catch (libany::bad_any_cast &e) { + std::cout << "memory_Context-01 exception/type: " << e.what() << ", " << value.type().name() << std::endl; + } + std::cout << "memory_Context-02\n"; + } else if (fieldName == "mismatchedItems") { + mismatchedItems = libany::any_cast >(value); + } else if (fieldName == "outputGoOn") { + outputGoOn = libany::any_cast(value); + } else if (fieldName == "outputGreeting") { + outputGreeting = libany::any_cast(value); + } else if (fieldName == "outputHelp") { + outputHelp = libany::any_cast(value); + } else if (fieldName == "outputMatchedItems") { + outputMatchedItems = libany::any_cast(value); + } else if (fieldName == "outputMismatchedItems") { + outputMismatchedItems = libany::any_cast(value); + } else if (fieldName == "outputPromptSelection") { + outputPromptSelection = libany::any_cast(value); + } else if (fieldName == "outputVictory") { + outputVictory = libany::any_cast(value); + } else if (fieldName == "playfieldItems") { + playfieldItems = libany::any_cast >(value); + } else if (fieldName == "playfieldSize") { + playfieldSize = libany::any_cast(value); + } else if (fieldName == "selectedId") { + selectedId = libany::any_cast(value); + } else if (fieldName == "selectedItems") { + selectedItems = libany::any_cast >(value); + } else if (fieldName == "victory") { + victory = libany::any_cast(value); + } +} + memory_Context memory_createContext() { return memory_Context(); } diff --git a/v4/memory_Context.h b/v4/memory_Context.h index b2d551d..78997fe 100644 --- a/v4/memory_Context.h +++ b/v4/memory_Context.h @@ -9,10 +9,9 @@ struct memory_Context { bool didLaunch = false; bool exit = false; - std::string input = ""; std::vector hiddenItems; + std::string input = ""; std::vector mismatchedItems; - std::string recentField = "none"; std::string outputGoOn = ""; std::string outputGreeting = ""; std::string outputHelp = ""; @@ -22,101 +21,13 @@ struct memory_Context { std::string outputVictory = ""; std::map playfieldItems; int playfieldSize = 0; + std::string recentField = "none"; int selectedId = -1; std::vector selectedItems; bool victory = false; - template T field(const std::string &fieldName) { - if (fieldName == "didLaunch") { - return *reinterpret_cast(&didLaunch); - } else if (fieldName == "exit") { - return *reinterpret_cast(&exit); - } else if (fieldName == "input") { - return *reinterpret_cast(&input); - } else if (fieldName == "hiddenItems") { - return *reinterpret_cast(&hiddenItems); - } else if (fieldName == "mismatchedItems") { - return *reinterpret_cast(&mismatchedItems); - } else if (fieldName == "outputGoOn") { - return *reinterpret_cast(&outputGoOn); - } else if (fieldName == "outputGreeting") { - return *reinterpret_cast(&outputGreeting); - } else if (fieldName == "outputHelp") { - return *reinterpret_cast(&outputHelp); - } else if (fieldName == "outputMatchedItems") { - return *reinterpret_cast(&outputMatchedItems); - } else if (fieldName == "outputMismatchedItems") { - return *reinterpret_cast(&outputMismatchedItems); - } else if (fieldName == "outputPromptSelection") { - return *reinterpret_cast(&outputPromptSelection); - } else if (fieldName == "outputVictory") { - return *reinterpret_cast(&outputVictory); - } else if (fieldName == "playfieldItems") { - return *reinterpret_cast(&playfieldItems); - } else if (fieldName == "playfieldSize") { - return *reinterpret_cast(&playfieldSize); - } else if (fieldName == "selectedId") { - return *reinterpret_cast(&selectedId); - } else if (fieldName == "selectedItems") { - return *reinterpret_cast(&selectedItems); - } - // victory - return *reinterpret_cast(&victory); - } - - void setField(const std::string &fieldName, bool value) { - if (fieldName == "didLaunch") { - didLaunch = value; - } else if (fieldName == "exit") { - exit = value; - } else if (fieldName == "victory") { - victory = value; - } - } - - void setField(const std::string &fieldName, const std::string &value) { - if (fieldName == "input") { - input = value; - } else if (fieldName == "outputGoOn") { - outputGoOn = value; - } else if (fieldName == "outputGreeting") { - outputGreeting = value; - } else if (fieldName == "outputHelp") { - outputHelp = value; - } else if (fieldName == "outputMatchedItems") { - outputMatchedItems = value; - } else if (fieldName == "outputMismatchedItems") { - outputMismatchedItems = value; - } else if (fieldName == "outputPromptSelection") { - outputPromptSelection = value; - } else if (fieldName == "outputVictory") { - outputVictory = value; - } - } - - void setField(const std::string &fieldName, const std::vector &value) { - if (fieldName == "hiddenItems") { - hiddenItems = value; - } else if (fieldName == "mismatchedItems") { - mismatchedItems = value; - } else if (fieldName == "selectedItems") { - selectedItems = value; - } - } - - void setField(const std::string &fieldName, const std::map &value) { - if (fieldName == "playfieldItems") { - playfieldItems = value; - } - } - - void setField(const std::string &fieldName, int value) { - if (fieldName == "playfieldSize") { - playfieldSize = value; - } else if (fieldName == "selectedId") { - selectedId = value; - } - } + libany::any field(const std::string &fieldName); + void setField(const std::string &fieldName, libany::any value); }; memory_Context memory_createContext();