diff --git a/Content/Player/CPlayerCharacter_BP.uasset b/Content/Player/CPlayerCharacter_BP.uasset index 1b17222..559024f 100644 --- a/Content/Player/CPlayerCharacter_BP.uasset +++ b/Content/Player/CPlayerCharacter_BP.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc9b3e36eac8f4064f0b4929deac68a2b097d610483441f13a883eafc52cc91d -size 45292 +oid sha256:b640ae13c56425af6299c324cd36d8e44d41bbeccbf310f8879c0c582abddfb7 +size 67485 diff --git a/Plugins/Developer/RiderLink/Resources/Icon128.png b/Plugins/Developer/RiderLink/Resources/Icon128.png new file mode 100644 index 0000000..4160954 Binary files /dev/null and b/Plugins/Developer/RiderLink/Resources/Icon128.png differ diff --git a/Plugins/Developer/RiderLink/Resources/checksum b/Plugins/Developer/RiderLink/Resources/checksum new file mode 100644 index 0000000..671758e --- /dev/null +++ b/Plugins/Developer/RiderLink/Resources/checksum @@ -0,0 +1 @@ +/££ù„ n“{‘ÞïF™àn \ No newline at end of file diff --git a/Plugins/Developer/RiderLink/RiderLink.uplugin b/Plugins/Developer/RiderLink/RiderLink.uplugin new file mode 100644 index 0000000..b0b7a12 --- /dev/null +++ b/Plugins/Developer/RiderLink/RiderLink.uplugin @@ -0,0 +1,61 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "2025.1.3", + "FriendlyName": "RiderLink", + "Description": "Plugin for establishing IPC connection with JetBrains Rider IDE", + "Category": "Programming", + "CreatedBy": "JetBrains", + "CreatedByURL": "https://www.jetbrains.com/", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": false, + "Installed": false, + "Modules": [ + { + "Name": "RD", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PreDefault" + }, + { + "Name": "RiderLink", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PreDefault" + }, + { + "Name": "RiderLogging", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PreDefault" + }, + { + "Name": "RiderBlueprint", + "Type": "EditorNoCommandlet", + "LoadingPhase": "Default" + }, + { + "Name": "RiderGameControl", + "Type": "EditorNoCommandlet", + "LoadingPhase": "Default" + }, + { + "Name": "RiderShaderInfo", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PostEngineInit" + }, + { + "Name": "RiderLC", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PostEngineInit" + }, + { + "Name": "RiderDebuggerSupport", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PreDefault", + "PlatformAllowList": [ + "Win64" + ] + } + ], + "EnabledByDefault": true +} \ No newline at end of file diff --git a/Plugins/Developer/RiderLink/Source/RD/RD.Build.cs b/Plugins/Developer/RiderLink/Source/RD/RD.Build.cs new file mode 100644 index 0000000..1376c85 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/RD.Build.cs @@ -0,0 +1,93 @@ +using System.IO; +using UnrealBuildTool; + +public class RD : ModuleRules +{ + public RD(ReadOnlyTargetRules Target) : base(Target) + { + PublicDependencyModuleNames.Add("Core"); + bUseRTTI = true; + +#if UE_5_2_OR_LATER + bDisableStaticAnalysis = true; +#endif + +#if UE_5_2_OR_LATER + IWYUSupport = IWYUSupport.KeepAsIs; +#else + bEnforceIWYU = false; +#endif + +#if UE_5_6_OR_LATER + CppStandard = CppStandardVersion.Cpp20; +#elif UE_4_22_OR_LATER + CppStandard = CppStandardVersion.Cpp17; +#endif + +#if UE_4_22_OR_LATER + PCHUsage = PCHUsageMode.NoPCHs; +#else + PCHUsage = PCHUsageMode.NoSharedPCHs; +#endif + +#if UE_5_6_OR_LATER + CppCompileWarningSettings.ShadowVariableWarningLevel = WarningLevel.Off; +#elif UE_4_24_OR_LATER + ShadowVariableWarningLevel = WarningLevel.Off; +#else + bEnableShadowVariableWarnings = false; +#endif + +#if UE_4_24_OR_LATER + bUseUnity = false; +#else + bFasterWithoutUnity = true; +#endif + + PublicDefinitions.Add("_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS"); + + if (Target.Platform == UnrealTargetPlatform.Win64) + { + PublicDefinitions.Add("_WINSOCK_DEPRECATED_NO_WARNINGS"); + PublicDefinitions.Add("_CRT_SECURE_NO_WARNINGS"); + PublicDefinitions.Add("_CRT_NONSTDC_NO_DEPRECATE"); + PublicDefinitions.Add("SPDLOG_WCHAR_FILENAMES"); + PublicDefinitions.Add("SPDLOG_WCHAR_TO_UTF8_SUPPORT"); + PrivateDefinitions.Add("WIN32_LEAN_AND_MEAN"); + } + + if (Target.Platform == UnrealTargetPlatform.Mac) + { + PublicDefinitions.Add("_DARWIN"); + } + + // Common dependencies + PrivateDefinitions.Add("rd_framework_cpp_EXPORTS"); + PrivateDefinitions.Add("rd_core_cpp_EXPORTS"); + PrivateDefinitions.Add("spdlog_EXPORTS"); + PrivateDefinitions.Add("FMT_EXPORT"); + + PublicDefinitions.Add("SPDLOG_NO_EXCEPTIONS"); + PublicDefinitions.Add("SPDLOG_COMPILED_LIB"); + PublicDefinitions.Add("SPDLOG_SHARED_LIB"); + PublicDefinitions.Add( + "nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_NONSTD"); + PublicDefinitions.Add("FMT_SHARED"); + + string[] Paths = + { + "src", "src/rd_core_cpp", "src/rd_core_cpp/src/main" + , "src/rd_framework_cpp", "src/rd_framework_cpp/src/main" + , "src/rd_framework_cpp/src/main/util", "src/rd_gen_cpp/src" + , "thirdparty", "thirdparty/ordered-map/include" + , "thirdparty/optional/tl", "thirdparty/variant/include" + , "thirdparty/string-view-lite/include", "thirdparty/spdlog/include" + , "thirdparty/clsocket/src", "thirdparty/CTPL/include", "thirdparty/utf-cpp/include" + }; + + foreach (var Item in Paths) + { + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, Item)); + } + } +} diff --git a/Plugins/Developer/RiderLink/Source/RD/RD.cpp b/Plugins/Developer/RiderLink/Source/RD/RD.cpp new file mode 100644 index 0000000..3840428 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/RD.cpp @@ -0,0 +1,11 @@ +#include "RD.h" + +#include + +#define LOCTEXT_NAMESPACE "RD" + +DEFINE_LOG_CATEGORY(FLogRDModule); + +IMPLEMENT_MODULE(FRDModule, RD); + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Plugins/Developer/RiderLink/Source/RD/RD.h b/Plugins/Developer/RiderLink/Source/RD/RD.h new file mode 100644 index 0000000..4a477e1 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/RD.h @@ -0,0 +1,16 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Logging/LogMacros.h" +#include "Logging/LogVerbosity.h" +#include "Modules/ModuleInterface.h" + +DECLARE_LOG_CATEGORY_EXTERN(FLogRDModule, Log, All); + +class FRDModule : public IModuleInterface +{ +public: + FRDModule() = default; + ~FRDModule() = default; +}; diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/rd_core_export.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/rd_core_export.h new file mode 100644 index 0000000..50b8f72 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/rd_core_export.h @@ -0,0 +1,83 @@ + +#ifndef RD_CORE_API_H +#define RD_CORE_API_H + +#if defined(_WIN32) +#ifdef RD_CORE_STATIC_DEFINE +# define RD_CORE_API +# define RD_CORE_NO_EXPORT +#else +# ifndef RD_CORE_API +# ifdef rd_core_cpp_EXPORTS + /* We are building this library */ +# define RD_CORE_API __declspec(dllexport) +# else + /* We are using this library */ +# define RD_CORE_API __declspec(dllimport) +# endif +# endif + +# ifndef RD_CORE_NO_EXPORT +# define RD_CORE_NO_EXPORT +# endif +#endif + +#ifndef RD_CORE_DEPRECATED +# define RD_CORE_DEPRECATED __declspec(deprecated) +#endif + +#ifndef RD_CORE_DEPRECATED_EXPORT +# define RD_CORE_DEPRECATED_EXPORT RD_CORE_API RD_CORE_DEPRECATED +#endif + +#ifndef RD_CORE_DEPRECATED_NO_EXPORT +# define RD_CORE_DEPRECATED_NO_EXPORT RD_CORE_NO_EXPORT RD_CORE_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef RD_CORE_NO_DEPRECATED +# define RD_CORE_NO_DEPRECATED +# endif +#endif +#endif + +#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) +#ifdef RD_CORE_STATIC_DEFINE +# define RD_CORE_API +# define RD_CORE_NO_EXPORT +#else +# ifndef RD_CORE_API +# ifdef rd_core_cpp_EXPORTS + /* We are building this library */ +# define RD_CORE_API __attribute__((visibility("default"))) +# else + /* We are using this library */ +# define RD_CORE_API __attribute__((visibility("default"))) +# endif +# endif + +# ifndef RD_CORE_NO_EXPORT +# define RD_CORE_NO_EXPORT __attribute__((visibility("hidden"))) +# endif +#endif + +#ifndef RD_CORE_DEPRECATED +# define RD_CORE_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef RD_CORE_DEPRECATED_EXPORT +# define RD_CORE_DEPRECATED_EXPORT RD_CORE_API RD_CORE_DEPRECATED +#endif + +#ifndef RD_CORE_DEPRECATED_NO_EXPORT +# define RD_CORE_DEPRECATED_NO_EXPORT RD_CORE_NO_EXPORT RD_CORE_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef RD_CORE_NO_DEPRECATED +# define RD_CORE_NO_DEPRECATED +# endif +#endif +#endif + +#endif /* RD_CORE_API_H */ diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.cpp b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.cpp new file mode 100644 index 0000000..d63a9bf --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.cpp @@ -0,0 +1,49 @@ +#include "Lifetime.h" + +#include + +#include +#include +#include + +namespace rd +{ +/*thread_local */ Lifetime::Allocator Lifetime::allocator; + +LifetimeImpl* Lifetime::operator->() const +{ + return ptr.operator->(); +} + +std::once_flag onceFlag; + +Lifetime::Lifetime(bool is_eternal) : ptr(std::allocate_shared(allocator, is_eternal)) +{ + std::call_once(onceFlag, [] { + spdlog::set_default_logger(spdlog::stderr_color_mt("default", spdlog::color_mode::automatic)); + }); +} + +Lifetime Lifetime::create_nested() const +{ + Lifetime lw(false); + ptr->attach_nested(lw.ptr); + return lw; +} + +Lifetime const& Lifetime::Eternal() +{ + static Lifetime ETERNAL(true); + return ETERNAL; +} + +bool operator==(Lifetime const& lw1, Lifetime const& lw2) +{ + return lw1.ptr == lw2.ptr; +} + +bool operator!=(Lifetime const& lw1, Lifetime const& lw2) +{ + return !(lw1 == lw2); +} +} // namespace rd diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.h new file mode 100644 index 0000000..72c48cf --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.h @@ -0,0 +1,76 @@ +#ifndef RD_CPP_CORE_LIFETIMEWRAPPER_H +#define RD_CPP_CORE_LIFETIMEWRAPPER_H + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +#include "LifetimeImpl.h" + +#include + +#include + +#include + +namespace rd +{ +class Lifetime; + +template <> +struct RD_CORE_API hash +{ + size_t operator()(const Lifetime& value) const noexcept; +}; + +class RD_CORE_API Lifetime final +{ +private: + using Allocator = std::allocator; + + static /*thread_local */ Allocator allocator; + + friend class LifetimeDefinition; + + friend struct hash; + + std::shared_ptr ptr; + +public: + static Lifetime const& Eternal(); + + // region ctor/dtor + + Lifetime(Lifetime const& other) = default; + + Lifetime& operator=(Lifetime const& other) = default; + + Lifetime(Lifetime&& other) noexcept = default; + + Lifetime& operator=(Lifetime&& other) noexcept = default; + + ~Lifetime() = default; + // endregion + + friend bool RD_CORE_API operator==(Lifetime const& lw1, Lifetime const& lw2); + friend bool RD_CORE_API operator!=(Lifetime const& lw1, Lifetime const& lw2); + + explicit Lifetime(bool is_eternal = false); + + LifetimeImpl* operator->() const; + + Lifetime create_nested() const; +}; + +inline size_t hash::operator()(const Lifetime& value) const noexcept +{ + return hash >()(value.ptr); +} +} // namespace rd +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + +#endif // RD_CPP_CORE_LIFETIMEWRAPPER_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.cpp b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.cpp new file mode 100644 index 0000000..afadd73 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.cpp @@ -0,0 +1,54 @@ +#include "LifetimeDefinition.h" + +#include + +namespace rd +{ +LifetimeDefinition::LifetimeDefinition(bool eternaled) : eternaled(eternaled), lifetime(eternaled) +{ +} + +LifetimeDefinition::LifetimeDefinition(const Lifetime& parent) : LifetimeDefinition(false) +{ + parent->attach_nested(lifetime.ptr); +} + +bool LifetimeDefinition::is_terminated() const +{ + return lifetime->is_terminated(); +} + +void LifetimeDefinition::terminate() +{ + lifetime->terminate(); +} + +bool LifetimeDefinition::is_eternal() const +{ + return lifetime->is_eternal(); +} + +namespace +{ +LifetimeDefinition ETERNAL(true); +} + +std::shared_ptr LifetimeDefinition::get_shared_eternal() +{ + return std::shared_ptr(&ETERNAL, [](LifetimeDefinition* /*ld*/) {}); +} + +LifetimeDefinition::~LifetimeDefinition() +{ + if (lifetime.ptr != nullptr) + { // wasn't moved + if (!is_eternal()) + { + if (!lifetime->is_terminated()) + { + lifetime->terminate(); + } + } + } +} +} // namespace rd diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.h new file mode 100644 index 0000000..f28584d --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.h @@ -0,0 +1,59 @@ +#ifndef RD_CPP_CORE_LIFETIME_DEFINITION_H +#define RD_CPP_CORE_LIFETIME_DEFINITION_H + +#include "util/core_traits.h" + +#include "LifetimeImpl.h" +#include "Lifetime.h" + +#include +#include + +#include + +namespace rd +{ +class RD_CORE_API LifetimeDefinition +{ +private: + friend class SequentialLifetimes; + + bool eternaled = false; + +public: + Lifetime lifetime; + + explicit LifetimeDefinition(bool is_eternal = false); + + explicit LifetimeDefinition(const Lifetime& parent); + + LifetimeDefinition(LifetimeDefinition const& other) = delete; + + LifetimeDefinition& operator=(LifetimeDefinition const& other) = delete; + + LifetimeDefinition(LifetimeDefinition&& other) = default; + + LifetimeDefinition& operator=(LifetimeDefinition&& other) = default; + + virtual ~LifetimeDefinition(); + + // static std::shared_ptr eternal; + static std::shared_ptr get_shared_eternal(); + + bool is_terminated() const; + + bool is_eternal() const; + + void terminate(); + + template + static auto use(F&& block) -> typename util::result_of_t + { + LifetimeDefinition definition(false); + Lifetime lw = definition.lifetime.create_nested(); + return block(lw); + } +}; +} // namespace rd + +#endif // RD_CPP_CORE_LIFETIME_DEFINITION_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.cpp b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.cpp new file mode 100644 index 0000000..fa6953b --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.cpp @@ -0,0 +1,66 @@ +#include "LifetimeImpl.h" + +#include + +namespace rd +{ +#if __cplusplus < 201703L +LifetimeImpl::counter_t LifetimeImpl::get_id = 0; +#endif + +LifetimeImpl::LifetimeImpl(bool is_eternal) : eternaled(is_eternal), id(LifetimeImpl::get_id++) +{ +} + +void LifetimeImpl::terminate() +{ + if (is_eternal()) + return; + + terminated = true; + + // region thread-safety section + + actions_t actions_copy; + { + std::lock_guard guard(actions_lock); + actions_copy = std::move(actions); + + actions.clear(); + } + // endregion + + for (auto it = actions_copy.rbegin(); it != actions_copy.rend(); ++it) + { + it->second(); + } +} + +bool LifetimeImpl::is_terminated() const +{ + return terminated; +} + +bool LifetimeImpl::is_eternal() const +{ + return eternaled; +} + +void LifetimeImpl::attach_nested(std::shared_ptr nested) +{ + if (nested->is_terminated() || is_eternal()) + return; + + std::function action = [nested] { nested->terminate(); }; + counter_t action_id = add_action(action); + nested->add_action([this, id = action_id] { actions.erase(id); }); +} + +LifetimeImpl::~LifetimeImpl() +{ + /*if (!is_eternal() && !is_terminated()) { + spdlog::error("forget to terminate lifetime with id: {}", to_string(id)); + terminate(); + }*/ +} +} // namespace rd diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.h new file mode 100644 index 0000000..455f28c --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.h @@ -0,0 +1,108 @@ +#ifndef RD_CPP_CORE_LIFETIME_H +#define RD_CPP_CORE_LIFETIME_H + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace rd +{ +class RD_CORE_API LifetimeImpl final +{ +public: + friend class LifetimeDefinition; + + friend class Lifetime; + + using counter_t = int32_t; + +private: + bool eternaled = false; + std::atomic terminated{false}; + + counter_t id = 0; + + counter_t action_id_in_map = 0; + using actions_t = ordered_map, rd::hash>; + actions_t actions; + + void terminate(); + + std::mutex actions_lock; + +public: + // region ctor/dtor + explicit LifetimeImpl(bool is_eternal = false); + + LifetimeImpl(LifetimeImpl const& other) = delete; + + ~LifetimeImpl(); + // endregion + + template + counter_t add_action(F&& action) + { + std::lock_guard guard(actions_lock); + + if (is_eternal()) + { + return -1; + } + if (is_terminated()) + { + throw std::invalid_argument("Already Terminated"); + } + + actions[action_id_in_map] = std::forward(action); + return action_id_in_map++; + } + + void remove_action(counter_t i) + { + std::lock_guard guard(actions_lock); + + actions.erase(i); + } + +#if __cplusplus >= 201703L + static inline counter_t get_id = 0; +#else + static counter_t get_id; +#endif + + template + void bracket(F&& opening, G&& closing) + { + if (is_terminated()) + return; + opening(); + add_action(std::forward(closing)); + } + + bool is_terminated() const; + + bool is_eternal() const; + + void attach_nested(std::shared_ptr nested); +}; +} // namespace rd +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + +#endif // RD_CPP_CORE_LIFETIME_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.cpp b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.cpp new file mode 100644 index 0000000..f57c5c6 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.cpp @@ -0,0 +1,33 @@ +#include "SequentialLifetimes.h" + +namespace rd +{ +SequentialLifetimes::SequentialLifetimes(Lifetime parent_lifetime) : parent_lifetime(std::move(parent_lifetime)) +{ + this->parent_lifetime->add_action([this] { set_current_lifetime(LifetimeDefinition::get_shared_eternal()); }); +} + +Lifetime SequentialLifetimes::next() +{ + std::shared_ptr new_def = std::make_shared(parent_lifetime); + set_current_lifetime(new_def); + return current_def->lifetime; +} + +void SequentialLifetimes::terminate_current() +{ + set_current_lifetime(LifetimeDefinition::get_shared_eternal()); +} + +bool SequentialLifetimes::is_terminated() const +{ + return current_def->is_eternal() || current_def->is_terminated(); +} + +void SequentialLifetimes::set_current_lifetime(std::shared_ptr new_def) +{ + std::shared_ptr prev = current_def; + current_def = new_def; + prev->terminate(); +} +} // namespace rd diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.h new file mode 100644 index 0000000..2a42275 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.h @@ -0,0 +1,51 @@ +#ifndef RD_CPP_CORE_SEQUENTIAL_LIFETIMES_H +#define RD_CPP_CORE_SEQUENTIAL_LIFETIMES_H + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +#include "LifetimeDefinition.h" +#include "Lifetime.h" + +#include + +namespace rd +{ +class RD_CORE_API SequentialLifetimes +{ +private: + std::shared_ptr current_def = LifetimeDefinition::get_shared_eternal(); + Lifetime parent_lifetime; + +public: + // region ctor/dtor + SequentialLifetimes() = delete; + + SequentialLifetimes(SequentialLifetimes const&) = delete; + + SequentialLifetimes& operator=(SequentialLifetimes const&) = delete; + + SequentialLifetimes(SequentialLifetimes&&) = delete; + + SequentialLifetimes& operator=(SequentialLifetimes&&) = delete; + + explicit SequentialLifetimes(Lifetime parent_lifetime); + // endregion + + Lifetime next(); + + void terminate_current(); + + bool is_terminated() const; + + void set_current_lifetime(std::shared_ptr new_def); +}; +} // namespace rd +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + +#endif // RD_CPP_CORE_SEQUENTIAL_LIFETIMES_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/Property.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/Property.h new file mode 100644 index 0000000..24fc8b1 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/Property.h @@ -0,0 +1,75 @@ +#ifndef RD_CPP_CORE_PROPERTY_H +#define RD_CPP_CORE_PROPERTY_H + +#include "base/IProperty.h" +#include "reactive/base/SignalX.h" + +#include + +namespace rd +{ +/** + * \brief complete class which has \a Property 's properties. + * \tparam T type of stored value (may be abstract) + */ +template +class Property : public IProperty +{ + using WT = typename IProperty::WT; + +public: + // region ctor/dtor + + Property() = default; + + Property(Property&& other) = default; + + Property& operator=(Property&& other) = default; + + virtual ~Property() = default; + + template + explicit Property(F&& value) : IProperty(std::forward(value)) + { + } + // endregion + + T const& get() const override + { + RD_ASSERT_THROW_MSG(this->has_value(), "get of uninitialized value from property"); + return *(this->value); + } + + void set(WT new_value) const override + { + if (!this->has_value() || (this->get() != wrapper::get(new_value))) + { + if (this->has_value()) + { + this->before_change.fire(*(this->value)); + } + this->value = std::move(new_value); + this->change.fire(*(this->value)); + } + } + + friend bool operator==(const Property& lhs, const Property& rhs) + { + return &lhs == &rhs; + } + + friend bool operator!=(const Property& lhs, const Property& rhs) + { + return !(rhs == lhs); + } + + friend std::string to_string(Property const& value) + { + return value.has_value() ? to_string(value.get()) : "empty property"s; + } +}; +} // namespace rd + +static_assert(std::is_move_constructible>::value, "Is not move constructible from Property"); + +#endif // RD_CPP_CORE_PROPERTY_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableList.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableList.h new file mode 100644 index 0000000..e214e70 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableList.h @@ -0,0 +1,331 @@ +#ifndef RD_CPP_CORE_VIEWABLELIST_H +#define RD_CPP_CORE_VIEWABLELIST_H + +#include "base/IViewableList.h" +#include "reactive/base/SignalX.h" +#include "util/core_util.h" + +#include +#include +#include + +namespace rd +{ +/** + * \brief complete class which has @code IViewableList's properties + */ +template > +class ViewableList : public IViewableList +{ +public: + using Event = typename IViewableList::Event; + +private: + using WA = typename std::allocator_traits::template rebind_alloc>; + + using data_t = std::vector, WA>; + mutable data_t list; + Signal change; + +protected: + using WT = typename IViewableList::WT; + + const std::vector>& getList() const override + { + return list; + } + +public: + // region ctor/dtor + + ViewableList() = default; + + ViewableList(ViewableList&&) = default; + + ViewableList& operator=(ViewableList&&) = default; + + virtual ~ViewableList() = default; + + // endregion + + // region iterators +public: + class iterator + { + friend class ViewableList; + + typename data_t::iterator it_; + + explicit iterator(const typename data_t::iterator& it) : it_(it) + { + } + + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T const*; + using reference = T const&; + + iterator(const iterator& other) = default; + + iterator(iterator&& other) noexcept = default; + + iterator& operator=(const iterator& other) = default; + + iterator& operator=(iterator&& other) noexcept = default; + + iterator& operator++() + { + ++it_; + return *this; + } + + iterator operator++(int) + { + auto it = *this; + ++*this; + return it; + } + + iterator& operator--() + { + --it_; + return *this; + } + + iterator operator--(int) + { + auto it = *this; + --*this; + return it; + } + + iterator& operator+=(difference_type delta) + { + it_ += delta; + return *this; + } + + iterator& operator-=(difference_type delta) + { + it_ -= delta; + return *this; + } + + iterator operator+(difference_type delta) const + { + auto it = *this; + return it += delta; + } + + iterator operator-(difference_type delta) const + { + auto it = *this; + return it -= delta; + } + + difference_type operator-(iterator const& other) const + { + return it_ - other.it_; + } + + bool operator<(iterator const& other) const noexcept + { + return this->it_ < other.it_; + } + + bool operator>(iterator const& other) const noexcept + { + return this->it_ > other.it_; + } + + bool operator==(iterator const& other) const noexcept + { + return this->it_ == other.it_; + } + + bool operator!=(iterator const& other) const noexcept + { + return !(*this == other); + } + + bool operator<=(iterator const& other) const noexcept + { + return (this->it_ < other.it_) || (*this == other); + } + + bool operator>=(iterator const& other) const noexcept + { + return (this->it_ > other.it_) || (*this == other); + } + + reference operator*() noexcept + { + return **it_; + } + + reference operator*() const noexcept + { + return **it_; + } + + pointer operator->() noexcept + { + return (*it_).get(); + } + + pointer operator->() const noexcept + { + return (*it_).get(); + } + }; + + using reverse_iterator = std::reverse_iterator; + + iterator begin() const + { + return iterator(list.begin()); + } + + iterator end() const + { + return iterator(list.end()); + } + + reverse_iterator rbegin() const + { + return reverse_iterator(end()); + } + + reverse_iterator rend() const + { + return reverse_iterator(begin()); + } + // endregion + + void advise(Lifetime lifetime, std::function handler) const override + { + if (lifetime->is_terminated()) + return; + change.advise(lifetime, handler); + for (int32_t i = 0; i < static_cast(size()); ++i) + { + handler(typename Event::Add(i, &(*list[i]))); + } + } + + bool add(WT element) const override + { + list.emplace_back(std::move(element)); + change.fire(typename Event::Add(static_cast(size()) - 1, &(*list.back()))); + return true; + } + + bool add(size_t index, WT element) const override + { + list.emplace(list.begin() + index, std::move(element)); + change.fire(typename Event::Add(static_cast(index), &(*list[index]))); + return true; + } + + WT removeAt(size_t index) const override + { + auto res = std::move(list[index]); + list.erase(list.begin() + index); + + change.fire(typename Event::Remove(static_cast(index), &(*res))); + return wrapper::unwrap(std::move(res)); + } + + bool remove(T const& element) const override + { + auto it = std::find_if(list.begin(), list.end(), [&element](auto const& p) { return *p == element; }); + if (it == list.end()) + { + return false; + } + ViewableList::removeAt(std::distance(list.begin(), it)); + return true; + } + + T const& get(size_t index) const override + { + return *list[index]; + } + + WT set(size_t index, WT element) const override + { + auto old_value = std::move(list[index]); + list[index] = Wrapper(std::move(element)); + change.fire(typename Event::Update(static_cast(index), &(*old_value), &(*list[index]))); //??? + return wrapper::unwrap(std::move(old_value)); + } + + bool addAll(size_t index, std::vector elements) const override + { + for (auto& element : elements) + { + ViewableList::add(index, std::move(element)); + ++index; + } + return true; + } + + bool addAll(std::vector elements) const override + { + for (auto&& element : elements) + { + ViewableList::add(std::move(element)); + } + return true; + } + + void clear() const override + { + std::vector changes; + for (size_t i = size(); i > 0; --i) + { + changes.push_back(typename Event::Remove(static_cast(i - 1), &(*list[i - 1]))); + } + for (auto const& e : changes) + { + change.fire(e); + } + list.clear(); + } + + bool removeAll(std::vector elements) const override + { + // TO-DO faster + // std::unordered_set set(elements.begin(), elements.end()); + + bool res = false; + for (size_t i = list.size(); i > 0; --i) + { + auto const& x = list[i - 1]; + if (std::count_if(elements.begin(), elements.end(), + [&x](auto const& elem) { return wrapper::TransparentKeyEqual()(elem, x); }) > 0) + { + removeAt(i - 1); + res = true; + } + } + return res; + } + + size_t size() const override + { + return list.size(); + } + + bool empty() const override + { + return list.empty(); + } +}; +} // namespace rd + +static_assert(std::is_move_constructible>::value, "Is move constructible from ViewableList"); + +#endif // RD_CPP_CORE_VIEWABLELIST_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableMap.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableMap.h new file mode 100644 index 0000000..6626949 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableMap.h @@ -0,0 +1,335 @@ +#ifndef RD_CPP_CORE_VIEWABLE_MAP_H +#define RD_CPP_CORE_VIEWABLE_MAP_H + +#include "base/IViewableMap.h" +#include "reactive/base/SignalX.h" + +#include +#include + +#include + +#include +#include + +namespace rd +{ +/** + * \brief complete class which has @code IViewableMap's properties + */ +template , typename VA = std::allocator> +class ViewableMap : public IViewableMap +{ +public: + using Event = typename IViewableMap::Event; + +private: + using WK = typename IViewableMap::WK; + using WV = typename IViewableMap::WV; + using OV = typename IViewableMap::OV; + using PA = typename std::allocator_traits::template rebind_alloc, Wrapper>>; + + Signal change; + + using data_t = ordered_map, Wrapper, wrapper::TransparentHash, wrapper::TransparentKeyEqual, PA>; + mutable data_t map; + +public: + // region ctor/dtor + + ViewableMap() = default; + + ViewableMap(ViewableMap&&) = default; + + ViewableMap& operator=(ViewableMap&&) = default; + + virtual ~ViewableMap() = default; + // endregion + + // region iterators + +public: + class iterator + { + friend class ViewableMap; + + mutable typename data_t::iterator it_; + + explicit iterator(const typename data_t::iterator& it) : it_(it) + { + } + + public: + using iterator_category = typename data_t::iterator::iterator_category; + using key_type = K; + using value_type = V; + using difference_type = std::ptrdiff_t; + using reference = V const&; + using pointer = V const*; + + iterator(const iterator& other) = default; + + iterator(iterator&& other) noexcept = default; + + iterator& operator=(const iterator& other) = default; + + iterator& operator=(iterator&& other) noexcept = default; + + iterator& operator++() + { + ++it_; + return *this; + } + + iterator operator++(int) + { + auto it = *this; + ++*this; + return it; + } + + iterator& operator--() + { + --it_; + return *this; + } + + iterator operator--(int) + { + auto it = *this; + --*this; + return it; + } + + iterator& operator+=(difference_type delta) + { + it_ += delta; + return *this; + } + + iterator& operator-=(difference_type delta) + { + it_ -= delta; + return *this; + } + + iterator operator+(difference_type delta) const + { + auto it = *this; + return it += delta; + } + + iterator operator-(difference_type delta) const + { + auto it = *this; + return it -= delta; + } + + difference_type operator-(iterator const& other) const + { + return it_ - other.it_; + } + + bool operator<(iterator const& other) const noexcept + { + return this->it_ < other.it_; + } + + bool operator>(iterator const& other) const noexcept + { + return this->it_ > other.it_; + } + + bool operator==(iterator const& other) const noexcept + { + return this->it_ == other.it_; + } + + bool operator!=(iterator const& other) const noexcept + { + return !(*this == other); + } + + bool operator<=(iterator const& other) const noexcept + { + return (this->it_ < other.it_) || (*this == other); + } + + bool operator>=(iterator const& other) const noexcept + { + return (this->it_ > other.it_) || (*this == other); + } + + reference operator*() const noexcept + { + return *it_.value(); + } + + pointer operator->() const noexcept + { + return it_.value().get(); + } + + key_type const& key() const + { + return *it_.key(); + } + + value_type const& value() const + { + return *it_.value(); + } + }; + + class reverse_iterator : public std::reverse_iterator + { + using base_t = std::reverse_iterator; + + public: + using iterator_category = typename iterator::iterator_category; + using key_type = typename iterator::key_type; + using value_type = typename iterator::value_type; + using difference_type = typename iterator::difference_type; + using reference = typename iterator::reference; + using pointer = typename iterator::pointer; + + reverse_iterator(const reverse_iterator& other) = default; + + reverse_iterator& operator=(const reverse_iterator& other) = default; + + explicit reverse_iterator(const iterator& other) : base_t(other){}; + + reverse_iterator& operator=(const iterator& other) + { + static_cast(*this) = other; + }; + + key_type const& key() const + { + auto it = base_t::current; + return (--(it)).key(); + } + + value_type const& value() const + { + auto it = base_t::current; + return (--it).value(); + } + }; + + iterator begin() const + { + return iterator(map.begin()); + } + + iterator end() const + { + return iterator(map.end()); + } + + reverse_iterator rbegin() const + { + return reverse_iterator(end()); + } + + reverse_iterator rend() const + { + return reverse_iterator(begin()); + } + + // endregion + + void advise(Lifetime lifetime, std::function handler) const override + { + change.advise(lifetime, handler); + /*for (auto const &[key, value] : map) {*/ + for (auto const& it : map) + { + auto& key = it.first; + auto& value = it.second; + handler(Event(typename Event::Add(&(*key), &(*value)))); + ; + } + } + + const V* get(K const& key) const override + { + auto it = map.find(key); + if (it == map.end()) + { + return nullptr; + } + return &(*it->second); + } + + const V* set(WK key, WV value) const override + { + if (map.count(key) == 0) + { + /*auto[it, success] = map.emplace(std::make_unique(std::move(key)), std::make_unique(std::move(value)));*/ + auto node = map.emplace(std::move(key), std::move(value)); + auto& it = node.first; + auto const& key_ptr = it->first; + auto const& value_ptr = it->second; + change.fire(typename Event::Add(&(*key_ptr), &(*value_ptr))); + return nullptr; + } + else + { + auto it = map.find(key); + auto const& key_ptr = it->first; + auto const& value_ptr = it->second; + + if (*value_ptr != wrapper::get(value)) + { // TO-DO more effective + Wrapper old_value = std::move(map.at(key)); + + map.at(key_ptr) = Wrapper(std::move(value)); + change.fire(typename Event::Update(&(*key_ptr), &(*old_value), &(*value_ptr))); + } + return &*(value_ptr); + } + } + + OV remove(K const& key) const override + { + if (map.count(key) > 0) + { + Wrapper old_value = std::move(map.at(key)); + change.fire(typename Event::Remove(&key, &(*old_value))); + map.erase(key); + return wrapper::unwrap(std::move(old_value)); + } + return nullopt; + } + + void clear() const override + { + std::vector changes; + /*for (auto const &[key, value] : map) {*/ + for (auto const& it : map) + { + changes.push_back(typename Event::Remove(&(*it.first), &(*it.second))); + } + for (auto const& it : changes) + { + change.fire(it); + } + map.clear(); + } + + size_t size() const override + { + return map.size(); + } + + bool empty() const override + { + return map.empty(); + } +}; +} // namespace rd + +static_assert(std::is_move_constructible>::value, "Is move constructible from ViewableMap"); + +#endif // RD_CPP_CORE_VIEWABLE_MAP_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableSet.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableSet.h new file mode 100644 index 0000000..ca3ba7b --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableSet.h @@ -0,0 +1,272 @@ +#ifndef RD_CPP_CORE_VIEWABLESET_H +#define RD_CPP_CORE_VIEWABLESET_H + +#include "base/IViewableSet.h" +#include "reactive/base/SignalX.h" + +#include +#include + +namespace rd +{ +/** + * \brief complete class which has @code IViewableSet's properties + * \tparam T + */ +template > +class ViewableSet : public IViewableSet +{ +public: + using Event = typename IViewableSet::Event; + + using IViewableSet::advise; + +private: + using WT = typename IViewableSet::WT; + using WA = typename std::allocator_traits::template rebind_alloc>; + + Signal change; + using data_t = ordered_set, wrapper::TransparentHash, wrapper::TransparentKeyEqual, WA>; + mutable data_t set; + +public: + // region ctor/dtor + + ViewableSet() = default; + + ViewableSet(ViewableSet&&) = default; + + ViewableSet& operator=(ViewableSet&&) = default; + + virtual ~ViewableSet() = default; + // endregion + + // region iterators +public: + class iterator + { + friend class ViewableSet; + + typename data_t::iterator it_; + + explicit iterator(const typename data_t::iterator& it) : it_(it) + { + } + + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T const*; + using reference = T const&; + + iterator(const iterator& other) = default; + + iterator(iterator&& other) noexcept = default; + + iterator& operator=(const iterator& other) = default; + + iterator& operator=(iterator&& other) noexcept = default; + + iterator& operator++() + { + ++it_; + return *this; + } + + iterator operator++(int) + { + auto it = *this; + ++*this; + return it; + } + + iterator& operator--() + { + --it_; + return *this; + } + + iterator operator--(int) + { + auto it = *this; + --*this; + return it; + } + + iterator& operator+=(difference_type delta) + { + it_ += delta; + return *this; + } + + iterator& operator-=(difference_type delta) + { + it_ -= delta; + return *this; + } + + iterator operator+(difference_type delta) const + { + auto it = *this; + return it += delta; + } + + iterator operator-(difference_type delta) const + { + auto it = *this; + return it -= delta; + } + + difference_type operator-(iterator const& other) const + { + return it_ - other.it_; + } + + bool operator<(iterator const& other) const noexcept + { + return this->it_ < other.it_; + } + + bool operator>(iterator const& other) const noexcept + { + return this->it_ > other.it_; + } + + bool operator==(iterator const& other) const noexcept + { + return this->it_ == other.it_; + } + + bool operator!=(iterator const& other) const noexcept + { + return !(*this == other); + } + + bool operator<=(iterator const& other) const noexcept + { + return (this->it_ < other.it_) || (*this == other); + } + + bool operator>=(iterator const& other) const noexcept + { + return (this->it_ > other.it_) || (*this == other); + } + + reference operator*() const noexcept + { + return **it_; + } + + pointer operator->() const noexcept + { + return (*it_).get(); + } + }; + + using reverse_iterator = std::reverse_iterator; + + iterator begin() const + { + return iterator(set.begin()); + } + + iterator end() const + { + return iterator(set.end()); + } + + reverse_iterator rbegin() const + { + return reverse_iterator(end()); + } + + reverse_iterator rend() const + { + return reverse_iterator(begin()); + } + + // endregion + + bool add(WT element) const override + { + /*auto const &[it, success] = set.emplace(std::make_unique(std::move(element)));*/ + auto const& it = set.emplace(std::move(element)); + if (!it.second) + { + return false; + } + change.fire(Event(AddRemove::ADD, &(wrapper::get(*it.first)))); + return true; + } + + bool addAll(std::vector elements) const override + { + for (auto&& element : elements) + { + ViewableSet::add(std::move(element)); + } + return true; + } + + void clear() const override + { + std::vector changes; + for (auto const& element : set) + { + changes.push_back(Event(AddRemove::REMOVE, &(*element))); + } + for (auto const& e : changes) + { + change.fire(e); + } + set.clear(); + } + + bool remove(T const& element) const override + { + if (!ViewableSet::contains(element)) + { + return false; + } + auto it = set.find(element); + change.fire(Event(AddRemove::REMOVE, &(wrapper::get(*it)))); + set.erase(it); + return true; + } + + void advise(Lifetime lifetime, std::function handler) const override + { + for (auto const& x : set) + { + handler(Event(AddRemove::ADD, &(*x))); + } + change.advise(lifetime, handler); + } + + size_t size() const override + { + return set.size(); + } + + bool contains(T const& element) const override + { + return set.count(element) > 0; + } + + bool empty() const override + { + return set.empty(); + } + + template + bool emplace_add(Args&&... args) const + { + return add(WT{std::forward(args)...}); + } +}; +} // namespace rd + +static_assert(std::is_move_constructible>::value, "Is move constructible from ViewableSet"); + +#endif // RD_CPP_CORE_VIEWABLESET_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IProperty.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IProperty.h new file mode 100644 index 0000000..f8766fc --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IProperty.h @@ -0,0 +1,96 @@ +#ifndef RD_CPP_IPROPERTY_H +#define RD_CPP_IPROPERTY_H + +#include "SignalX.h" +#include "IPropertyBase.h" + +#include +#include + +#include + +namespace rd +{ +/** + * \brief A mutable property. + * \tparam T type of stored value (may be abstract) + */ +template +class IProperty : public IPropertyBase +{ +protected: + using WT = typename IPropertyBase::WT; + +public: + // region ctor/dtor + + IProperty() = default; + + IProperty(IProperty&& other) = default; + + IProperty& operator=(IProperty&& other) = default; + + explicit IProperty(T const& value) : IPropertyBase(value) + { + } + + template + explicit IProperty(F&& value) : IPropertyBase(std::forward(value)) + { + } + + virtual ~IProperty() = default; + // endregion + + virtual T const& get() const = 0; + +private: + void advise0(Lifetime lifetime, std::function handler, Signal const& signal) const + { + if (lifetime->is_terminated()) + { + return; + } + signal.advise(lifetime, handler); + if (this->has_value()) + { + handler(this->get()); + } + } + + void advise_before(Lifetime lifetime, std::function handler) const override + { + advise0(lifetime, handler, this->before_change); + } + +public: + void advise(Lifetime lifetime, std::function handler) const override + { + advise0(lifetime, std::move(handler), this->change); + } + + /** + * \brief set value of type T or derived type to it. + */ + virtual void set(value_or_wrapper) const = 0; + + /** + * \brief construct value of type T and delegate call to set + */ + template + void emplace(Args&&... args) const + { + set(value_or_wrapper{std::forward(args)...}); + } + + void set_if_empty(WT new_value) const + { + if (!this->has_value()) + { + set(std::move(new_value)); + } + } +}; +} // namespace rd + +#endif // RD_CPP_IPROPERTY_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IPropertyBase.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IPropertyBase.h new file mode 100644 index 0000000..209e235 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IPropertyBase.h @@ -0,0 +1,73 @@ +#ifndef RD_CPP_IPROPERTYBASE_H +#define RD_CPP_IPROPERTYBASE_H + +#include "interfaces.h" +#include "SignalX.h" + +#include +#include + +#include "thirdparty.hpp" + +namespace rd +{ +template +class IPropertyBase : public ISource, public IViewable +{ +protected: + mutable property_storage value; + + Signal change, before_change; + + using WT = value_or_wrapper; + +public: + bool has_value() const + { + return (bool) (value); + } + + // region ctor/dtor + + IPropertyBase() = default; + + IPropertyBase(IPropertyBase&& other) = default; + + IPropertyBase& operator=(IPropertyBase&& other) = default; + + template + explicit IPropertyBase(F&& value) : value(std::forward(value)) + { + } + + virtual ~IPropertyBase() = default; + // endregion + + virtual void advise_before(Lifetime lifetime, std::function handler) const = 0; + + void view(Lifetime lifetime, std::function handler) const override + { + if (lifetime->is_terminated()) + return; + + Lifetime lf = lifetime.create_nested(); + std::shared_ptr seq = std::make_shared(lf); + + this->advise_before(lf, [lf, seq](T const& /*v*/) { + if (!lf->is_terminated()) + { + seq->terminate_current(); + } + }); + + this->advise(lf, [lf, seq, handler](T const& v) { + if (!lf->is_terminated()) + { + handler(seq->next(), v); + } + }); + } +}; +} // namespace rd + +#endif // RD_CPP_IPROPERTYBASE_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableList.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableList.h new file mode 100644 index 0000000..cf40c2d --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableList.h @@ -0,0 +1,257 @@ +#ifndef RD_CPP_IVIEWABLELIST_H +#define RD_CPP_IVIEWABLELIST_H + +#include "interfaces.h" +#include "viewable_collections.h" + +#include +#include +#include + +#include + +#include +#include +#include + +#include "thirdparty.hpp" + +namespace rd +{ +namespace detail +{ +template +class ListEvent +{ +public: + class Add + { + public: + int32_t index; + T const* new_value; + + Add(int32_t index, T const* new_value) : index(index), new_value(new_value) + { + } + }; + + class Update + { + public: + int32_t index; + T const* old_value; + T const* new_value; + + Update(int32_t index, T const* old_value, T const* new_value) : index(index), old_value(old_value), new_value(new_value) + { + } + }; + + class Remove + { + public: + int32_t index; + T const* old_value; + + Remove(int32_t index, T const* old_value) : index(index), old_value(old_value) + { + } + }; + + variant v; + + ListEvent(Add x) : v(x) + { + } + + ListEvent(Update x) : v(x) + { + } + + ListEvent(Remove x) : v(x) + { + } + + int32_t get_index() const + { + return visit(util::make_visitor([](Add const& e) { return e.index; }, [](Update const& e) { return e.index; }, + [](Remove const& e) { return e.index; }), + v); + } + + T const* get_new_value() const + { + return visit(util::make_visitor([](Add const& e) { return e.new_value; }, [](Update const& e) { return e.new_value; }, + [](Remove const& /*e*/) { return static_cast(nullptr); }), + v); + } + + friend std::string to_string(ListEvent const& e) + { + std::string res = visit( + util::make_visitor( + [](typename ListEvent::Add const& e) { return "Add " + std::to_string(e.index) + ":" + to_string(*e.new_value); }, + [](typename ListEvent::Update const& e) { + return "Update " + std::to_string(e.index) + ":" + + // to_string(e.old_value) + ":" + + to_string(*e.new_value); + }, + [](typename ListEvent::Remove const& e) { return "Remove " + std::to_string(e.index); }), + e.v); + return res; + } +}; +} // namespace detail +/** + * \brief A list allowing its contents to be observed. + * \tparam T type of stored values (may be abstract) + */ +template +class IViewableList : public IViewable>, public ISource> +{ +protected: + using WT = value_or_wrapper; + +public: + /** + * \brief Represents an addition, update or removal of an element in the list. + */ + using Event = typename detail::ListEvent; + +protected: + mutable rd::unordered_map> lifetimes; + +public: + // region ctor/dtor + + IViewableList() = default; + + IViewableList(IViewableList&&) = default; + + IViewableList& operator=(IViewableList&&) = default; + + virtual ~IViewableList() = default; + // endregion + + /** + * \brief Adds a subscription to additions and removals of list elements. When a list element is updated, + * the [handler] is called twice: to report the removal of the old element and the addition of the new one. + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void advise_add_remove(Lifetime lifetime, std::function handler) const + { + advise(lifetime, [handler](Event e) { + visit(util::make_visitor([handler](typename Event::Add const& e) { handler(AddRemove::ADD, e.index, *e.new_value); }, + [handler](typename Event::Update const& e) { + handler(AddRemove::REMOVE, e.index, *e.old_value); + handler(AddRemove::ADD, e.index, *e.new_value); + }, + [handler](typename Event::Remove const& e) { handler(AddRemove::REMOVE, e.index, *e.old_value); }), + e.v); + }); + } + + /** + * \brief Adds a subscription to changes of the contents of the list. + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void view(Lifetime lifetime, std::function const&)> handler) const override + { + view(lifetime, [handler](Lifetime lt, size_t idx, T const& v) { handler(lt, std::make_pair(idx, &v)); }); + } + + /** + * \brief @see view above + */ + void view(Lifetime lifetime, std::function handler) const + { + advise_add_remove(lifetime, [this, lifetime, handler](AddRemove kind, size_t idx, T const& value) { + switch (kind) + { + case AddRemove::ADD: + { + LifetimeDefinition def(lifetime); + std::vector& v = lifetimes[lifetime]; + auto it = v.emplace(v.begin() + idx, std::move(def)); + handler(it->lifetime, idx, value); + break; + } + case AddRemove::REMOVE: + { + LifetimeDefinition def = std::move(lifetimes.at(lifetime)[idx]); + std::vector& v = lifetimes.at(lifetime); + v.erase(v.begin() + idx); + def.terminate(); + break; + } + } + }); + } + + void advise(Lifetime lifetime, std::function handler) const override = 0; + + virtual bool add(WT element) const = 0; + + virtual bool add(size_t index, WT element) const = 0; + + virtual WT removeAt(size_t index) const = 0; + + virtual bool remove(T const& element) const = 0; + + virtual T const& get(size_t index) const = 0; + + virtual WT set(size_t index, WT element) const = 0; + + virtual bool addAll(size_t index, std::vector elements) const = 0; + + virtual bool addAll(std::vector elements) const = 0; + + virtual void clear() const = 0; + + virtual bool removeAll(std::vector elements) const = 0; + + virtual size_t size() const = 0; + + virtual bool empty() const = 0; + + template + bool emplace_add(Args&&... args) const + { + return add(WT{std::forward(args)...}); + } + + template + bool emplace_add(size_t index, Args&&... args) const + { + return add(index, WT{std::forward(args)...}); + } + + template + WT emplace_set(size_t index, Args&&... args) const + { + return set(index, WT{std::forward(args)...}); + } + + template + friend typename std::enable_if<(!std::is_abstract::value), std::vector>::type convert_to_list( + IViewableList const& list); + +protected: + virtual const std::vector>& getList() const = 0; +}; + +template +typename std::enable_if<(!std::is_abstract::value), std::vector>::type convert_to_list(IViewableList const& list) +{ + std::vector res(list.size()); + std::transform(list.getList().begin(), list.getList().end(), res.begin(), [](Wrapper const& ptr) { return *ptr; }); + return res; +} +} // namespace rd + +static_assert( + std::is_move_constructible::Event>::value, "Is move constructible from IViewableList::Event"); + +#endif // RD_CPP_IVIEWABLELIST_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableMap.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableMap.h new file mode 100644 index 0000000..6b13ef2 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableMap.h @@ -0,0 +1,247 @@ +#ifndef RD_CPP_IVIEWABLEMAP_H +#define RD_CPP_IVIEWABLEMAP_H + +#include "lifetime/LifetimeDefinition.h" +#include "util/overloaded.h" +#include "interfaces.h" +#include "viewable_collections.h" +#include "util/core_util.h" + +#include "std/unordered_map.h" + +#include "thirdparty.hpp" + +namespace rd +{ +namespace detail +{ +template +class MapEvent +{ +public: + class Add + { + public: + K const* key; + V const* new_value; + + Add(K const* key, V const* new_value) : key(key), new_value(new_value) + { + } + }; + + class Update + { + public: + K const* key; + V const* old_value; + V const* new_value; + + Update(K const* key, V const* old_value, V const* new_value) : key(key), old_value(old_value), new_value(new_value) + { + } + }; + + class Remove + { + public: + K const* key; + V const* old_value; + + Remove(K const* key, V const* old_value) : key(key), old_value(old_value) + { + } + }; + + variant v; + + MapEvent(Add x) : v(x) + { + } + + MapEvent(Update x) : v(x) + { + } + + MapEvent(Remove x) : v(x) + { + } + + K const* get_key() const + { + return visit( + util::make_visitor([](typename MapEvent::Add const& e) { return e.key; }, + [](typename MapEvent::Update const& e) { return e.key; }, [](typename MapEvent::Remove const& e) { return e.key; }), + v); + } + + V const* get_old_value() const + { + return visit(util::make_visitor([](typename MapEvent::Add const& e) { return static_cast(nullptr); }, + [](typename MapEvent::Update const& e) { return e.old_value; }, + [](typename MapEvent::Remove const& e) { e.old_value; }), + v); + } + + V const* get_new_value() const + { + return visit(util::make_visitor([](typename MapEvent::Add const& e) { return e.new_value; }, + [](typename MapEvent::Update const& e) { return e.new_value; }, + [](typename MapEvent::Remove const& /*e*/) { return static_cast(nullptr); }), + v); + } + + friend std::string to_string(MapEvent const& e) + { + std::string res = + visit(util::make_visitor([](typename MapEvent::Add const& e) + -> std::string { return "Add " + to_string(*e.key) + ":" + to_string(*e.new_value); }, + [](typename MapEvent::Update const& e) -> std::string { + return "Update " + to_string(*e.key) + ":" + + // to_string(e.old_value) + ":" + + to_string(*e.new_value); + }, + [](typename MapEvent::Remove const& e) -> std::string { return "Remove " + to_string(*e.key); }), + e.v); + return res; + } +}; +} // namespace detail + +/** + * \brief A set allowing its contents to be observed. + * \tparam K type of stored keys (may be abstract) + * \tparam V type of stored values (may be abstract) + */ +template +class IViewableMap : public IViewable>, public ISource> +{ +protected: + using WK = value_or_wrapper; + using WV = value_or_wrapper; + using OV = opt_or_wrapper; + + mutable rd::unordered_map, wrapper::TransparentKeyEqual>> + lifetimes; + +public: + /** + * \brief Represents an addition, update or removal of an element in the map. + */ + using Event = typename detail::MapEvent; + + // region ctor/dtor + + IViewableMap() = default; + + IViewableMap(IViewableMap&&) = default; + + IViewableMap& operator=(IViewableMap&&) = default; + + virtual ~IViewableMap() = default; + // endregion + + void view(Lifetime lifetime, std::function const&) + + > + handler) const override + { + advise_add_remove(lifetime, [this, lifetime, handler](AddRemove kind, K const& key, V const& value) { + const std::pair entry = std::make_pair(&key, &value); + switch (kind) + { + case AddRemove::ADD: + { + if (lifetimes[lifetime].count(key) == 0) + { + /*auto const &[it, inserted] = lifetimes[lifetime].emplace(key, LifetimeDefinition(lifetime));*/ + auto const& pair = lifetimes[lifetime].emplace(&key, LifetimeDefinition(lifetime)); + auto& it = pair.first; + auto& inserted = pair.second; + RD_ASSERT_MSG(inserted, "lifetime definition already exists in viewable map by key:" + to_string(key)); + handler(it->second.lifetime, entry); + } + break; + } + case AddRemove::REMOVE: + { + RD_ASSERT_MSG(lifetimes.at(lifetime).count(key) > 0, + "attempting to remove non-existing lifetime in viewable map by key:" + to_string(key)); + LifetimeDefinition def = std::move(lifetimes.at(lifetime).at(key)); + lifetimes.at(lifetime).erase(key); + def.terminate(); + break; + } + } + }); + } + + /** + * \brief Adds a subscription to additions and removals of map elements. When a map element is updated, the [handler] + * is called twice: to report the removal of the old element and the addition of the new one. + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void advise_add_remove(Lifetime lifetime, std::function + handler) const + { + advise(lifetime, [handler](Event e) { + visit(util::make_visitor([handler](typename Event::Add const& e) { handler(AddRemove::ADD, *e.key, *e.new_value); }, + [handler](typename Event::Update const& e) { + handler(AddRemove::REMOVE, *e.key, *e.old_value); + handler(AddRemove::ADD, *e.key, *e.new_value); + }, + [handler](typename Event::Remove const& e) { handler(AddRemove::REMOVE, *e.key, *e.old_value); }), + e.v); + }); + } + + /** + * \brief Adds a subscription to changes of the contents of the map, with the handler receiving keys and values + * as separate parameters. + * + * \details When [handler] is initially added, it is called receiving all keys and values currently in the map. + * Every time a key/value pair is added to the map, the [handler] is called receiving the new key and value. + * The [Lifetime] instance passed to the handler expires when the key/value pair is removed from the map. + * + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void view(Lifetime lifetime, std::function + handler) const + { + view(lifetime, + [handler](Lifetime lf, const std::pair entry) { handler(lf, *entry.first, *entry.second); }); + } + + void advise(Lifetime lifetime, std::function handler) const override = 0; + + virtual const V* get(K const&) const = 0; + + virtual const V* set(WK, WV) const = 0; + + virtual OV remove(K const&) const = 0; + + virtual void clear() const = 0; + + virtual size_t size() const = 0; + + virtual bool empty() const = 0; + + template + const V* emplace_set(WK wk, Args&&... args) const + { + return set(std::move(wk), WV{std::forward(args)...}); + } +}; +} // namespace rd + +static_assert(std::is_move_constructible::Event>::value, + "Is move constructible from IViewableMap::Event"); + +#endif // RD_CPP_IVIEWABLEMAP_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableSet.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableSet.h new file mode 100644 index 0000000..d7ffb2d --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableSet.h @@ -0,0 +1,148 @@ +#ifndef RD_CPP_IVIEWABLESET_H +#define RD_CPP_IVIEWABLESET_H + +#include "interfaces.h" +#include "viewable_collections.h" + +#include +#include + +#include + +#include + +namespace rd +{ +namespace detail +{ +template +class SetEvent +{ +public: + SetEvent(AddRemove kind, T const* value) : kind(kind), value(value) + { + } + + AddRemove kind; + T const* value; + + friend std::string to_string(SetEvent const& e) + { + return to_string(e.kind) + ":" + to_string(*e.value); + } +}; +} // namespace detail + +/** + * \brief A set allowing its contents to be observed. + * \tparam T type of stored values (may be abstract) + */ +template > +class IViewableSet : public IViewable, public ISource> +{ +protected: + using WT = value_or_wrapper; + mutable rd::unordered_map, wrapper::TransparentKeyEqual>> + lifetimes; + +public: + // region ctor/dtor + + IViewableSet() = default; + + IViewableSet(IViewableSet&&) = default; + + IViewableSet& operator=(IViewableSet&&) = default; + + virtual ~IViewableSet() = default; + // endregion + + /** + * \brief Represents an addition or removal of an element in the set. + */ + using Event = typename detail::SetEvent; + + /** + * \brief Adds a subscription for additions and removals of set elements. When the subscription is initially + * added, [handler] is called with [AddRemove::Add] events for all elements currently in the set. + * + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void advise(Lifetime lifetime, std::function handler) const + { + this->advise(lifetime, [handler](Event e) { handler(e.kind, *e.value); }); + } + + /** + * \brief Adds a subscription to changes of the contents of the set. + * + * \details When [handler] is initially added, it is called receiving all elements currently in the set. + * Every time an object is added to the set, the [handler] is called receiving the new element. + * The [Lifetime] instance passed to the handler expires when the element is removed from the set. + * + * \param lifetime + * \param handler + */ + void view(Lifetime lifetime, std::function handler) const override + { + advise(lifetime, [this, lifetime, handler](AddRemove kind, T const& key) { + switch (kind) + { + case AddRemove::ADD: + { + /*auto const &[it, inserted] = lifetimes[lifetime].emplace(key, LifetimeDefinition(lifetime));*/ + auto const& it = lifetimes[lifetime].emplace(&key, lifetime); + RD_ASSERT_MSG(it.second, "lifetime definition already exists in viewable set by key:" + to_string(key)); + handler(it.first->second.lifetime, key); + break; + } + case AddRemove::REMOVE: + { + RD_ASSERT_MSG(lifetimes.at(lifetime).count(key) > 0, + "attempting to remove non-existing lifetime in viewable set by key:" + to_string(key)); + LifetimeDefinition def = std::move(lifetimes.at(lifetime).at(key)); + lifetimes.at(lifetime).erase(key); + def.terminate(); + break; + } + } + }); + } + + /** + * \brief Adds a subscription for additions and removals of set elements. When the subscription is initially + * added, [handler] is called with [AddRemove.Add] events for all elements currently in the set. + * + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void advise(Lifetime lifetime, std::function handler) const override = 0; + + virtual bool add(WT) const = 0; + + virtual bool addAll(std::vector elements) const = 0; + + virtual void clear() const = 0; + + virtual bool remove(T const&) const = 0; + + virtual size_t size() const = 0; + + virtual bool contains(T const&) const = 0; + + virtual bool empty() const = 0; + + template + bool emplace_add(Args&&... args) const + { + return add(WT{std::forward(args)...}); + } +}; +} // namespace rd + +static_assert( + std::is_move_constructible::Event>::value, "Is move constructible from IViewableSet::Event"); + +#endif // RD_CPP_IVIEWABLESET_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.cpp b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.cpp new file mode 100644 index 0000000..5aa1d78 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.cpp @@ -0,0 +1,23 @@ +#include "SignalCookie.h" + +#include + +namespace +{ +std::atomic cookie; +} + +void rd_signal_cookie_inc() +{ + ++cookie; +} + +void rd_signal_cookie_dec() +{ + --cookie; +} + +int32_t rd_signal_cookie_get() +{ + return cookie; +} diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.h new file mode 100644 index 0000000..b2659b7 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.h @@ -0,0 +1,11 @@ +#ifndef RD_CPP_SIGNALCOOKIE_H +#define RD_CPP_SIGNALCOOKIE_H + +#include +#include + +extern "C" void RD_CORE_API rd_signal_cookie_inc(); +extern "C" void RD_CORE_API rd_signal_cookie_dec(); +extern "C" int32_t RD_CORE_API rd_signal_cookie_get(); + +#endif // RD_CPP_SIGNALCOOKIE_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalX.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalX.h new file mode 100644 index 0000000..598cc60 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalX.h @@ -0,0 +1,138 @@ +#ifndef RD_CPP_CORE_SIGNAL_H +#define RD_CPP_CORE_SIGNAL_H + +#include "interfaces.h" +#include "SignalCookie.h" + +#include +#include + +#include +#include +#include + +namespace rd +{ +/** + * \brief complete class which has \a Signal 's properties + */ +template +class Signal final : public ISignal +{ +private: + using WT = typename ISignal::WT; + + class Event + { + private: + std::function action; + Lifetime lifetime; + + public: + // region ctor/dtor + Event() = delete; + + template + Event(F&& action, Lifetime lifetime) : action(std::forward(action)), lifetime(lifetime) + { + } + + Event(Event&&) = default; + // endregion + + bool is_alive() const + { + return !lifetime->is_terminated(); + } + + void execute_if_alive(T const& value) const + { + if (is_alive()) + { + action(value); + } + } + }; + + using counter_t = int32_t; + using listeners_t = std::map; + + mutable counter_t advise_id = 0; + mutable listeners_t listeners, priority_listeners; + + static void cleanup(listeners_t& queue) + { + util::erase_if(queue, [](Event const& e) -> bool { return !e.is_alive(); }); + } + + void fire_impl(T const& value, listeners_t& queue) const + { + for (auto const& p : queue) + { + auto const& event = p.second; + event.execute_if_alive(value); + } + cleanup(queue); + } + + template + void advise0(const Lifetime& lifetime, F&& handler, listeners_t& queue) const + { + if (lifetime->is_terminated()) + return; + counter_t id = advise_id /*.load()*/; + queue.emplace(id, Event(std::forward(handler), lifetime)); + ++advise_id; + } + +public: + // region ctor/dtor + + Signal() = default; + + Signal(Signal const& other) = delete; + + Signal& operator=(Signal const& other) = delete; + + Signal(Signal&&) = default; + + Signal& operator=(Signal&&) = default; + + virtual ~Signal() = default; + + // endregion + + using ISignal::fire; + + void fire(T const& value) const override + { + fire_impl(value, priority_listeners); + fire_impl(value, listeners); + } + + using ISignal::advise; + + void advise(Lifetime lifetime, std::function handler) const override + { + advise0(lifetime, std::move(handler), isPriorityAdvise() ? priority_listeners : listeners); + } + + static bool isPriorityAdvise() + { + return rd_signal_cookie_get() > 0; + } +}; + +template +void priorityAdviseSection(F&& block) +{ + rd_signal_cookie_inc(); + block(); + rd_signal_cookie_dec(); +} +} // namespace rd + +static_assert(std::is_move_constructible>::value, "Is not move constructible from Signal"); +static_assert(std::is_move_constructible>::value, "Is not move constructible from Signal"); + +#endif // RD_CPP_CORE_SIGNAL_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/interfaces.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/interfaces.h new file mode 100644 index 0000000..6588b34 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/interfaces.h @@ -0,0 +1,90 @@ +#ifndef RD_CPP_CORE_INTERFACES_H +#define RD_CPP_CORE_INTERFACES_H + +#include +#include +#include + +#include +#include + +namespace rd +{ +/** + * \brief An object that allows to subscribe to events. + * \tparam T type of events + */ +template +class ISource +{ +public: + virtual ~ISource() = default; + + /** + * \brief Adds an event subscription. + * \param lifetime lifetime of subscription. + * \param handler to be called, every time an event occurs. + */ + virtual void advise(Lifetime lifetime, std::function handler) const = 0; + + /** + * \brief @code advise with Eternal lifetime + */ + template + void advise_eternal(F&& handler) const + { + advise(Lifetime::Eternal(), std::forward(handler)); + } + + /** + * \brief @code Void specialisation of @code advise method, at @tparam T=Void + */ + void advise(Lifetime lifetime, std::function handler) const + { + advise(lifetime, [handler = std::move(handler)](Void) { handler(); }); + } +}; + +/** + * \brief An object that allows to subscribe to changes of its contents. + * \tparam T type of content + */ +template +class IViewable +{ +public: + virtual ~IViewable() = default; + + virtual void view(Lifetime lifetime, std::function + handler) const = 0; +}; + +/** + * \brief An object which has a collection of event listeners and can broadcast an event to the listeners. + * \tparam T type of events + */ +template +class ISignal : public ISource +{ +protected: + using WT = value_or_wrapper; + +public: + virtual ~ISignal() = default; + + virtual void fire(T const& value) const = 0; + + /** + * \brief @code fire specialisation at T=Void + */ + template + typename std::enable_if_t> fire() const + { + fire(Void{}); + } +}; +} // namespace rd + +#endif // RD_CPP_CORE_INTERFACES_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/viewable_collections.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/viewable_collections.h new file mode 100644 index 0000000..bd90008 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/viewable_collections.h @@ -0,0 +1,53 @@ +#ifndef RD_CPP_VIEWABLE_COLLECTIONS_H +#define RD_CPP_VIEWABLE_COLLECTIONS_H + +#include + +namespace rd +{ +enum class AddRemove +{ + ADD, + REMOVE +}; + +inline std::string to_string(AddRemove kind) +{ + switch (kind) + { + case AddRemove::ADD: + return "Add"; + case AddRemove::REMOVE: + return "Remove"; + default: + return ""; + } +} + +enum class Op +{ + ADD, + UPDATE, + REMOVE, + ACK +}; + +inline std::string to_string(Op op) +{ + switch (op) + { + case Op::ADD: + return "Add"; + case Op::UPDATE: + return "Update"; + case Op::REMOVE: + return "Remove"; + case Op::ACK: + return "Ack"; + default: + return ""; + } +} +} // namespace rd + +#endif // RD_CPP_VIEWABLE_COLLECTIONS_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/allocator.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/allocator.h new file mode 100644 index 0000000..b8a7227 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/allocator.h @@ -0,0 +1,12 @@ +#ifndef RD_CPP_ALLOCATOR_H +#define RD_CPP_ALLOCATOR_H + +#include + +namespace rd +{ +template +using allocator = std::allocator; +} + +#endif // RD_CPP_ALLOCATOR_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/hash.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/hash.h new file mode 100644 index 0000000..fce90df --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/hash.h @@ -0,0 +1,19 @@ +#ifndef RD_CPP_HASH_H +#define RD_CPP_HASH_H + +#include +#include + +namespace rd +{ +template +struct hash +{ + size_t operator()(const T& value) const noexcept + { + return std::hash()(value); + } +}; +} // namespace rd + +#endif // RD_CPP_HASH_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/list.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/list.h new file mode 100644 index 0000000..dd6ecc8 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/list.h @@ -0,0 +1,35 @@ +#ifndef RD_CPP_LIST_H +#define RD_CPP_LIST_H + +#include +#include + +namespace rd +{ +template +int32_t size(T const& value) = delete; + +// c++17 has std::size for std::vector +#if __cplusplus < 201703L + +template +int32_t size(std::vector const& value) +{ + return static_cast(value.size()); +} +#else +template +int32_t size(std::vector const& value) +{ + return std::size(value); +} +#endif + +template +void resize(std::vector& value, int32_t size) +{ + value.resize(size); +} +} // namespace rd + +#endif // RD_CPP_LIST_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/to_string.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/to_string.h new file mode 100644 index 0000000..91fbab3 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/to_string.h @@ -0,0 +1,147 @@ +// ReSharper disable CppUE4CodingStandardNamingViolationWarning +#ifndef RD_CPP_TO_STRING_H +#define RD_CPP_TO_STRING_H + +#include +#include +#include +#include +#include +#include + +#include "ww898/utf_converters.hpp" + +#include + +namespace rd +{ +namespace detail +{ +using std::to_string; + +inline std::string to_string(std::string const& val) +{ + return val; +} + +inline std::string to_string(const char* val) +{ + return val; +} + +inline std::string to_string(std::wstring const& val) +{ + return ww898::utf::conv(val); +} + +inline std::string to_string(std::thread::id const& id) +{ + std::ostringstream ss; + ss << id; + return ss.str(); +} + +inline std::string to_string(std::exception const& e) +{ + return std::string(e.what()); +} + +inline std::string to_string(std::future_status const& status) +{ + switch (status) + { + case std::future_status::ready: + return "ready"; + case std::future_status::timeout: + return "timeout"; + case std::future_status::deferred: + return "deferred"; + default: + return "unknown"; + } +} + +template +inline std::string to_string(std::chrono::duration const& time) +{ + return std::to_string(std::chrono::duration_cast(time).count()) + "ms"; +} + +template +inline std::string to_string(T const* val) +{ + return val ? to_string(*val) : "nullptr"; +} + +template +inline std::string to_string(std::atomic const& value) +{ + return to_string(value.load()); +} + +template +inline std::string to_string(optional const& val) +{ + if (val.has_value()) + { + return to_string(*val); + } + else + { + return "nullopt"; + } +} + +template +inline std::string to_string(const std::pair p) +{ + return "(" + to_string(p.first) + ", " + to_string(p.second) + ")"; +} + +template