removed unused methods(BeginPlay, Tick) and added rider plugin
This commit is contained in:
Binary file not shown.
BIN
Plugins/Developer/RiderLink/Resources/Icon128.png
Normal file
BIN
Plugins/Developer/RiderLink/Resources/Icon128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
1
Plugins/Developer/RiderLink/Resources/checksum
Normal file
1
Plugins/Developer/RiderLink/Resources/checksum
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/」」<EFBDA3><EFBDA3>n怒退<E68092>吭n
|
||||||
61
Plugins/Developer/RiderLink/RiderLink.uplugin
Normal file
61
Plugins/Developer/RiderLink/RiderLink.uplugin
Normal file
@@ -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
|
||||||
|
}
|
||||||
93
Plugins/Developer/RiderLink/Source/RD/RD.Build.cs
Normal file
93
Plugins/Developer/RiderLink/Source/RD/RD.Build.cs
Normal file
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Plugins/Developer/RiderLink/Source/RD/RD.cpp
Normal file
11
Plugins/Developer/RiderLink/Source/RD/RD.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include "RD.h"
|
||||||
|
|
||||||
|
#include <Modules/ModuleManager.h>
|
||||||
|
|
||||||
|
#define LOCTEXT_NAMESPACE "RD"
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY(FLogRDModule);
|
||||||
|
|
||||||
|
IMPLEMENT_MODULE(FRDModule, RD);
|
||||||
|
|
||||||
|
#undef LOCTEXT_NAMESPACE
|
||||||
16
Plugins/Developer/RiderLink/Source/RD/RD.h
Normal file
16
Plugins/Developer/RiderLink/Source/RD/RD.h
Normal file
@@ -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;
|
||||||
|
};
|
||||||
@@ -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 */
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#include "Lifetime.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <thirdparty.hpp>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||||
|
|
||||||
|
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<LifetimeImpl, Allocator>(allocator, is_eternal))
|
||||||
|
{
|
||||||
|
std::call_once(onceFlag, [] {
|
||||||
|
spdlog::set_default_logger(spdlog::stderr_color_mt<spdlog::synchronous_factory>("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
|
||||||
@@ -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 <std/hash.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <rd_core_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class Lifetime;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct RD_CORE_API hash<Lifetime>
|
||||||
|
{
|
||||||
|
size_t operator()(const Lifetime& value) const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RD_CORE_API Lifetime final
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using Allocator = std::allocator<LifetimeImpl>;
|
||||||
|
|
||||||
|
static /*thread_local */ Allocator allocator;
|
||||||
|
|
||||||
|
friend class LifetimeDefinition;
|
||||||
|
|
||||||
|
friend struct hash<Lifetime>;
|
||||||
|
|
||||||
|
std::shared_ptr<LifetimeImpl> 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<Lifetime>::operator()(const Lifetime& value) const noexcept
|
||||||
|
{
|
||||||
|
return hash<std::shared_ptr<LifetimeImpl> >()(value.ptr);
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_LIFETIMEWRAPPER_H
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#include "LifetimeDefinition.h"
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
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> LifetimeDefinition::get_shared_eternal()
|
||||||
|
{
|
||||||
|
return std::shared_ptr<LifetimeDefinition>(&ETERNAL, [](LifetimeDefinition* /*ld*/) {});
|
||||||
|
}
|
||||||
|
|
||||||
|
LifetimeDefinition::~LifetimeDefinition()
|
||||||
|
{
|
||||||
|
if (lifetime.ptr != nullptr)
|
||||||
|
{ // wasn't moved
|
||||||
|
if (!is_eternal())
|
||||||
|
{
|
||||||
|
if (!lifetime->is_terminated())
|
||||||
|
{
|
||||||
|
lifetime->terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -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 <functional>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <rd_core_export.h>
|
||||||
|
|
||||||
|
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<LifetimeDefinition> eternal;
|
||||||
|
static std::shared_ptr<LifetimeDefinition> get_shared_eternal();
|
||||||
|
|
||||||
|
bool is_terminated() const;
|
||||||
|
|
||||||
|
bool is_eternal() const;
|
||||||
|
|
||||||
|
void terminate();
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
static auto use(F&& block) -> typename util::result_of_t<F(Lifetime)>
|
||||||
|
{
|
||||||
|
LifetimeDefinition definition(false);
|
||||||
|
Lifetime lw = definition.lifetime.create_nested();
|
||||||
|
return block(lw);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_LIFETIME_DEFINITION_H
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#include "LifetimeImpl.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
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<decltype(actions_lock)> 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<LifetimeImpl> nested)
|
||||||
|
{
|
||||||
|
if (nested->is_terminated() || is_eternal())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::function<void()> 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
|
||||||
@@ -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 <std/hash.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <atomic>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <thirdparty.hpp>
|
||||||
|
|
||||||
|
#include <rd_core_export.h>
|
||||||
|
|
||||||
|
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<bool> terminated{false};
|
||||||
|
|
||||||
|
counter_t id = 0;
|
||||||
|
|
||||||
|
counter_t action_id_in_map = 0;
|
||||||
|
using actions_t = ordered_map<int, std::function<void()>, rd::hash<int>>;
|
||||||
|
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 <typename F>
|
||||||
|
counter_t add_action(F&& action)
|
||||||
|
{
|
||||||
|
std::lock_guard<decltype(actions_lock)> guard(actions_lock);
|
||||||
|
|
||||||
|
if (is_eternal())
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (is_terminated())
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("Already Terminated");
|
||||||
|
}
|
||||||
|
|
||||||
|
actions[action_id_in_map] = std::forward<F>(action);
|
||||||
|
return action_id_in_map++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_action(counter_t i)
|
||||||
|
{
|
||||||
|
std::lock_guard<decltype(actions_lock)> guard(actions_lock);
|
||||||
|
|
||||||
|
actions.erase(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201703L
|
||||||
|
static inline counter_t get_id = 0;
|
||||||
|
#else
|
||||||
|
static counter_t get_id;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename F, typename G>
|
||||||
|
void bracket(F&& opening, G&& closing)
|
||||||
|
{
|
||||||
|
if (is_terminated())
|
||||||
|
return;
|
||||||
|
opening();
|
||||||
|
add_action(std::forward<G>(closing));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_terminated() const;
|
||||||
|
|
||||||
|
bool is_eternal() const;
|
||||||
|
|
||||||
|
void attach_nested(std::shared_ptr<LifetimeImpl> nested);
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_LIFETIME_H
|
||||||
@@ -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<LifetimeDefinition> new_def = std::make_shared<LifetimeDefinition>(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<LifetimeDefinition> new_def)
|
||||||
|
{
|
||||||
|
std::shared_ptr<LifetimeDefinition> prev = current_def;
|
||||||
|
current_def = new_def;
|
||||||
|
prev->terminate();
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -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 <rd_core_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RD_CORE_API SequentialLifetimes
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::shared_ptr<LifetimeDefinition> 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<LifetimeDefinition> new_def);
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_SEQUENTIAL_LIFETIMES_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 <util/core_util.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief complete class which has \a Property<T> 's properties.
|
||||||
|
* \tparam T type of stored value (may be abstract)
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class Property : public IProperty<T>
|
||||||
|
{
|
||||||
|
using WT = typename IProperty<T>::WT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
Property() = default;
|
||||||
|
|
||||||
|
Property(Property&& other) = default;
|
||||||
|
|
||||||
|
Property& operator=(Property&& other) = default;
|
||||||
|
|
||||||
|
virtual ~Property() = default;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
explicit Property(F&& value) : IProperty<T>(std::forward<F>(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<T>(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<rd::Property<int>>::value, "Is not move constructible from Property<int>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_PROPERTY_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 <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief complete class which has @code IViewableList<T>'s properties
|
||||||
|
*/
|
||||||
|
template <typename T, typename A = allocator<T>>
|
||||||
|
class ViewableList : public IViewableList<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Event = typename IViewableList<T>::Event;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using WA = typename std::allocator_traits<A>::template rebind_alloc<Wrapper<T>>;
|
||||||
|
|
||||||
|
using data_t = std::vector<Wrapper<T>, WA>;
|
||||||
|
mutable data_t list;
|
||||||
|
Signal<Event> change;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using WT = typename IViewableList<T>::WT;
|
||||||
|
|
||||||
|
const std::vector<Wrapper<T>>& 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<T>;
|
||||||
|
|
||||||
|
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>;
|
||||||
|
|
||||||
|
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<void(Event const&)> handler) const override
|
||||||
|
{
|
||||||
|
if (lifetime->is_terminated())
|
||||||
|
return;
|
||||||
|
change.advise(lifetime, handler);
|
||||||
|
for (int32_t i = 0; i < static_cast<int32_t>(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<int32_t>(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<int32_t>(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<int32_t>(index), &(*res)));
|
||||||
|
return wrapper::unwrap<T>(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<T>(std::move(element));
|
||||||
|
change.fire(typename Event::Update(static_cast<int32_t>(index), &(*old_value), &(*list[index]))); //???
|
||||||
|
return wrapper::unwrap<T>(std::move(old_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addAll(size_t index, std::vector<WT> elements) const override
|
||||||
|
{
|
||||||
|
for (auto& element : elements)
|
||||||
|
{
|
||||||
|
ViewableList::add(index, std::move(element));
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addAll(std::vector<WT> elements) const override
|
||||||
|
{
|
||||||
|
for (auto&& element : elements)
|
||||||
|
{
|
||||||
|
ViewableList::add(std::move(element));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() const override
|
||||||
|
{
|
||||||
|
std::vector<Event> changes;
|
||||||
|
for (size_t i = size(); i > 0; --i)
|
||||||
|
{
|
||||||
|
changes.push_back(typename Event::Remove(static_cast<int32_t>(i - 1), &(*list[i - 1])));
|
||||||
|
}
|
||||||
|
for (auto const& e : changes)
|
||||||
|
{
|
||||||
|
change.fire(e);
|
||||||
|
}
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeAll(std::vector<WT> elements) const override
|
||||||
|
{
|
||||||
|
// TO-DO faster
|
||||||
|
// std::unordered_set<T> 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<T>()(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<rd::ViewableList<int>>::value, "Is move constructible from ViewableList<int>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_VIEWABLELIST_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 <util/core_util.h>
|
||||||
|
#include <std/unordered_map.h>
|
||||||
|
|
||||||
|
#include <thirdparty.hpp>
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief complete class which has @code IViewableMap<K, V>'s properties
|
||||||
|
*/
|
||||||
|
template <typename K, typename V, typename KA = std::allocator<K>, typename VA = std::allocator<V>>
|
||||||
|
class ViewableMap : public IViewableMap<K, V>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Event = typename IViewableMap<K, V>::Event;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using WK = typename IViewableMap<K, V>::WK;
|
||||||
|
using WV = typename IViewableMap<K, V>::WV;
|
||||||
|
using OV = typename IViewableMap<K, V>::OV;
|
||||||
|
using PA = typename std::allocator_traits<VA>::template rebind_alloc<std::pair<Wrapper<K>, Wrapper<V>>>;
|
||||||
|
|
||||||
|
Signal<Event> change;
|
||||||
|
|
||||||
|
using data_t = ordered_map<Wrapper<K>, Wrapper<V>, wrapper::TransparentHash<K>, wrapper::TransparentKeyEqual<K>, 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<K, V>;
|
||||||
|
|
||||||
|
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<iterator>
|
||||||
|
{
|
||||||
|
using base_t = std::reverse_iterator<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<base_t>(*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<void(Event const&)> 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<K>(std::move(key)), std::make_unique<V>(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<V>(value))
|
||||||
|
{ // TO-DO more effective
|
||||||
|
Wrapper<V> old_value = std::move(map.at(key));
|
||||||
|
|
||||||
|
map.at(key_ptr) = Wrapper<V>(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<V> old_value = std::move(map.at(key));
|
||||||
|
change.fire(typename Event::Remove(&key, &(*old_value)));
|
||||||
|
map.erase(key);
|
||||||
|
return wrapper::unwrap<V>(std::move(old_value));
|
||||||
|
}
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() const override
|
||||||
|
{
|
||||||
|
std::vector<Event> 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<rd::ViewableMap<int, int>>::value, "Is move constructible from ViewableMap<int, int>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_VIEWABLE_MAP_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 <std/allocator.h>
|
||||||
|
#include <util/core_util.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief complete class which has @code IViewableSet<T>'s properties
|
||||||
|
* \tparam T
|
||||||
|
*/
|
||||||
|
template <typename T, typename A = allocator<T>>
|
||||||
|
class ViewableSet : public IViewableSet<T, A>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Event = typename IViewableSet<T>::Event;
|
||||||
|
|
||||||
|
using IViewableSet<T, A>::advise;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using WT = typename IViewableSet<T, A>::WT;
|
||||||
|
using WA = typename std::allocator_traits<A>::template rebind_alloc<Wrapper<T>>;
|
||||||
|
|
||||||
|
Signal<Event> change;
|
||||||
|
using data_t = ordered_set<Wrapper<T>, wrapper::TransparentHash<T>, wrapper::TransparentKeyEqual<T>, 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<T>;
|
||||||
|
|
||||||
|
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>;
|
||||||
|
|
||||||
|
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<T>(std::move(element)));*/
|
||||||
|
auto const& it = set.emplace(std::move(element));
|
||||||
|
if (!it.second)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
change.fire(Event(AddRemove::ADD, &(wrapper::get<T>(*it.first))));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addAll(std::vector<WT> elements) const override
|
||||||
|
{
|
||||||
|
for (auto&& element : elements)
|
||||||
|
{
|
||||||
|
ViewableSet::add(std::move(element));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() const override
|
||||||
|
{
|
||||||
|
std::vector<Event> 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<T>(*it))));
|
||||||
|
set.erase(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, std::function<void(Event const&)> 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 <typename... Args>
|
||||||
|
bool emplace_add(Args&&... args) const
|
||||||
|
{
|
||||||
|
return add(WT{std::forward<Args>(args)...});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
static_assert(std::is_move_constructible<rd::ViewableSet<int>>::value, "Is move constructible from ViewableSet<int>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_VIEWABLESET_H
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#ifndef RD_CPP_IPROPERTY_H
|
||||||
|
#define RD_CPP_IPROPERTY_H
|
||||||
|
|
||||||
|
#include "SignalX.h"
|
||||||
|
#include "IPropertyBase.h"
|
||||||
|
|
||||||
|
#include <lifetime/Lifetime.h>
|
||||||
|
#include <types/wrapper.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief A mutable property.
|
||||||
|
* \tparam T type of stored value (may be abstract)
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class IProperty : public IPropertyBase<T>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
using WT = typename IPropertyBase<T>::WT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
IProperty() = default;
|
||||||
|
|
||||||
|
IProperty(IProperty&& other) = default;
|
||||||
|
|
||||||
|
IProperty& operator=(IProperty&& other) = default;
|
||||||
|
|
||||||
|
explicit IProperty(T const& value) : IPropertyBase<T>(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
explicit IProperty(F&& value) : IPropertyBase<T>(std::forward<F>(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~IProperty() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
virtual T const& get() const = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void advise0(Lifetime lifetime, std::function<void(T const&)> handler, Signal<T> 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<void(T const&)> handler) const override
|
||||||
|
{
|
||||||
|
advise0(lifetime, handler, this->before_change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void advise(Lifetime lifetime, std::function<void(T const&)> 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<T>) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief construct value of type T and delegate call to set
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
void emplace(Args&&... args) const
|
||||||
|
{
|
||||||
|
set(value_or_wrapper<T>{std::forward<Args>(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
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#ifndef RD_CPP_IPROPERTYBASE_H
|
||||||
|
#define RD_CPP_IPROPERTYBASE_H
|
||||||
|
|
||||||
|
#include "interfaces.h"
|
||||||
|
#include "SignalX.h"
|
||||||
|
|
||||||
|
#include <types/wrapper.h>
|
||||||
|
#include <lifetime/SequentialLifetimes.h>
|
||||||
|
|
||||||
|
#include "thirdparty.hpp"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class IPropertyBase : public ISource<T>, public IViewable<T>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
mutable property_storage<T> value;
|
||||||
|
|
||||||
|
Signal<T> change, before_change;
|
||||||
|
|
||||||
|
using WT = value_or_wrapper<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool has_value() const
|
||||||
|
{
|
||||||
|
return (bool) (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
IPropertyBase() = default;
|
||||||
|
|
||||||
|
IPropertyBase(IPropertyBase&& other) = default;
|
||||||
|
|
||||||
|
IPropertyBase& operator=(IPropertyBase&& other) = default;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
explicit IPropertyBase(F&& value) : value(std::forward<F>(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~IPropertyBase() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
virtual void advise_before(Lifetime lifetime, std::function<void(T const&)> handler) const = 0;
|
||||||
|
|
||||||
|
void view(Lifetime lifetime, std::function<void(Lifetime, T const&)> handler) const override
|
||||||
|
{
|
||||||
|
if (lifetime->is_terminated())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Lifetime lf = lifetime.create_nested();
|
||||||
|
std::shared_ptr<SequentialLifetimes> seq = std::make_shared<SequentialLifetimes>(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
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
#ifndef RD_CPP_IVIEWABLELIST_H
|
||||||
|
#define RD_CPP_IVIEWABLELIST_H
|
||||||
|
|
||||||
|
#include "interfaces.h"
|
||||||
|
#include "viewable_collections.h"
|
||||||
|
|
||||||
|
#include <lifetime/LifetimeDefinition.h>
|
||||||
|
#include <util/overloaded.h>
|
||||||
|
#include <types/wrapper.h>
|
||||||
|
|
||||||
|
#include <std/unordered_map.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "thirdparty.hpp"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
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<Add, Update, Remove> 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<T const*>(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 <typename T>
|
||||||
|
class IViewableList : public IViewable<std::pair<size_t, T const*>>, public ISource<detail::ListEvent<T>>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
using WT = value_or_wrapper<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief Represents an addition, update or removal of an element in the list.
|
||||||
|
*/
|
||||||
|
using Event = typename detail::ListEvent<T>;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mutable rd::unordered_map<Lifetime, std::vector<LifetimeDefinition>> 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<void(AddRemove, size_t, T const&)> 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<void(Lifetime lifetime, std::pair<size_t, T const*> 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<void(Lifetime, size_t, T const&)> 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<LifetimeDefinition>& 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<LifetimeDefinition>& v = lifetimes.at(lifetime);
|
||||||
|
v.erase(v.begin() + idx);
|
||||||
|
def.terminate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, std::function<void(Event const&)> 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<WT> elements) const = 0;
|
||||||
|
|
||||||
|
virtual bool addAll(std::vector<WT> elements) const = 0;
|
||||||
|
|
||||||
|
virtual void clear() const = 0;
|
||||||
|
|
||||||
|
virtual bool removeAll(std::vector<WT> elements) const = 0;
|
||||||
|
|
||||||
|
virtual size_t size() const = 0;
|
||||||
|
|
||||||
|
virtual bool empty() const = 0;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
bool emplace_add(Args&&... args) const
|
||||||
|
{
|
||||||
|
return add(WT{std::forward<Args>(args)...});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
bool emplace_add(size_t index, Args&&... args) const
|
||||||
|
{
|
||||||
|
return add(index, WT{std::forward<Args>(args)...});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
WT emplace_set(size_t index, Args&&... args) const
|
||||||
|
{
|
||||||
|
return set(index, WT{std::forward<Args>(args)...});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
friend typename std::enable_if<(!std::is_abstract<U>::value), std::vector<U>>::type convert_to_list(
|
||||||
|
IViewableList<U> const& list);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual const std::vector<Wrapper<T>>& getList() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<(!std::is_abstract<T>::value), std::vector<T>>::type convert_to_list(IViewableList<T> const& list)
|
||||||
|
{
|
||||||
|
std::vector<T> res(list.size());
|
||||||
|
std::transform(list.getList().begin(), list.getList().end(), res.begin(), [](Wrapper<T> const& ptr) { return *ptr; });
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
std::is_move_constructible<rd::IViewableList<int>::Event>::value, "Is move constructible from IViewableList<int>::Event");
|
||||||
|
|
||||||
|
#endif // RD_CPP_IVIEWABLELIST_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 <typename K, typename V>
|
||||||
|
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<Add, Update, Remove> 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<V const*>(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<V const*>(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 <typename K, typename V>
|
||||||
|
class IViewableMap : public IViewable<std::pair<K const*, V const*>>, public ISource<detail::MapEvent<K, V>>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
using WK = value_or_wrapper<K>;
|
||||||
|
using WV = value_or_wrapper<V>;
|
||||||
|
using OV = opt_or_wrapper<V>;
|
||||||
|
|
||||||
|
mutable rd::unordered_map<Lifetime,
|
||||||
|
ordered_map<K const*, LifetimeDefinition, wrapper::TransparentHash<K>, wrapper::TransparentKeyEqual<K>>>
|
||||||
|
lifetimes;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief Represents an addition, update or removal of an element in the map.
|
||||||
|
*/
|
||||||
|
using Event = typename detail::MapEvent<K, V>;
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
IViewableMap() = default;
|
||||||
|
|
||||||
|
IViewableMap(IViewableMap&&) = default;
|
||||||
|
|
||||||
|
IViewableMap& operator=(IViewableMap&&) = default;
|
||||||
|
|
||||||
|
virtual ~IViewableMap() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
void view(Lifetime lifetime, std::function<void(Lifetime lifetime, std::pair<K const*, V const*> const&)
|
||||||
|
|
||||||
|
>
|
||||||
|
handler) const override
|
||||||
|
{
|
||||||
|
advise_add_remove(lifetime, [this, lifetime, handler](AddRemove kind, K const& key, V const& value) {
|
||||||
|
const std::pair<K const*, V const*> 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<void(AddRemove, K const&, V const&)
|
||||||
|
|
||||||
|
>
|
||||||
|
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<void(Lifetime, K const&, V const&)
|
||||||
|
|
||||||
|
>
|
||||||
|
handler) const
|
||||||
|
{
|
||||||
|
view(lifetime,
|
||||||
|
[handler](Lifetime lf, const std::pair<K const*, V const*> entry) { handler(lf, *entry.first, *entry.second); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, std::function<void(Event const&)> 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 <typename... Args>
|
||||||
|
const V* emplace_set(WK wk, Args&&... args) const
|
||||||
|
{
|
||||||
|
return set(std::move(wk), WV{std::forward<Args>(args)...});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
static_assert(std::is_move_constructible<rd::IViewableMap<int, int>::Event>::value,
|
||||||
|
"Is move constructible from IViewableMap<int, int>::Event");
|
||||||
|
|
||||||
|
#endif // RD_CPP_IVIEWABLEMAP_H
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
#ifndef RD_CPP_IVIEWABLESET_H
|
||||||
|
#define RD_CPP_IVIEWABLESET_H
|
||||||
|
|
||||||
|
#include "interfaces.h"
|
||||||
|
#include "viewable_collections.h"
|
||||||
|
|
||||||
|
#include <lifetime/LifetimeDefinition.h>
|
||||||
|
#include <util/core_util.h>
|
||||||
|
|
||||||
|
#include <std/unordered_map.h>
|
||||||
|
|
||||||
|
#include <thirdparty.hpp>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
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 <typename T, typename A = allocator<T>>
|
||||||
|
class IViewableSet : public IViewable<T>, public ISource<detail::SetEvent<T>>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
using WT = value_or_wrapper<T>;
|
||||||
|
mutable rd::unordered_map<Lifetime,
|
||||||
|
ordered_map<T const*, LifetimeDefinition, wrapper::TransparentHash<T>, wrapper::TransparentKeyEqual<T>>>
|
||||||
|
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<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \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<void(AddRemove, T const&)> 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<void(Lifetime, T const&)> 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<void(Event const&)> handler) const override = 0;
|
||||||
|
|
||||||
|
virtual bool add(WT) const = 0;
|
||||||
|
|
||||||
|
virtual bool addAll(std::vector<WT> 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 <typename... Args>
|
||||||
|
bool emplace_add(Args&&... args) const
|
||||||
|
{
|
||||||
|
return add(WT{std::forward<Args>(args)...});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
std::is_move_constructible<rd::IViewableSet<int>::Event>::value, "Is move constructible from IViewableSet<int>::Event");
|
||||||
|
|
||||||
|
#endif // RD_CPP_IVIEWABLESET_H
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#include "SignalCookie.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::atomic<int32_t> cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rd_signal_cookie_inc()
|
||||||
|
{
|
||||||
|
++cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rd_signal_cookie_dec()
|
||||||
|
{
|
||||||
|
--cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t rd_signal_cookie_get()
|
||||||
|
{
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef RD_CPP_SIGNALCOOKIE_H
|
||||||
|
#define RD_CPP_SIGNALCOOKIE_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <rd_core_export.h>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
#ifndef RD_CPP_CORE_SIGNAL_H
|
||||||
|
#define RD_CPP_CORE_SIGNAL_H
|
||||||
|
|
||||||
|
#include "interfaces.h"
|
||||||
|
#include "SignalCookie.h"
|
||||||
|
|
||||||
|
#include <lifetime/Lifetime.h>
|
||||||
|
#include <util/core_util.h>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <functional>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief complete class which has \a Signal<T> 's properties
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class Signal final : public ISignal<T>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using WT = typename ISignal<T>::WT;
|
||||||
|
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::function<void(T const&)> action;
|
||||||
|
Lifetime lifetime;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
Event() = delete;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
Event(F&& action, Lifetime lifetime) : action(std::forward<F>(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<counter_t, Event>;
|
||||||
|
|
||||||
|
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 <typename F>
|
||||||
|
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<F>(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<T>::fire;
|
||||||
|
|
||||||
|
void fire(T const& value) const override
|
||||||
|
{
|
||||||
|
fire_impl(value, priority_listeners);
|
||||||
|
fire_impl(value, listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
using ISignal<T>::advise;
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, std::function<void(T const&)> handler) const override
|
||||||
|
{
|
||||||
|
advise0(lifetime, std::move(handler), isPriorityAdvise() ? priority_listeners : listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isPriorityAdvise()
|
||||||
|
{
|
||||||
|
return rd_signal_cookie_get() > 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void priorityAdviseSection(F&& block)
|
||||||
|
{
|
||||||
|
rd_signal_cookie_inc();
|
||||||
|
block();
|
||||||
|
rd_signal_cookie_dec();
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
static_assert(std::is_move_constructible<rd::Signal<int>>::value, "Is not move constructible from Signal<int>");
|
||||||
|
static_assert(std::is_move_constructible<rd::Signal<rd::Void>>::value, "Is not move constructible from Signal<Void>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_SIGNAL_H
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#ifndef RD_CPP_CORE_INTERFACES_H
|
||||||
|
#define RD_CPP_CORE_INTERFACES_H
|
||||||
|
|
||||||
|
#include <lifetime/Lifetime.h>
|
||||||
|
#include <types/wrapper.h>
|
||||||
|
#include <util/core_traits.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief An object that allows to subscribe to events.
|
||||||
|
* \tparam T type of events
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
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<void(T const&)> handler) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief @code advise with Eternal lifetime
|
||||||
|
*/
|
||||||
|
template <typename F>
|
||||||
|
void advise_eternal(F&& handler) const
|
||||||
|
{
|
||||||
|
advise(Lifetime::Eternal(), std::forward<F>(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief @code Void specialisation of @code advise method, at @tparam T=Void
|
||||||
|
*/
|
||||||
|
void advise(Lifetime lifetime, std::function<void()> 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 <typename T>
|
||||||
|
class IViewable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IViewable() = default;
|
||||||
|
|
||||||
|
virtual void view(Lifetime lifetime, std::function<void(Lifetime, T const&)
|
||||||
|
|
||||||
|
>
|
||||||
|
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 <typename T>
|
||||||
|
class ISignal : public ISource<T>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
using WT = value_or_wrapper<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~ISignal() = default;
|
||||||
|
|
||||||
|
virtual void fire(T const& value) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief @code fire specialisation at T=Void
|
||||||
|
*/
|
||||||
|
template <typename U = T>
|
||||||
|
typename std::enable_if_t<util::is_void<U>> fire() const
|
||||||
|
{
|
||||||
|
fire(Void{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_INTERFACES_H
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#ifndef RD_CPP_VIEWABLE_COLLECTIONS_H
|
||||||
|
#define RD_CPP_VIEWABLE_COLLECTIONS_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef RD_CPP_ALLOCATOR_H
|
||||||
|
#define RD_CPP_ALLOCATOR_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
using allocator = std::allocator<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RD_CPP_ALLOCATOR_H
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#ifndef RD_CPP_HASH_H
|
||||||
|
#define RD_CPP_HASH_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
struct hash
|
||||||
|
{
|
||||||
|
size_t operator()(const T& value) const noexcept
|
||||||
|
{
|
||||||
|
return std::hash<T>()(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_HASH_H
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#ifndef RD_CPP_LIST_H
|
||||||
|
#define RD_CPP_LIST_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
int32_t size(T const& value) = delete;
|
||||||
|
|
||||||
|
// c++17 has std::size for std::vector
|
||||||
|
#if __cplusplus < 201703L
|
||||||
|
|
||||||
|
template <typename T, typename A>
|
||||||
|
int32_t size(std::vector<T, A> const& value)
|
||||||
|
{
|
||||||
|
return static_cast<int32_t>(value.size());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <typename T, typename A>
|
||||||
|
int32_t size(std::vector<T, A> const& value)
|
||||||
|
{
|
||||||
|
return std::size(value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T, typename A>
|
||||||
|
void resize(std::vector<T, A>& value, int32_t size)
|
||||||
|
{
|
||||||
|
value.resize(size);
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_LIST_H
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
// ReSharper disable CppUE4CodingStandardNamingViolationWarning
|
||||||
|
#ifndef RD_CPP_TO_STRING_H
|
||||||
|
#define RD_CPP_TO_STRING_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <sstream>
|
||||||
|
#include <atomic>
|
||||||
|
#include <future>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
#include "ww898/utf_converters.hpp"
|
||||||
|
|
||||||
|
#include <thirdparty.hpp>
|
||||||
|
|
||||||
|
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<std::string::value_type>(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 <typename Rep, typename Period>
|
||||||
|
inline std::string to_string(std::chrono::duration<Rep, Period> const& time)
|
||||||
|
{
|
||||||
|
return std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(time).count()) + "ms";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::string to_string(T const* val)
|
||||||
|
{
|
||||||
|
return val ? to_string(*val) : "nullptr";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::string to_string(std::atomic<T> const& value)
|
||||||
|
{
|
||||||
|
return to_string(value.load());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::string to_string(optional<T> const& val)
|
||||||
|
{
|
||||||
|
if (val.has_value())
|
||||||
|
{
|
||||||
|
return to_string(*val);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "nullopt";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F, typename S>
|
||||||
|
inline std::string to_string(const std::pair<F, S> p)
|
||||||
|
{
|
||||||
|
return "(" + to_string(p.first) + ", " + to_string(p.second) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class, class> class C, typename T, typename A>
|
||||||
|
std::string to_string(C<T, A> const& v)
|
||||||
|
{
|
||||||
|
std::string res = "[";
|
||||||
|
for (const auto& item : v)
|
||||||
|
{
|
||||||
|
res += to_string(item);
|
||||||
|
res += ",";
|
||||||
|
}
|
||||||
|
res += "]";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::string as_string(T const& t)
|
||||||
|
{
|
||||||
|
return to_string(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
using std::to_wstring;
|
||||||
|
|
||||||
|
inline std::wstring to_wstring(std::string const& s)
|
||||||
|
{
|
||||||
|
return ww898::utf::conv<std::wstring::value_type>(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::wstring as_wstring(T const& t)
|
||||||
|
{
|
||||||
|
return to_wstring(t);
|
||||||
|
}
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::string to_string(T const& val)
|
||||||
|
{
|
||||||
|
return detail::as_string(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::wstring to_wstring(T const& val)
|
||||||
|
{
|
||||||
|
return detail::as_wstring(val);
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_TO_STRING_H
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef RD_CPP_UNORDERED_MAP_H
|
||||||
|
#define RD_CPP_UNORDERED_MAP_H
|
||||||
|
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
template <class _Key, class _Tp, class _Hash = hash<_Key>, class _Pred = std::equal_to<_Key>,
|
||||||
|
class _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
|
||||||
|
using unordered_map = std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RD_CPP_UNORDERED_MAP_H
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef RD_CPP_UNORDERED_SET_H
|
||||||
|
#define RD_CPP_UNORDERED_SET_H
|
||||||
|
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
template <class _Value, class _Hash = hash<_Value>, class _Pred = std::equal_to<_Value>, class _Alloc = std::allocator<_Value> >
|
||||||
|
using unordered_set = std::unordered_set<_Value, _Hash, _Pred, _Alloc>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RD_CPP_UNORDERED_SET_H
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#include "DateTime.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
DateTime::DateTime(time_t seconds) : seconds(seconds)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const DateTime& lhs, const DateTime& rhs)
|
||||||
|
{
|
||||||
|
return lhs.seconds < rhs.seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const DateTime& lhs, const DateTime& rhs)
|
||||||
|
{
|
||||||
|
return rhs < lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const DateTime& lhs, const DateTime& rhs)
|
||||||
|
{
|
||||||
|
return !(rhs < lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const DateTime& lhs, const DateTime& rhs)
|
||||||
|
{
|
||||||
|
return !(lhs < rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string(DateTime const& time)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::put_time(std::localtime(&time.seconds), "%F %T");
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const DateTime& lhs, const DateTime& rhs)
|
||||||
|
{
|
||||||
|
return lhs.seconds == rhs.seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const DateTime& lhs, const DateTime& rhs)
|
||||||
|
{
|
||||||
|
return !(rhs == lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hash<rd::DateTime>::operator()(const rd::DateTime& value) const noexcept
|
||||||
|
{
|
||||||
|
return rd::hash<decltype(value.seconds)>()(value.seconds);
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef RD_CPP_DATETIME_H
|
||||||
|
#define RD_CPP_DATETIME_H
|
||||||
|
|
||||||
|
#include <std/hash.h>
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <rd_core_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Wrapper around time_t to be synchronized with "Date" in Kt and "DateTime" in C#.
|
||||||
|
*/
|
||||||
|
class RD_CORE_API DateTime
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::time_t seconds;
|
||||||
|
|
||||||
|
explicit DateTime(time_t seconds);
|
||||||
|
|
||||||
|
friend bool RD_CORE_API operator<(const DateTime& lhs, const DateTime& rhs);
|
||||||
|
|
||||||
|
friend bool RD_CORE_API operator>(const DateTime& lhs, const DateTime& rhs);
|
||||||
|
|
||||||
|
friend bool RD_CORE_API operator<=(const DateTime& lhs, const DateTime& rhs);
|
||||||
|
|
||||||
|
friend bool RD_CORE_API operator>=(const DateTime& lhs, const DateTime& rhs);
|
||||||
|
|
||||||
|
friend bool RD_CORE_API operator==(const DateTime& lhs, const DateTime& rhs);
|
||||||
|
|
||||||
|
friend bool RD_CORE_API operator!=(const DateTime& lhs, const DateTime& rhs);
|
||||||
|
|
||||||
|
//"1970-01-01 03:01:38" for example
|
||||||
|
friend std::string RD_CORE_API to_string(DateTime const& time);
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct RD_CORE_API hash<rd::DateTime>
|
||||||
|
{
|
||||||
|
size_t operator()(const rd::DateTime& value) const noexcept;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_DATETIME_H
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef RD_CPP_VOID_H
|
||||||
|
#define RD_CPP_VOID_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief For using in idle events
|
||||||
|
*/
|
||||||
|
class Void
|
||||||
|
{
|
||||||
|
friend inline bool operator==(const Void&, const Void&)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline bool operator!=(const Void&, const Void&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::string to_string(Void const&)
|
||||||
|
{
|
||||||
|
return "void";
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct hash<rd::Void>
|
||||||
|
{
|
||||||
|
size_t operator()(const rd::Void&) const noexcept
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
#endif // RD_CPP_VOID_H
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
#ifndef RD_CPP_WRAPPER_H
|
||||||
|
#define RD_CPP_WRAPPER_H
|
||||||
|
|
||||||
|
#include <util/core_traits.h>
|
||||||
|
#include <std/allocator.h>
|
||||||
|
#include <std/hash.h>
|
||||||
|
#include <std/to_string.h>
|
||||||
|
|
||||||
|
#include <thirdparty.hpp>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
template <typename T, typename A = std::allocator<T>>
|
||||||
|
class Wrapper;
|
||||||
|
|
||||||
|
template <typename T, typename R = void>
|
||||||
|
struct helper
|
||||||
|
{
|
||||||
|
using value_or_wrapper_type = T;
|
||||||
|
using opt_or_wrapper_type = optional<T>;
|
||||||
|
using property_storage = optional<T>;
|
||||||
|
using raw_type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct helper<T, typename std::enable_if_t<util::in_heap_v<T>>>
|
||||||
|
{
|
||||||
|
using value_or_wrapper_type = Wrapper<T>;
|
||||||
|
using opt_or_wrapper_type = Wrapper<T>;
|
||||||
|
using property_storage = Wrapper<T>;
|
||||||
|
using raw_type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*template<typename T>
|
||||||
|
struct helper<optional<T>> {
|
||||||
|
using value_or_wrapper_type = Wrapper<T>;
|
||||||
|
using opt_or_wrapper_type = Wrapper<T>;
|
||||||
|
using property_storage = optional<optional<T>>;
|
||||||
|
using raw_type = T;
|
||||||
|
};*/
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct helper<Wrapper<T>>
|
||||||
|
{
|
||||||
|
using value_or_wrapper_type = Wrapper<T>;
|
||||||
|
using opt_or_wrapper_type = Wrapper<T>;
|
||||||
|
using property_storage = Wrapper<Wrapper<T>>;
|
||||||
|
using raw_type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using value_or_wrapper = typename helper<T>::value_or_wrapper_type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using opt_or_wrapper = typename helper<T>::opt_or_wrapper_type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using property_storage = typename helper<T>::property_storage;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using raw_type = typename helper<T>::raw_type;
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
struct is_wrapper : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_wrapper<Wrapper<T>> : std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool is_wrapper_v = is_wrapper<T>::value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief wrapper over value of any type. It supports semantic of shared ownership due to shared_ptr as storage.
|
||||||
|
* \tparam T type of value
|
||||||
|
*/
|
||||||
|
template <typename T, typename A>
|
||||||
|
class Wrapper final : public std::shared_ptr<T>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
template <typename, typename>
|
||||||
|
friend class Wrapper;
|
||||||
|
|
||||||
|
using Base = std::shared_ptr<T>;
|
||||||
|
|
||||||
|
A alloc;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using type = T;
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
Wrapper() = default;
|
||||||
|
|
||||||
|
Wrapper(Wrapper const&) = default;
|
||||||
|
|
||||||
|
Wrapper& operator=(Wrapper const&) = default;
|
||||||
|
|
||||||
|
Wrapper(Wrapper&&) = default;
|
||||||
|
|
||||||
|
Wrapper& operator=(Wrapper&&) = default;
|
||||||
|
|
||||||
|
constexpr explicit Wrapper(std::nullptr_t) noexcept
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Wrapper(nullopt_t) noexcept
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R, typename = typename std::enable_if_t<util::is_base_of_v<T, R>>>
|
||||||
|
Wrapper(Wrapper<R> const& other) : Base(std::static_pointer_cast<T>(static_cast<std::shared_ptr<R>>(other)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R, typename = typename std::enable_if_t<util::is_base_of_v<T, R>>>
|
||||||
|
Wrapper(Wrapper<R>&& other) : Base(std::static_pointer_cast<T>(static_cast<std::shared_ptr<R>>(std::move(other))))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F, typename G = typename util::not_string_literal<F&&>::type,
|
||||||
|
typename = typename std::enable_if_t<
|
||||||
|
/*util::negation<
|
||||||
|
util::disjunction<
|
||||||
|
std::is_null_pointer<std::decay_t<F>>,
|
||||||
|
std::is_same<Wrapper<T>, std::decay_t<F>>,
|
||||||
|
detail::is_optional<std::decay_t<F>>
|
||||||
|
>
|
||||||
|
>::value*/
|
||||||
|
util::conjunction<std::is_constructible<std::shared_ptr<T>, std::shared_ptr<G>>,
|
||||||
|
util::negation<std::is_abstract<G>>>::value>>
|
||||||
|
Wrapper(F&& value) : Base(std::allocate_shared<G>(alloc, std::forward<F>(value)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
Wrapper(std::shared_ptr<F> const& ptr) noexcept : Base(std::static_pointer_cast<T>(ptr))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
Wrapper(std::shared_ptr<F>&& ptr) noexcept : Base(std::static_pointer_cast<T>(std::move(ptr)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U = T, typename R = typename std::enable_if_t<!std::is_abstract<std::decay_t<U>>::value>>
|
||||||
|
Wrapper(optional<U>&& opt)
|
||||||
|
{
|
||||||
|
if (opt)
|
||||||
|
{
|
||||||
|
*this = std::allocate_shared<U>(alloc, *std::move(opt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
static Wrapper<T> dynamic(Wrapper<R> const& w)
|
||||||
|
{
|
||||||
|
return Wrapper<T>(std::dynamic_pointer_cast<T>(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
static Wrapper<T> dynamic(Wrapper<R>&& w)
|
||||||
|
{
|
||||||
|
return Wrapper<T>(std::dynamic_pointer_cast<T>(std::move(w)));
|
||||||
|
}
|
||||||
|
|
||||||
|
~Wrapper() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
constexpr bool has_value() const
|
||||||
|
{
|
||||||
|
return operator bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr T& operator*() &
|
||||||
|
{
|
||||||
|
return *static_cast<Base&>(*this);
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr T const& operator*() const&
|
||||||
|
{
|
||||||
|
return *static_cast<Base const&>(*this);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*constexpr T &&operator*() &&{
|
||||||
|
return *ptr.get();
|
||||||
|
};*/
|
||||||
|
|
||||||
|
T const* operator->() const
|
||||||
|
{
|
||||||
|
return Base::operator->();
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->()
|
||||||
|
{
|
||||||
|
return Base::operator->();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const noexcept
|
||||||
|
{
|
||||||
|
return Base::operator bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const Wrapper& lhs, const Wrapper& rhs)
|
||||||
|
{
|
||||||
|
bool is_lhs = (bool) lhs;
|
||||||
|
bool is_rhs = (bool) rhs;
|
||||||
|
if (is_lhs != is_rhs)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!is_lhs && !is_rhs)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return *lhs.get() == *rhs.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const Wrapper& lhs, const Wrapper& rhs)
|
||||||
|
{
|
||||||
|
return !(rhs == lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::string to_string(Wrapper const& value)
|
||||||
|
{
|
||||||
|
return value.has_value() ? to_string(*value) : "nullptr"s;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace wrapper
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
decltype(auto) get(T&& w)
|
||||||
|
{
|
||||||
|
return std::forward<T>(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
decltype(auto) get(T const& w)
|
||||||
|
{
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& get(Wrapper<T>& w)
|
||||||
|
{
|
||||||
|
return *w;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T const& get(Wrapper<T> const& w)
|
||||||
|
{
|
||||||
|
return *w;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T&& get(Wrapper<T>&& w)
|
||||||
|
{
|
||||||
|
return *std::move(std::move(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*template<typename T>
|
||||||
|
Wrapper<T> make_wrapper(std::unique_ptr<T> &&value) {
|
||||||
|
return Wrapper<T>(std::move(value));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if_t<!util::in_heap_v<T>, T> unwrap(Wrapper<T>&& ptr)
|
||||||
|
{
|
||||||
|
return std::move(*ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if_t<util::in_heap_v<T>, Wrapper<T>> unwrap(Wrapper<T>&& ptr)
|
||||||
|
{
|
||||||
|
return Wrapper<T>(std::move(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
Wrapper<T> make_wrapper(Args&&... args)
|
||||||
|
{
|
||||||
|
return Wrapper<T>(std::make_shared<T>(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename A, typename... Args>
|
||||||
|
Wrapper<T> allocate_wrapper(const A& alloc, Args&&... args)
|
||||||
|
{
|
||||||
|
return Wrapper<T>(std::allocate_shared<T, A>(alloc, std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
/*template<typename T>
|
||||||
|
constexpr Wrapper<T> null_wrapper = Wrapper<T>(nullptr);*/
|
||||||
|
} // namespace wrapper
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct hash<rd::Wrapper<T>>
|
||||||
|
{
|
||||||
|
size_t operator()(const rd::Wrapper<T>& value) const noexcept
|
||||||
|
{
|
||||||
|
return rd::hash<T>()(*value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
static_assert(rd::is_wrapper<rd::Wrapper<std::wstring>>::value, "is wrapper doesn't work");
|
||||||
|
|
||||||
|
#endif // RD_CPP_WRAPPER_H
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
#ifndef RD_CORE_CPP_TRAITS_H
|
||||||
|
#define RD_CORE_CPP_TRAITS_H
|
||||||
|
|
||||||
|
#include <types/Void.h>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class IPolymorphicSerializable;
|
||||||
|
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
// region non_std
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
constexpr bool is_same_v = std::is_same<T, U>::value;
|
||||||
|
|
||||||
|
template <typename Base, typename Derived>
|
||||||
|
constexpr bool is_base_of_v = std::is_base_of<Base, Derived>::value;
|
||||||
|
|
||||||
|
template <bool B>
|
||||||
|
using bool_constant = std::integral_constant<bool, B>;
|
||||||
|
|
||||||
|
template <class B>
|
||||||
|
struct negation : bool_constant<!bool(B::value)>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class...>
|
||||||
|
struct disjunction : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <class B1>
|
||||||
|
struct disjunction<B1> : B1
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <class B1, class... Bn>
|
||||||
|
struct disjunction<B1, Bn...> : std::conditional_t<bool(B1::value), B1, disjunction<Bn...>>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class...>
|
||||||
|
struct conjunction : std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <class B1>
|
||||||
|
struct conjunction<B1> : B1
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <class B1, class... Bn>
|
||||||
|
struct conjunction<B1, Bn...> : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class...>
|
||||||
|
using void_t = void;
|
||||||
|
|
||||||
|
template <typename F, typename... Args>
|
||||||
|
struct is_invocable
|
||||||
|
: std::is_constructible<std::function<void(Args...)>, std::reference_wrapper<typename std::remove_reference<F>::type>>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R, typename F, typename... Args>
|
||||||
|
struct is_invocable_r
|
||||||
|
: std::is_constructible<std::function<R(Args...)>, std::reference_wrapper<typename std::remove_reference<F>::type>>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class F, class... Ts>
|
||||||
|
constexpr bool is_invocable_v = is_invocable<F, Ts...>::value;
|
||||||
|
|
||||||
|
#ifdef __cpp_lib_is_invocable
|
||||||
|
template <class> struct result_of;
|
||||||
|
|
||||||
|
template <class F, class... TN>
|
||||||
|
struct result_of<F(TN...)>
|
||||||
|
{
|
||||||
|
using type = std::invoke_result_t<F, TN...>;
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
template<class... TN> using result_of = std::result_of<TN...>;
|
||||||
|
#endif
|
||||||
|
template<class... TN> using result_of_t = typename result_of<TN...>::type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool is_enum_v = std::is_enum<T>::value;
|
||||||
|
|
||||||
|
// TO-DO: is is_pod actually required for memcpy-like serialization?
|
||||||
|
template <class T>
|
||||||
|
constexpr bool is_pod_v = std::is_trivial<T>::value && std::is_standard_layout<T>::value;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
/*inline */ constexpr bool is_void = std::is_same<T, Void>::value;
|
||||||
|
|
||||||
|
// region in_heap
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
// using in_heap = disjunction<std::is_abstract<T>, std::is_same<T, std::wstring>>;
|
||||||
|
using in_heap = disjunction<std::is_base_of<IPolymorphicSerializable, T>, std::is_same<T, std::wstring>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
/*inline */ constexpr bool in_heap_v = in_heap<T>::value;
|
||||||
|
|
||||||
|
static_assert(in_heap_v<std::wstring>, "std::wstring should be placed in shared memory");
|
||||||
|
static_assert(!in_heap_v<int>, "int shouldn't be placed in shared memory");
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region literal
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_wstring_literal :
|
||||||
|
/*std::is_same<
|
||||||
|
T,
|
||||||
|
std::add_lvalue_reference_t<const wchar_t[std::extent<std::remove_reference_t<T>>::value]>*/
|
||||||
|
std::is_convertible<T, std::wstring>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, bool = is_wstring_literal<T>::value>
|
||||||
|
struct not_string_literal
|
||||||
|
{
|
||||||
|
using type = std::wstring;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct not_string_literal<std::wstring, true>
|
||||||
|
{
|
||||||
|
using type = std::wstring;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct not_string_literal<T, false>
|
||||||
|
{
|
||||||
|
using type = std::decay_t<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(is_wstring_literal<decltype(L" ")>::value, "is_wstring trait doesn't work");
|
||||||
|
static_assert(is_wstring_literal<decltype(L" ")&&>::value, "is_wstring trait doesn't work");
|
||||||
|
static_assert(is_wstring_literal<decltype(L" ")&>::value, "is_wstring trait doesn't work");
|
||||||
|
static_assert(is_wstring_literal<wchar_t const (&)[1]>::value, "is_wstring trait doesn't work");
|
||||||
|
static_assert(!is_wstring_literal<int>::value, "is_wstring trait doesn't work");
|
||||||
|
// static_assert(is_wstring_literal<std::wstring>::value, "is_wstring trait doesn't work");
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
} // namespace util
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CORE_CPP_TRAITS_H
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
#ifndef RD_CPP_CORE_CPP_UTIL_H
|
||||||
|
#define RD_CPP_CORE_CPP_UTIL_H
|
||||||
|
|
||||||
|
#include "erase_if.h"
|
||||||
|
#include "gen_util.h"
|
||||||
|
#include "overloaded.h"
|
||||||
|
#include "shared_function.h"
|
||||||
|
|
||||||
|
#include <std/hash.h>
|
||||||
|
#include <std/to_string.h>
|
||||||
|
#include <types/wrapper.h>
|
||||||
|
|
||||||
|
#include <thirdparty.hpp>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#define RD_ASSERT_MSG(expr, msg) \
|
||||||
|
if (!(expr)) \
|
||||||
|
{ \
|
||||||
|
spdlog::error("{}\n", msg); \
|
||||||
|
assert(expr); \
|
||||||
|
}
|
||||||
|
#define RD_ASSERT_THROW_MSG(expr, msg) \
|
||||||
|
if (!(expr)) \
|
||||||
|
{ \
|
||||||
|
spdlog::error("{}\n", msg); \
|
||||||
|
throw std::runtime_error(msg); \
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
template <typename T0, typename... T>
|
||||||
|
constexpr std::vector<T0> arrayListOf(T0&& arg, T&&... args)
|
||||||
|
{
|
||||||
|
return std::vector<T0>{std::forward<T0>(arg), std::forward<T>(args)...};
|
||||||
|
}
|
||||||
|
} // namespace util
|
||||||
|
|
||||||
|
namespace wrapper
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
struct TransparentKeyEqual
|
||||||
|
{
|
||||||
|
using is_transparent = void;
|
||||||
|
|
||||||
|
bool operator()(T const& val_l, T const& val_r) const
|
||||||
|
{
|
||||||
|
return val_l == val_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(Wrapper<T> const& ptr_l, Wrapper<T> const& ptr_r) const
|
||||||
|
{
|
||||||
|
return ptr_l == ptr_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(T const* val_l, T const* val_r) const
|
||||||
|
{
|
||||||
|
return *val_l == *val_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(T const& val_r, Wrapper<T> const& ptr_l) const
|
||||||
|
{
|
||||||
|
return *ptr_l == val_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(T const& val_l, T const* ptr_r) const
|
||||||
|
{
|
||||||
|
return val_l == *ptr_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(Wrapper<T> const& val_l, T const* ptr_r) const
|
||||||
|
{
|
||||||
|
return *val_l == *ptr_r;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct TransparentHash
|
||||||
|
{
|
||||||
|
using is_transparent = void;
|
||||||
|
using transparent_key_equal = std::equal_to<>;
|
||||||
|
|
||||||
|
size_t operator()(T const& val) const noexcept
|
||||||
|
{
|
||||||
|
return rd::hash<T>()(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t operator()(Wrapper<T> const& ptr) const noexcept
|
||||||
|
{
|
||||||
|
return rd::hash<Wrapper<T>>()(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t operator()(T const* val) const noexcept
|
||||||
|
{
|
||||||
|
return rd::hash<T>()(*val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace wrapper
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_CORE_CPP_UTIL_H
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef RD_CPP_ENUM_H
|
||||||
|
#define RD_CPP_ENUM_H
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#define RD_DEFINE_ENUM_FLAG_OPERATORS(ENUMTYPE) \
|
||||||
|
extern "C++" \
|
||||||
|
{ \
|
||||||
|
inline ENUMTYPE operator|(ENUMTYPE a, ENUMTYPE b) \
|
||||||
|
{ \
|
||||||
|
return ENUMTYPE(((std::underlying_type_t<ENUMTYPE>) a) | ((std::underlying_type_t<ENUMTYPE>) b)); \
|
||||||
|
} \
|
||||||
|
inline ENUMTYPE& operator|=(ENUMTYPE& a, ENUMTYPE b) \
|
||||||
|
{ \
|
||||||
|
return (ENUMTYPE&) (((std::underlying_type_t<ENUMTYPE>&) a) |= ((std::underlying_type_t<ENUMTYPE>) b)); \
|
||||||
|
} \
|
||||||
|
inline ENUMTYPE operator&(ENUMTYPE a, ENUMTYPE b) \
|
||||||
|
{ \
|
||||||
|
return ENUMTYPE(((std::underlying_type_t<ENUMTYPE>) a) & ((std::underlying_type_t<ENUMTYPE>) b)); \
|
||||||
|
} \
|
||||||
|
inline ENUMTYPE& operator&=(ENUMTYPE& a, ENUMTYPE b) \
|
||||||
|
{ \
|
||||||
|
return (ENUMTYPE&) (((std::underlying_type_t<ENUMTYPE>&) a) &= ((std::underlying_type_t<ENUMTYPE>) b)); \
|
||||||
|
} \
|
||||||
|
inline ENUMTYPE operator~(ENUMTYPE a) \
|
||||||
|
{ \
|
||||||
|
return ENUMTYPE(~((std::underlying_type_t<ENUMTYPE>) a)); \
|
||||||
|
} \
|
||||||
|
inline ENUMTYPE operator^(ENUMTYPE a, ENUMTYPE b) \
|
||||||
|
{ \
|
||||||
|
return ENUMTYPE(((std::underlying_type_t<ENUMTYPE>) a) ^ ((std::underlying_type_t<ENUMTYPE>) b)); \
|
||||||
|
} \
|
||||||
|
inline ENUMTYPE& operator^=(ENUMTYPE& a, ENUMTYPE b) \
|
||||||
|
{ \
|
||||||
|
return (ENUMTYPE&) (((std::underlying_type_t<ENUMTYPE>&) a) ^= ((std::underlying_type_t<ENUMTYPE>) b)); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RD_CPP_ENUM_H
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef RD_CPP_ERASE_IF_H
|
||||||
|
#define RD_CPP_ERASE_IF_H
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
template <typename ContainerT, class _Pr>
|
||||||
|
void erase_if(ContainerT& items, _Pr _Pred)
|
||||||
|
{
|
||||||
|
for (auto it = items.begin(); it != items.end();)
|
||||||
|
{
|
||||||
|
if (_Pred(it->second))
|
||||||
|
it = items.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace util
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_ERASE_IF_H
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#ifndef RD_CPP_GEN_UTIL_H
|
||||||
|
#define RD_CPP_GEN_UTIL_H
|
||||||
|
|
||||||
|
#include <std/hash.h>
|
||||||
|
#include <std/allocator.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
|
||||||
|
template <template <class, class> class C, typename T, typename A = allocator<T>>
|
||||||
|
size_t contentHashCode(C<T, A> const& list) noexcept
|
||||||
|
{
|
||||||
|
size_t __r = 0;
|
||||||
|
for (auto const& e : list)
|
||||||
|
{
|
||||||
|
__r = __r * 31 + hash<T>()(e);
|
||||||
|
}
|
||||||
|
return __r;
|
||||||
|
// TO-DO faster for integrals
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
|
||||||
|
size_t contentDeepHashCode(T const& value) noexcept
|
||||||
|
{
|
||||||
|
return rd::hash<T>()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
using remove_all_t = std::remove_reference_t<std::remove_cv_t<T>>;
|
||||||
|
|
||||||
|
// optional and rd::Wrapper
|
||||||
|
template <class T, typename = std::enable_if_t<!std::is_integral<T>::value>>
|
||||||
|
typename std::enable_if_t<std::is_same<decltype(T{}.has_value()), bool>::value, size_t> contentDeepHashCode(T const& value) noexcept
|
||||||
|
{
|
||||||
|
return rd::hash<remove_all_t<T>>()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// containers
|
||||||
|
template <class T, typename = std::enable_if_t<!std::is_integral<T>::value>>
|
||||||
|
typename std::enable_if_t<std::is_integral<remove_all_t<decltype(*begin(T{}))>>::value, size_t> contentDeepHashCode(T const& value) noexcept
|
||||||
|
{
|
||||||
|
return contentHashCode(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// containers of non-integral types
|
||||||
|
template <class T, typename = std::enable_if_t<!std::is_integral<T>::value>>
|
||||||
|
typename std::enable_if_t<!std::is_integral<remove_all_t<decltype(*begin(T{}))>>::value, size_t> contentDeepHashCode(T const& value) noexcept
|
||||||
|
{
|
||||||
|
size_t result = 1;
|
||||||
|
for (auto const& x : value)
|
||||||
|
{
|
||||||
|
result = 31 * result + contentDeepHashCode<remove_all_t<decltype(x)>>(x);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_GEN_UTIL_H
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#ifndef RD_CPP_OVERLOADED_H
|
||||||
|
#define RD_CPP_OVERLOADED_H
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
template <typename... Ts>
|
||||||
|
struct overloaded;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct overloaded<T> : T
|
||||||
|
{
|
||||||
|
overloaded(T t) : T(t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
using T::operator();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, class... Ts>
|
||||||
|
struct overloaded<T, Ts...> : T, overloaded<Ts...>
|
||||||
|
{
|
||||||
|
overloaded(T t, Ts... rest) : T(t), overloaded<Ts...>(rest...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
using T::operator();
|
||||||
|
using overloaded<Ts...>::operator();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
overloaded<Ts...> make_visitor(Ts... lambdas)
|
||||||
|
{
|
||||||
|
return overloaded<Ts...>(lambdas...);
|
||||||
|
}
|
||||||
|
} // namespace util
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_OVERLOADED_H
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef RD_CPP_SHARED_FUNCTION_H
|
||||||
|
#define RD_CPP_SHARED_FUNCTION_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
template <class F>
|
||||||
|
struct shared_function
|
||||||
|
{
|
||||||
|
std::shared_ptr<F> f;
|
||||||
|
|
||||||
|
shared_function() = delete; // = default works, but I don't use it
|
||||||
|
shared_function(F&& f_) : f(std::make_shared<F>(std::move(f_)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_function(shared_function const&) = default;
|
||||||
|
|
||||||
|
shared_function(shared_function&&) = default;
|
||||||
|
|
||||||
|
shared_function& operator=(shared_function const&) = default;
|
||||||
|
|
||||||
|
shared_function& operator=(shared_function&&) = default;
|
||||||
|
|
||||||
|
template <class... As>
|
||||||
|
auto operator()(As&&... as) const
|
||||||
|
{
|
||||||
|
return (*f)(std::forward<As>(as)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class F>
|
||||||
|
shared_function<std::decay_t<F> > make_shared_function(F&& f)
|
||||||
|
{
|
||||||
|
return {std::forward<F>(f)};
|
||||||
|
}
|
||||||
|
} // namespace util
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_SHARED_FUNCTION_H
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
|
||||||
|
#ifndef RD_FRAMEWORK_API_H
|
||||||
|
#define RD_FRAMEWORK_API_H
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
# ifdef RD_FRAMEWORK_STATIC_DEFINE
|
||||||
|
# define RD_FRAMEWORK_API
|
||||||
|
# define RD_FRAMEWORK_NO_EXPORT
|
||||||
|
# else
|
||||||
|
# ifndef RD_FRAMEWORK_API
|
||||||
|
# ifdef rd_framework_cpp_EXPORTS
|
||||||
|
/* We are building this library */
|
||||||
|
# define RD_FRAMEWORK_API __declspec(dllexport)
|
||||||
|
# else
|
||||||
|
/* We are using this library */
|
||||||
|
# define RD_FRAMEWORK_API __declspec(dllimport)
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef RD_FRAMEWORK_NO_EXPORT
|
||||||
|
# define RD_FRAMEWORK_NO_EXPORT
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef RD_FRAMEWORK_DEPRECATED
|
||||||
|
# define RD_FRAMEWORK_DEPRECATED __declspec(deprecated)
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef RD_FRAMEWORK_DEPRECATED_EXPORT
|
||||||
|
# define RD_FRAMEWORK_DEPRECATED_EXPORT RD_FRAMEWORK_API RD_FRAMEWORK_DEPRECATED
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef RD_FRAMEWORK_DEPRECATED_NO_EXPORT
|
||||||
|
# define RD_FRAMEWORK_DEPRECATED_NO_EXPORT RD_FRAMEWORK_NO_EXPORT RD_FRAMEWORK_DEPRECATED
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if 0 /* DEFINE_NO_DEPRECATED */
|
||||||
|
# ifndef RD_FRAMEWORK_NO_DEPRECATED
|
||||||
|
# define RD_FRAMEWORK_NO_DEPRECATED
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__)
|
||||||
|
# ifdef RD_FRAMEWORK_STATIC_DEFINE
|
||||||
|
# define RD_FRAMEWORK_API
|
||||||
|
# define RD_FRAMEWORK_NO_EXPORT
|
||||||
|
# else
|
||||||
|
# ifndef RD_FRAMEWORK_API
|
||||||
|
# ifdef rd_framework_cpp_EXPORTS
|
||||||
|
/* We are building this library */
|
||||||
|
# define RD_FRAMEWORK_API __attribute__((visibility("default")))
|
||||||
|
# else
|
||||||
|
/* We are using this library */
|
||||||
|
# define RD_FRAMEWORK_API __attribute__((visibility("default")))
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef RD_FRAMEWORK_NO_EXPORT
|
||||||
|
# define RD_FRAMEWORK_NO_EXPORT __attribute__((visibility("hidden")))
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef RD_FRAMEWORK_DEPRECATED
|
||||||
|
# define RD_FRAMEWORK_DEPRECATED __attribute__ ((__deprecated__))
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef RD_FRAMEWORK_DEPRECATED_EXPORT
|
||||||
|
# define RD_FRAMEWORK_DEPRECATED_EXPORT RD_FRAMEWORK_API RD_FRAMEWORK_DEPRECATED
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef RD_FRAMEWORK_DEPRECATED_NO_EXPORT
|
||||||
|
# define RD_FRAMEWORK_DEPRECATED_NO_EXPORT RD_FRAMEWORK_NO_EXPORT RD_FRAMEWORK_DEPRECATED
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if 0 /* DEFINE_NO_DEPRECATED */
|
||||||
|
# ifndef RD_FRAMEWORK_NO_DEPRECATED
|
||||||
|
# define RD_FRAMEWORK_NO_DEPRECATED
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* RD_FRAMEWORK_API_H */
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#include "IProtocol.h"
|
||||||
|
|
||||||
|
#include "scheduler/base/IScheduler.h"
|
||||||
|
#include "protocol/Identities.h"
|
||||||
|
#include "base/IWire.h"
|
||||||
|
#include "base/IProtocol.h"
|
||||||
|
//#include "serialization/SerializationCtx.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
IProtocol::IProtocol()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IProtocol::IProtocol(std::shared_ptr<Identities> identity, IScheduler* scheduler, std::shared_ptr<IWire> wire)
|
||||||
|
: identity(std::move(identity)), scheduler(scheduler), wire(std::move(wire))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const IProtocol* IProtocol::get_protocol() const
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
IScheduler* IProtocol::get_scheduler() const
|
||||||
|
{
|
||||||
|
return scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IWire* IProtocol::get_wire() const
|
||||||
|
{
|
||||||
|
return wire.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Serializers& IProtocol::get_serializers() const
|
||||||
|
{
|
||||||
|
return *serializers;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Identities* IProtocol::get_identity() const
|
||||||
|
{
|
||||||
|
return identity.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const RName& IProtocol::get_location() const
|
||||||
|
{
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
IProtocol::~IProtocol() = default;
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
#ifndef RD_CPP_IPROTOCOL_H
|
||||||
|
#define RD_CPP_IPROTOCOL_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "IRdDynamic.h"
|
||||||
|
#include "serialization/Serializers.h"
|
||||||
|
#include "protocol/Identities.h"
|
||||||
|
#include "scheduler/base/IScheduler.h"
|
||||||
|
#include "base/IWire.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
// region predeclared
|
||||||
|
|
||||||
|
class SerializationCtx;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A root node in an object graph which can be synchronized with its remote copy over a network or a similar connection.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API IProtocol : public IRdDynamic
|
||||||
|
{
|
||||||
|
friend class RdExtBase;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::unique_ptr<Serializers> serializers = std::make_unique<Serializers>();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mutable RName location;
|
||||||
|
|
||||||
|
std::shared_ptr<Identities> identity;
|
||||||
|
IScheduler* scheduler = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::shared_ptr<IWire> wire;
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
IProtocol();
|
||||||
|
|
||||||
|
IProtocol(std::shared_ptr<Identities> identity, IScheduler* scheduler, std::shared_ptr<IWire> wire);
|
||||||
|
|
||||||
|
IProtocol(IProtocol&& other) noexcept = default;
|
||||||
|
|
||||||
|
IProtocol& operator=(IProtocol&& other) noexcept = default;
|
||||||
|
|
||||||
|
virtual ~IProtocol();
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
const Identities* get_identity() const;
|
||||||
|
|
||||||
|
const IProtocol* get_protocol() const override;
|
||||||
|
|
||||||
|
IScheduler* get_scheduler() const;
|
||||||
|
|
||||||
|
const IWire* get_wire() const;
|
||||||
|
|
||||||
|
const Serializers& get_serializers() const;
|
||||||
|
|
||||||
|
const RName& get_location() const override;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_IPROTOCOL_H
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#ifndef RD_CPP_FRAMEWORK_IRDBINDABLE_H
|
||||||
|
#define RD_CPP_FRAMEWORK_IRDBINDABLE_H
|
||||||
|
|
||||||
|
#include "IRdDynamic.h"
|
||||||
|
#include "lifetime/Lifetime.h"
|
||||||
|
#include "protocol/RdId.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class Identities;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief A non-root node in an object graph which can be synchronized with its remote copy over a network or
|
||||||
|
* a similar connection.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API IRdBindable : public IRdDynamic
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
IRdBindable() = default;
|
||||||
|
|
||||||
|
IRdBindable(IRdBindable&& other) = default;
|
||||||
|
|
||||||
|
IRdBindable& operator=(IRdBindable&& other) = default;
|
||||||
|
|
||||||
|
virtual ~IRdBindable() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
virtual void set_id(RdId id) const = 0;
|
||||||
|
virtual RdId get_id() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Inserts the node into the object graph under the given [parent] and assigns the specified [name] to it.
|
||||||
|
* The node will be removed from the graph when the specified [lf] lifetime is terminated.
|
||||||
|
*
|
||||||
|
* \param lf lifetime of node.
|
||||||
|
* \param parent to whom bind.
|
||||||
|
* \param name specified name of node.
|
||||||
|
*/
|
||||||
|
virtual void bind(Lifetime lf, IRdDynamic const* parent, string_view name) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Assigns IDs to this node and its child nodes in the graph.
|
||||||
|
*
|
||||||
|
* \param identities to generate unique identifiers for children.
|
||||||
|
* \param id which is assigned to this node.
|
||||||
|
*/
|
||||||
|
virtual void identify(Identities const& identities, RdId const& id) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if_t<!util::is_base_of_v<IRdBindable, typename std::decay_t<T>>> inline identifyPolymorphic(
|
||||||
|
T&&, Identities const& /*identities*/, RdId const& /*id*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// template <>
|
||||||
|
inline void identifyPolymorphic(const IRdBindable& that, Identities const& identities, RdId const& id)
|
||||||
|
{
|
||||||
|
that.identify(identities, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if_t<util::is_base_of_v<IRdBindable, T>> inline identifyPolymorphic(
|
||||||
|
std::vector<T> const& that, Identities const& identities, RdId const& id)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < that.size(); ++i)
|
||||||
|
{
|
||||||
|
that[i].identify(identities, id.mix(static_cast<int32_t>(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if_t<!util::is_base_of_v<IRdBindable, typename std::decay_t<T>>> inline bindPolymorphic(
|
||||||
|
T&&, Lifetime /*lf*/, const IRdDynamic* /*parent*/, string_view /*name*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void bindPolymorphic(IRdBindable const& that, Lifetime lf, const IRdDynamic* parent, string_view name)
|
||||||
|
{
|
||||||
|
that.bind(lf, parent, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if_t<util::is_base_of_v<IRdBindable, T>> inline bindPolymorphic(
|
||||||
|
std::vector<T> const& that, Lifetime lf, IRdDynamic const* parent, string_view name)
|
||||||
|
{
|
||||||
|
for (auto& obj : that)
|
||||||
|
{
|
||||||
|
obj.bind(lf, parent, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_FRAMEWORK_IRDBINDABLE_H
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef RD_CPP_IRDDYNAMIC_H
|
||||||
|
#define RD_CPP_IRDDYNAMIC_H
|
||||||
|
|
||||||
|
#include "impl/RName.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
// region predeclared
|
||||||
|
|
||||||
|
class IProtocol;
|
||||||
|
|
||||||
|
class SerializationCtx;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief A node in a graph of entities that can be synchronized with its remote copy over a network or
|
||||||
|
* a similar connection.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API IRdDynamic
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
IRdDynamic() = default;
|
||||||
|
|
||||||
|
IRdDynamic(IRdDynamic&& other) = default;
|
||||||
|
|
||||||
|
IRdDynamic& operator=(IRdDynamic&& other) = default;
|
||||||
|
|
||||||
|
virtual ~IRdDynamic() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
virtual const IProtocol* get_protocol() const = 0;
|
||||||
|
|
||||||
|
virtual SerializationCtx& get_serialization_context() const = 0;
|
||||||
|
|
||||||
|
virtual const RName& get_location() const = 0;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_IRDDYNAMIC_H
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#ifndef RD_CPP_FRAMEWORK_IRDREACTIVE_H
|
||||||
|
#define RD_CPP_FRAMEWORK_IRDREACTIVE_H
|
||||||
|
|
||||||
|
#include "IRdBindable.h"
|
||||||
|
#include "scheduler/base/IScheduler.h"
|
||||||
|
#include "IRdWireable.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief A non-root node in an object graph which can be synchronized with its remote copy over a network or
|
||||||
|
* a similar connection, and which allows to subscribe to its changes.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API IRdReactive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief If set to true, local changes to this object can be performed on any thread.
|
||||||
|
* Otherwise, local changes can be performed only on the UI thread.
|
||||||
|
*/
|
||||||
|
bool async = false;
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
IRdReactive() = default;
|
||||||
|
|
||||||
|
virtual ~IRdReactive() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Scheduler on which wire invokes callback [onWireReceived]. Default is the same as [protocol]'s one.
|
||||||
|
* \return scheduler
|
||||||
|
*/
|
||||||
|
virtual IScheduler* get_wire_scheduler() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Callback that wire triggers when it receives messaged
|
||||||
|
* \param buffer where serialised info is stored
|
||||||
|
*/
|
||||||
|
virtual void on_wire_received(Buffer buffer) const = 0;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_FRAMEWORK_IRDREACTIVE_H
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#include "IRdWireable.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#ifndef RD_CPP_IRDWIREABLE_H
|
||||||
|
#define RD_CPP_IRDWIREABLE_H
|
||||||
|
|
||||||
|
#include "protocol/RdId.h"
|
||||||
|
#include "scheduler/base/IScheduler.h"
|
||||||
|
#include "protocol/Buffer.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RD_FRAMEWORK_API IRdWireable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
mutable RdId rdid = RdId::Null();
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_IRDWIREABLE_H
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#include "ISerializersOwner.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
void ISerializersOwner::registry(Serializers const& serializers) const
|
||||||
|
{
|
||||||
|
auto it = used.insert(&serializers);
|
||||||
|
if (!it.second)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerSerializersCore(serializers);
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#ifndef RD_CPP_ISERIALIZERSOWNER_H
|
||||||
|
#define RD_CPP_ISERIALIZERSOWNER_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
// region predeclared
|
||||||
|
|
||||||
|
class Serializers;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
class RD_FRAMEWORK_API ISerializersOwner
|
||||||
|
{
|
||||||
|
mutable std::unordered_set<Serializers const*> used;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
virtual ~ISerializersOwner() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
void registry(Serializers const& serializers) const;
|
||||||
|
|
||||||
|
virtual void registerSerializersCore(Serializers const& serializers) const = 0;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_ISERIALIZERSOWNER_H
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include "IUnknownInstance.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
IUnknownInstance::IUnknownInstance()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IUnknownInstance::IUnknownInstance(const RdId& unknownId) : unknownId(unknownId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IUnknownInstance::IUnknownInstance(RdId&& unknownId) : unknownId(std::move(unknownId))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef RD_CPP_IUNKNOWNINSTANCE_H
|
||||||
|
#define RD_CPP_IUNKNOWNINSTANCE_H
|
||||||
|
|
||||||
|
#include "protocol/RdId.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RD_FRAMEWORK_API IUnknownInstance
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RdId unknownId{0};
|
||||||
|
|
||||||
|
IUnknownInstance();
|
||||||
|
|
||||||
|
explicit IUnknownInstance(const RdId& unknownId);
|
||||||
|
|
||||||
|
explicit IUnknownInstance(RdId&& unknownId);
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_IUNKNOWNINSTANCE_H
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#ifndef RD_CPP_IWIRE_H
|
||||||
|
#define RD_CPP_IWIRE_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "reactive/base/interfaces.h"
|
||||||
|
#include "base/IRdReactive.h"
|
||||||
|
#include "reactive/Property.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RdReactiveBase;
|
||||||
|
/**
|
||||||
|
* \brief Sends and receives serialized object data over a network or a similar connection.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API IWire
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Property<bool> connected{false};
|
||||||
|
Property<bool> heartbeatAlive{false};
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
IWire() = default;
|
||||||
|
|
||||||
|
IWire(IWire&&) = default;
|
||||||
|
|
||||||
|
virtual ~IWire() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sends a data block with the given [id] and the given [writer] function that can write the data.
|
||||||
|
* \param id of recipient.
|
||||||
|
* \param writer is used to serialise data before send.
|
||||||
|
*/
|
||||||
|
virtual void send(RdId const& id, std::function<void(Buffer& buffer)> writer) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Adds a [handler] for receiving updated values of the object with the given [id]. The handler is removed
|
||||||
|
* when the given [lifetime] is terminated.
|
||||||
|
* \param lifetime lifetime of subscription.
|
||||||
|
* \param entity to be subscripted
|
||||||
|
*/
|
||||||
|
virtual void advise(Lifetime lifetime, RdReactiveBase const* entity) const = 0;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_IWIRE_H
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#include "RdBindableBase.h"
|
||||||
|
|
||||||
|
#include "reactive/base/SignalX.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
std::string RdBindableBase::toString() const
|
||||||
|
{
|
||||||
|
return "location=" + to_string(location) + ",rdid=" + to_string(rdid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RdBindableBase::is_bound() const
|
||||||
|
{
|
||||||
|
return parent != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RdBindableBase::bind(Lifetime lf, IRdDynamic const* parent, string_view name) const
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(!is_bound(), ("Trying to bind already bound this to " + to_string(parent->get_location())));
|
||||||
|
lf->bracket(
|
||||||
|
[this, lf, parent, &name] {
|
||||||
|
this->parent = parent;
|
||||||
|
location = parent->get_location().sub(name, ".");
|
||||||
|
this->bind_lifetime = lf;
|
||||||
|
},
|
||||||
|
[this, lf]() {
|
||||||
|
this->bind_lifetime = lf;
|
||||||
|
location = location.sub("<<unbound>>", "::");
|
||||||
|
this->parent = nullptr;
|
||||||
|
rdid = RdId::Null();
|
||||||
|
});
|
||||||
|
|
||||||
|
get_protocol()->get_scheduler()->assert_thread();
|
||||||
|
|
||||||
|
priorityAdviseSection([this, lf]() { init(lf); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be overriden if derived class have bindable members
|
||||||
|
void RdBindableBase::identify(const Identities& identities, RdId const& id) const
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(rdid.isNull(), "Already has RdId: " + to_string(rdid) + ", entities: $this");
|
||||||
|
RD_ASSERT_MSG(!id.isNull(), "Assigned RdId mustn't be null, entities: $this");
|
||||||
|
|
||||||
|
this->rdid = id;
|
||||||
|
for (const auto& it : bindable_extensions)
|
||||||
|
{
|
||||||
|
identifyPolymorphic(*(it.second), identities, id.mix(".").mix(it.first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const IProtocol* RdBindableBase::get_protocol() const
|
||||||
|
{
|
||||||
|
if (is_bound())
|
||||||
|
{
|
||||||
|
auto protocol = parent->get_protocol();
|
||||||
|
if (protocol != nullptr)
|
||||||
|
{
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::invalid_argument("Not bound: " + to_string(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
const RName& RdBindableBase::get_location() const
|
||||||
|
{
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
RdId RdBindableBase::get_id() const
|
||||||
|
{
|
||||||
|
return rdid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RdBindableBase::set_id(RdId id) const
|
||||||
|
{
|
||||||
|
rdid = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializationCtx& RdBindableBase::get_serialization_context() const
|
||||||
|
{
|
||||||
|
if (is_bound())
|
||||||
|
{
|
||||||
|
return parent->get_serialization_context();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("Not bound: " + to_string(location));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RdBindableBase::init(Lifetime lifetime) const
|
||||||
|
{
|
||||||
|
for (const auto& it : bindable_extensions)
|
||||||
|
{
|
||||||
|
if (it.second != nullptr)
|
||||||
|
{
|
||||||
|
bindPolymorphic(*(it.second), lifetime, this, it.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
#ifndef RD_CPP_RDBINDABLEBASE_H
|
||||||
|
#define RD_CPP_RDBINDABLEBASE_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "IRdBindable.h"
|
||||||
|
#include "base/IProtocol.h"
|
||||||
|
|
||||||
|
#include "thirdparty.hpp"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RD_FRAMEWORK_API RdBindableBase : public virtual IRdBindable /*, IPrintable*/
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
mutable RName location;
|
||||||
|
mutable IRdDynamic const* parent = nullptr;
|
||||||
|
mutable RdId rdid = RdId::Null();
|
||||||
|
|
||||||
|
mutable optional<Lifetime> bind_lifetime;
|
||||||
|
|
||||||
|
bool is_bound() const;
|
||||||
|
|
||||||
|
const IProtocol* get_protocol() const override;
|
||||||
|
|
||||||
|
virtual std::string toString() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
RdBindableBase() : location("<<not bound>>")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RdBindableBase(RdBindableBase&& other) = default;
|
||||||
|
|
||||||
|
RdBindableBase& operator=(RdBindableBase&& other) = default;
|
||||||
|
|
||||||
|
~RdBindableBase() override = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// need to implement in subclasses
|
||||||
|
virtual void init(Lifetime lifetime) const;
|
||||||
|
|
||||||
|
const RName& get_location() const override;
|
||||||
|
|
||||||
|
RdId get_id() const override;
|
||||||
|
|
||||||
|
void set_id(RdId id) const override;
|
||||||
|
|
||||||
|
void bind(Lifetime lf, IRdDynamic const* parent, string_view name) const override;
|
||||||
|
|
||||||
|
void identify(const Identities& identities, RdId const& id) const override;
|
||||||
|
|
||||||
|
SerializationCtx& get_serialization_context() const override;
|
||||||
|
|
||||||
|
mutable ordered_map<std::string, std::shared_ptr<IRdBindable>> bindable_extensions; // TO-DO concurrency
|
||||||
|
// mutable std::map<std::string, std::any> non_bindable_extensions;//TO-DO concurrency
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
auto getOrCreateExtension(std::string name, Args&&... args) const ->
|
||||||
|
typename std::enable_if_t<util::is_base_of_v<IRdBindable, T>, T> const&
|
||||||
|
{
|
||||||
|
auto it = bindable_extensions.find(name);
|
||||||
|
if (it != bindable_extensions.end())
|
||||||
|
{
|
||||||
|
return *dynamic_cast<T const*>(it->second.get());
|
||||||
|
}
|
||||||
|
std::shared_ptr<T> new_extension = std::make_shared<T>(std::forward<Args>(args)...);
|
||||||
|
T const& res = *new_extension.get();
|
||||||
|
if (bind_lifetime.has_value())
|
||||||
|
{
|
||||||
|
auto protocol = get_protocol();
|
||||||
|
new_extension->identify(*protocol->get_identity(), rdid.mix(".").mix(name));
|
||||||
|
new_extension->bind(*bind_lifetime, this, name);
|
||||||
|
}
|
||||||
|
bindable_extensions.emplace(name, std::move(new_extension));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* template<typename T>
|
||||||
|
std::enable_if_t<!util::is_base_of_v<IRdBindable, T>, T> const &
|
||||||
|
getOrCreateExtension(std::string const &name, std::function<T()> create) const {
|
||||||
|
if (non_bindable_extensions.count(name) == 0) {
|
||||||
|
return std::any_cast<T const &>(non_bindable_extensions[name] = create());
|
||||||
|
}
|
||||||
|
return std::any_cast<T const &>(non_bindable_extensions.at(name));
|
||||||
|
}*/
|
||||||
|
};
|
||||||
|
|
||||||
|
// T : RdBindableBase
|
||||||
|
template <typename T>
|
||||||
|
T& withId(T& that, RdId const& id)
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(that.get_id() == RdId::Null(), "this.id != RdId.NULL_ID, but " + to_string(that.get_id()));
|
||||||
|
RD_ASSERT_MSG(id != RdId::Null(), "id != RdId.NULL_ID");
|
||||||
|
|
||||||
|
that.set_id(id);
|
||||||
|
return that;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& statics(T& that, int64_t id)
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG((id > 0 && id < RdId::MAX_STATIC_ID), ("Expected id > 0 && id < RdId.MaxStaticId, got " + std::to_string(id)));
|
||||||
|
return withId(that, RdId(static_cast<int64_t>(id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& withIdFromName(T& that, std::string const& name)
|
||||||
|
{
|
||||||
|
return withId(that, RdId::Null().mix(name));
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // RD_CPP_RDBINDABLEBASE_H
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
#ifndef RD_CPP_RDPROPERTYBASE_H
|
||||||
|
#define RD_CPP_RDPROPERTYBASE_H
|
||||||
|
|
||||||
|
#include "base/RdReactiveBase.h"
|
||||||
|
#include "serialization/Polymorphic.h"
|
||||||
|
#include "reactive/Property.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4250)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
template <typename T, typename S = Polymorphic<T>>
|
||||||
|
class RdPropertyBase : public RdReactiveBase, public Property<T>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
using WT = typename IProperty<T>::WT;
|
||||||
|
// mastering
|
||||||
|
mutable int32_t master_version = 0;
|
||||||
|
mutable bool default_value_changed = false;
|
||||||
|
|
||||||
|
// init
|
||||||
|
public:
|
||||||
|
mutable bool optimize_nested = false;
|
||||||
|
|
||||||
|
bool is_master = false;
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
RdPropertyBase() = default;
|
||||||
|
|
||||||
|
RdPropertyBase(RdPropertyBase const&) = delete;
|
||||||
|
|
||||||
|
RdPropertyBase(RdPropertyBase&& other) = default;
|
||||||
|
|
||||||
|
RdPropertyBase& operator=(RdPropertyBase&& other) = default;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
explicit RdPropertyBase(F&& value) : Property<T>(std::forward<F>(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~RdPropertyBase() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
bool is_default_value_changed() const
|
||||||
|
{
|
||||||
|
return default_value_changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(Lifetime lifetime) const override
|
||||||
|
{
|
||||||
|
RdReactiveBase::init(lifetime);
|
||||||
|
|
||||||
|
if (!optimize_nested)
|
||||||
|
{
|
||||||
|
this->change.advise(lifetime, [this](T const& v) {
|
||||||
|
if (is_local_change)
|
||||||
|
{
|
||||||
|
if (this->has_value())
|
||||||
|
{
|
||||||
|
const IProtocol* iProtocol = get_protocol();
|
||||||
|
const Identities* identity = iProtocol->get_identity();
|
||||||
|
identifyPolymorphic(v, *identity, identity->next(rdid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
advise(lifetime, [this](T const& v) {
|
||||||
|
if (!is_local_change)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (is_master)
|
||||||
|
{
|
||||||
|
master_version++;
|
||||||
|
}
|
||||||
|
get_wire()->send(rdid, [this, &v](Buffer& buffer) {
|
||||||
|
buffer.write_integral<int32_t>(master_version);
|
||||||
|
S::write(this->get_serialization_context(), buffer, v);
|
||||||
|
spdlog::get("logSend")->trace("SEND property {} + {}:: ver = {}, value = {}", to_string(location), to_string(rdid),
|
||||||
|
std::to_string(master_version), to_string(v));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
get_wire()->advise(lifetime, this);
|
||||||
|
|
||||||
|
if (!optimize_nested)
|
||||||
|
{
|
||||||
|
this->view(lifetime, [this](Lifetime lf, T const& v) {
|
||||||
|
if (this->has_value())
|
||||||
|
{
|
||||||
|
bindPolymorphic(v, lf, this, "$");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_wire_received(Buffer buffer) const override
|
||||||
|
{
|
||||||
|
int32_t version = buffer.read_integral<int32_t>();
|
||||||
|
WT v = S::read(this->get_serialization_context(), buffer);
|
||||||
|
|
||||||
|
bool rejected = is_master && version < master_version;
|
||||||
|
spdlog::get("logSend")->trace("RECV property {} {}:: oldver={}, ver={}, value = {}{}", to_string(location), to_string(rdid),
|
||||||
|
master_version, version, to_string(v), (rejected ? ">> REJECTED" : ""));
|
||||||
|
if (rejected)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
master_version = version;
|
||||||
|
|
||||||
|
Property<T>::set(std::move(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, std::function<void(T const&)> handler) const override
|
||||||
|
{
|
||||||
|
if (is_bound())
|
||||||
|
{
|
||||||
|
assert_threading();
|
||||||
|
}
|
||||||
|
Property<T>::advise(lifetime, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(value_or_wrapper<T> new_value) const override
|
||||||
|
{
|
||||||
|
this->local_change([this, new_value = std::move(new_value)]() mutable {
|
||||||
|
this->default_value_changed = true;
|
||||||
|
Property<T>::set(std::move(new_value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const RdPropertyBase& lhs, const RdPropertyBase& rhs)
|
||||||
|
{
|
||||||
|
return &lhs == &rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const RdPropertyBase& lhs, const RdPropertyBase& rhs)
|
||||||
|
{
|
||||||
|
return !(rhs == lhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static_assert(std::is_move_constructible<rd::RdPropertyBase<int>>::value, "Is move constructible from RdPropertyBase<int>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_RDPROPERTYBASE_H
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#include "RdReactiveBase.h"
|
||||||
|
|
||||||
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
static std::shared_ptr<spdlog::logger> logReceived =
|
||||||
|
spdlog::stderr_color_mt<spdlog::synchronous_factory>("logReceived", spdlog::color_mode::automatic);
|
||||||
|
std::shared_ptr<spdlog::logger> logSend =
|
||||||
|
spdlog::stderr_color_mt<spdlog::synchronous_factory>("logSend", spdlog::color_mode::automatic);
|
||||||
|
|
||||||
|
RdReactiveBase::RdReactiveBase(RdReactiveBase&& other) : RdBindableBase(std::move(other)) /*, async(other.async)*/
|
||||||
|
{
|
||||||
|
async = other.async;
|
||||||
|
}
|
||||||
|
|
||||||
|
RdReactiveBase& RdReactiveBase::operator=(RdReactiveBase&& other)
|
||||||
|
{
|
||||||
|
async = other.async;
|
||||||
|
static_cast<RdBindableBase&>(*this) = std::move(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IWire* RdReactiveBase::get_wire() const
|
||||||
|
{
|
||||||
|
return get_protocol()->get_wire();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RdReactiveBase::assert_threading() const
|
||||||
|
{
|
||||||
|
if (!async)
|
||||||
|
{
|
||||||
|
get_default_scheduler()->assert_thread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RdReactiveBase::assert_bound() const
|
||||||
|
{
|
||||||
|
if (!is_bound())
|
||||||
|
{
|
||||||
|
throw std::invalid_argument("Not bound");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Serializers& RdReactiveBase::get_serializers() const
|
||||||
|
{
|
||||||
|
return *get_protocol()->serializers.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
IScheduler* RdReactiveBase::get_default_scheduler() const
|
||||||
|
{
|
||||||
|
return get_protocol()->get_scheduler();
|
||||||
|
}
|
||||||
|
|
||||||
|
IScheduler* RdReactiveBase::get_wire_scheduler() const
|
||||||
|
{
|
||||||
|
return get_default_scheduler();
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#ifndef RD_CPP_RDREACTIVEBASE_H
|
||||||
|
#define RD_CPP_RDREACTIVEBASE_H
|
||||||
|
|
||||||
|
#include "base/RdBindableBase.h"
|
||||||
|
#include "base/IRdReactive.h"
|
||||||
|
#include "guards.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4250)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
// region predeclared
|
||||||
|
|
||||||
|
class IWire;
|
||||||
|
|
||||||
|
class IProtocol;
|
||||||
|
|
||||||
|
class Serializers;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
class RD_FRAMEWORK_API RdReactiveBase : public RdBindableBase, public IRdReactive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
RdReactiveBase() = default;
|
||||||
|
|
||||||
|
RdReactiveBase(RdReactiveBase&& other);
|
||||||
|
|
||||||
|
RdReactiveBase& operator=(RdReactiveBase&& other);
|
||||||
|
|
||||||
|
virtual ~RdReactiveBase() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
const IWire* get_wire() const;
|
||||||
|
|
||||||
|
mutable bool is_local_change = false;
|
||||||
|
|
||||||
|
// delegated
|
||||||
|
|
||||||
|
const Serializers& get_serializers() const;
|
||||||
|
|
||||||
|
IScheduler* get_default_scheduler() const;
|
||||||
|
|
||||||
|
IScheduler* get_wire_scheduler() const override;
|
||||||
|
|
||||||
|
void assert_threading() const;
|
||||||
|
|
||||||
|
void assert_bound() const;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
auto local_change(F&& action) const -> typename util::result_of_t<F()>
|
||||||
|
{
|
||||||
|
if (is_bound() && !async)
|
||||||
|
{
|
||||||
|
assert_threading();
|
||||||
|
}
|
||||||
|
|
||||||
|
RD_ASSERT_MSG(!is_local_change, "!isLocalChange for RdReactiveBase with id:" + to_string(rdid));
|
||||||
|
|
||||||
|
util::bool_guard bool_guard(is_local_change);
|
||||||
|
return action();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // RD_CPP_RDREACTIVEBASE_H
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#include "WireBase.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
void WireBase::advise(Lifetime lifetime, const RdReactiveBase* entity) const
|
||||||
|
{
|
||||||
|
message_broker.advise_on(lifetime, entity);
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef RD_CPP_WIREBASE_H
|
||||||
|
#define RD_CPP_WIREBASE_H
|
||||||
|
|
||||||
|
#include "reactive/Property.h"
|
||||||
|
#include "base/IWire.h"
|
||||||
|
#include "protocol/MessageBroker.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RD_FRAMEWORK_API WireBase : public IWire
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
IScheduler* scheduler = nullptr;
|
||||||
|
|
||||||
|
MessageBroker message_broker;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
explicit WireBase(IScheduler* scheduler) : scheduler(scheduler), message_broker(scheduler)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~WireBase() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
virtual void advise(Lifetime lifetime, RdReactiveBase const* entity) const override;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_WIREBASE_H
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#include "ExtWire.h"
|
||||||
|
|
||||||
|
#include "protocol/Buffer.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
ExtWire::ExtWire()
|
||||||
|
{
|
||||||
|
connected.advise(Lifetime::Eternal(), [this](bool b) {
|
||||||
|
if (b)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<decltype(lock)> guard(lock);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (sendQ.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// auto[id, payload] = std::move(sendQ.front());
|
||||||
|
auto it = std::move(sendQ.front());
|
||||||
|
sendQ.pop();
|
||||||
|
realWire->send(
|
||||||
|
it.first, [payload = std::move(it.second)](Buffer& buffer) { buffer.write_byte_array_raw(payload); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtWire::advise(Lifetime lifetime, RdReactiveBase const* entity) const
|
||||||
|
{
|
||||||
|
realWire->advise(lifetime, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtWire::send(RdId const& id, std::function<void(Buffer& buffer)> writer) const
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<decltype(lock)> guard(lock);
|
||||||
|
if (!sendQ.empty() || !connected.get())
|
||||||
|
{
|
||||||
|
Buffer buffer;
|
||||||
|
writer(buffer);
|
||||||
|
sendQ.emplace(id, buffer.getRealArray());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
realWire->send(id, std::move(writer));
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef RD_CPP_EXTWIRE_H
|
||||||
|
#define RD_CPP_EXTWIRE_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "base/IWire.h"
|
||||||
|
#include "protocol/RdId.h"
|
||||||
|
#include "protocol/Buffer.h"
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RD_FRAMEWORK_API ExtWire final : public IWire
|
||||||
|
{
|
||||||
|
mutable std::mutex lock;
|
||||||
|
|
||||||
|
mutable std::queue<std::pair<RdId, Buffer::ByteArray> > sendQ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ExtWire();
|
||||||
|
|
||||||
|
mutable IWire const* realWire = nullptr;
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, RdReactiveBase const* entity) const override;
|
||||||
|
|
||||||
|
void send(RdId const& id, std::function<void(Buffer& buffer)> writer) const override;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_EXTWIRE_H
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
#include "RdExtBase.h"
|
||||||
|
|
||||||
|
#include "lifetime/Lifetime.h"
|
||||||
|
#include "base/RdPropertyBase.h"
|
||||||
|
#include "protocol/Protocol.h"
|
||||||
|
#include "scheduler/SynchronousScheduler.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
const IProtocol* RdExtBase::get_protocol() const
|
||||||
|
{
|
||||||
|
return extProtocol ? extProtocol.get() : RdReactiveBase::get_protocol();
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be overriden if derived ext have bindable members
|
||||||
|
void RdExtBase::init(Lifetime lifetime) const
|
||||||
|
{
|
||||||
|
// Protocol.initializationLogger.traceMe { "binding" }
|
||||||
|
|
||||||
|
auto parentProtocol = RdReactiveBase::get_protocol();
|
||||||
|
std::shared_ptr<IWire> parentWire = parentProtocol->wire;
|
||||||
|
|
||||||
|
// serializersOwner.registry(parentProtocol.serializers);
|
||||||
|
|
||||||
|
IScheduler* sc = parentProtocol->get_scheduler();
|
||||||
|
extWire->realWire = parentWire.get();
|
||||||
|
lifetime->bracket(
|
||||||
|
[&] {
|
||||||
|
extProtocol =
|
||||||
|
std::make_shared<Protocol>(parentProtocol->identity, sc, std::static_pointer_cast<IWire>(extWire), lifetime);
|
||||||
|
},
|
||||||
|
[this] { extProtocol = nullptr; });
|
||||||
|
|
||||||
|
parentWire->advise(lifetime, this);
|
||||||
|
|
||||||
|
// it's critical to advise before 'Ready' is sent because we advise on SynchronousScheduler
|
||||||
|
|
||||||
|
lifetime->bracket([this, parentWire] { sendState(*parentWire, ExtState::Ready); },
|
||||||
|
[this, parentWire] { sendState(*parentWire, ExtState::Disconnected); });
|
||||||
|
|
||||||
|
for (auto const& it : bindable_extensions)
|
||||||
|
{
|
||||||
|
bindPolymorphic(*(it.second), lifetime, this, it.first);
|
||||||
|
}
|
||||||
|
traceMe(Protocol::initializationLogger, "created and bound");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RdExtBase::on_wire_received(Buffer buffer) const
|
||||||
|
{
|
||||||
|
ExtState remoteState = buffer.read_enum<ExtState>();
|
||||||
|
traceMe(spdlog::get("logReceived"), "remote: " + to_string(remoteState));
|
||||||
|
|
||||||
|
switch (remoteState)
|
||||||
|
{
|
||||||
|
case ExtState::Ready:
|
||||||
|
{
|
||||||
|
sendState(*extWire->realWire, ExtState::ReceivedCounterpart);
|
||||||
|
extWire->connected.set(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ExtState::ReceivedCounterpart:
|
||||||
|
{
|
||||||
|
extWire->connected.set(true); // don't set anything if already set
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ExtState::Disconnected:
|
||||||
|
{
|
||||||
|
extWire->connected.set(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t counterpartSerializationHash = buffer.read_integral<int64_t>();
|
||||||
|
if (serializationHash != counterpartSerializationHash)
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(false, "serializationHash of ext " + to_string(location) +
|
||||||
|
" doesn't match to counterpart: maybe you forgot to generate models?\n"
|
||||||
|
"our: " +
|
||||||
|
to_string(serializationHash) + ", counterpart:" + to_string(counterpartSerializationHash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RdExtBase::sendState(IWire const& wire, ExtState state) const
|
||||||
|
{
|
||||||
|
wire.send(rdid, [&](Buffer& buffer) {
|
||||||
|
buffer.write_enum<ExtState>(state);
|
||||||
|
buffer.write_integral<int64_t>(serializationHash);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void RdExtBase::traceMe(std::shared_ptr<spdlog::logger> logger, string_view message) const
|
||||||
|
{
|
||||||
|
logger->trace("ext {} {}:: {}", to_string(location), to_string(rdid), std::string(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
IScheduler* RdExtBase::get_wire_scheduler() const
|
||||||
|
{
|
||||||
|
return &SynchronousScheduler::Instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string(RdExtBase::ExtState state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case RdExtBase::ExtState::Ready:
|
||||||
|
return "Ready";
|
||||||
|
case RdExtBase::ExtState::ReceivedCounterpart:
|
||||||
|
return "ReceivedCounterpart";
|
||||||
|
case RdExtBase::ExtState::Disconnected:
|
||||||
|
return "Disconnected";
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#ifndef RD_CPP_RDEXTBASE_H
|
||||||
|
#define RD_CPP_RDEXTBASE_H
|
||||||
|
|
||||||
|
#include "base/RdReactiveBase.h"
|
||||||
|
#include "ExtWire.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4250)
|
||||||
|
#pragma warning(disable : 4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Base class for creating extension node according to bottom-up design.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API RdExtBase : public RdReactiveBase
|
||||||
|
{
|
||||||
|
std::shared_ptr<ExtWire> extWire = std::make_shared<ExtWire>();
|
||||||
|
mutable std::shared_ptr<IProtocol> extProtocol /* = nullptr*/;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class ExtState
|
||||||
|
{
|
||||||
|
Ready,
|
||||||
|
ReceivedCounterpart,
|
||||||
|
Disconnected
|
||||||
|
};
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
RdExtBase() = default;
|
||||||
|
|
||||||
|
RdExtBase(RdExtBase&&) = default;
|
||||||
|
|
||||||
|
RdExtBase& operator=(RdExtBase&&) = default;
|
||||||
|
|
||||||
|
virtual ~RdExtBase() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
mutable int64_t serializationHash = 0;
|
||||||
|
|
||||||
|
const IProtocol* get_protocol() const override;
|
||||||
|
|
||||||
|
IScheduler* get_wire_scheduler() const override;
|
||||||
|
|
||||||
|
void init(Lifetime lifetime) const override;
|
||||||
|
|
||||||
|
void on_wire_received(Buffer buffer) const override;
|
||||||
|
|
||||||
|
void sendState(IWire const& wire, ExtState state) const;
|
||||||
|
|
||||||
|
void traceMe(std::shared_ptr<spdlog::logger> logger, string_view message) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string to_string(RdExtBase::ExtState state);
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_RDEXTBASE_H
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
#include "RName.h"
|
||||||
|
|
||||||
|
#include "thirdparty.hpp"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RNameImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
RNameImpl(RName parent, string_view localName, string_view separator);
|
||||||
|
|
||||||
|
RNameImpl(const RName& other) = delete;
|
||||||
|
RNameImpl(RName&& other) noexcept = delete;
|
||||||
|
RNameImpl& operator=(const RNameImpl& other) = delete;
|
||||||
|
RNameImpl& operator=(RNameImpl&& other) noexcept = delete;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
friend std::string to_string(RNameImpl const& value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
RName parent;
|
||||||
|
std::string local_name, separator;
|
||||||
|
};
|
||||||
|
|
||||||
|
RNameImpl::RNameImpl(RName parent, string_view localName, string_view separator)
|
||||||
|
: parent(parent), local_name(localName), separator(separator)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RName::RName(RName parent, string_view localName, string_view separator)
|
||||||
|
: impl(std::make_shared<RNameImpl>(std::move(parent), localName, separator))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RName RName::sub(string_view localName, string_view separator) const
|
||||||
|
{
|
||||||
|
return RName(*this, localName, separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string(RName const& value)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
if (value.impl)
|
||||||
|
{
|
||||||
|
res = to_string(*value.impl);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string(RNameImpl const& value)
|
||||||
|
{
|
||||||
|
if (value.parent)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
res = to_string(value.parent);
|
||||||
|
res += value.separator;
|
||||||
|
res += value.local_name;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return value.local_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
RName::RName(string_view local_name) : RName(RName(), local_name, "")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef RD_CPP_FRAMEWORK_RNAME_H
|
||||||
|
#define RD_CPP_FRAMEWORK_RNAME_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "thirdparty.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RNameImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Recursive name. For constructs like Aaaa.Bbb::CCC
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API RName
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
RName() = default;
|
||||||
|
|
||||||
|
RName(const RName& other) = default;
|
||||||
|
|
||||||
|
RName(RName&& other) noexcept = default;
|
||||||
|
|
||||||
|
RName& operator=(const RName& other) = default;
|
||||||
|
|
||||||
|
RName& operator=(RName&& other) noexcept = default;
|
||||||
|
|
||||||
|
RName(RName parent, string_view localName, string_view separator);
|
||||||
|
|
||||||
|
explicit RName(string_view local_name);
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
RName sub(string_view localName, string_view separator) const;
|
||||||
|
|
||||||
|
explicit operator bool() const
|
||||||
|
{
|
||||||
|
return impl != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::string RD_FRAMEWORK_API to_string(RName const& value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<RNameImpl> impl;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_FRAMEWORK_RNAME_H
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
#ifndef RD_CPP_RDLIST_H
|
||||||
|
#define RD_CPP_RDLIST_H
|
||||||
|
|
||||||
|
#include "reactive/ViewableList.h"
|
||||||
|
#include "base/RdReactiveBase.h"
|
||||||
|
#include "serialization/Polymorphic.h"
|
||||||
|
#include "std/allocator.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4250)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Reactive list for connection through wire.
|
||||||
|
*
|
||||||
|
* \tparam T type of stored values
|
||||||
|
* \tparam S "SerDes" for values
|
||||||
|
* \tparam A allocator for values
|
||||||
|
*/
|
||||||
|
template <typename T, typename S = Polymorphic<T>, typename A = allocator<T>>
|
||||||
|
class RdList final : public RdReactiveBase, public ViewableList<T, A>, public ISerializable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using WT = typename IViewableList<T>::WT;
|
||||||
|
|
||||||
|
// mutable ViewableList<T> list;
|
||||||
|
using list = ViewableList<T>;
|
||||||
|
mutable int64_t next_version = 1;
|
||||||
|
|
||||||
|
std::string logmsg(Op op, int64_t version, int32_t key, T const* value = nullptr) const
|
||||||
|
{
|
||||||
|
return "list " + to_string(location) + " " + to_string(rdid) + ":: " + to_string(op) + ":: key = " + std::to_string(key) +
|
||||||
|
((version > 0) ? " :: version = " + std::to_string(version) : "") +
|
||||||
|
" :: value = " + (value ? to_string(*value) : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Event = typename IViewableList<T>::Event;
|
||||||
|
|
||||||
|
using value_t = T;
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
RdList() = default;
|
||||||
|
|
||||||
|
RdList(RdList&&) = default;
|
||||||
|
|
||||||
|
RdList& operator=(RdList&&) = default;
|
||||||
|
|
||||||
|
virtual ~RdList() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
static RdList<T, S> read(SerializationCtx& /*ctx*/, Buffer& buffer)
|
||||||
|
{
|
||||||
|
RdList<T, S> result;
|
||||||
|
int64_t next_version = buffer.read_integral<int64_t>();
|
||||||
|
RdId id = RdId::read(buffer);
|
||||||
|
|
||||||
|
result.next_version = next_version;
|
||||||
|
withId(result, std::move(id));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(SerializationCtx& /*ctx*/, Buffer& buffer) const override
|
||||||
|
{
|
||||||
|
buffer.write_integral<int64_t>(next_version);
|
||||||
|
rdid.write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int32_t versionedFlagShift = 2; // update when changing Op
|
||||||
|
|
||||||
|
bool optimize_nested = false;
|
||||||
|
|
||||||
|
void init(Lifetime lifetime) const override
|
||||||
|
{
|
||||||
|
RdBindableBase::init(lifetime);
|
||||||
|
|
||||||
|
local_change([this, lifetime] {
|
||||||
|
advise(lifetime, [this, lifetime](typename IViewableList<T>::Event e) {
|
||||||
|
if (!is_local_change)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!optimize_nested)
|
||||||
|
{
|
||||||
|
T const* new_value = e.get_new_value();
|
||||||
|
if (new_value)
|
||||||
|
{
|
||||||
|
const IProtocol* iProtocol = get_protocol();
|
||||||
|
const Identities* identity = iProtocol->get_identity();
|
||||||
|
identifyPolymorphic(*new_value, *identity, identity->next(rdid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_wire()->send(rdid, [this, e](Buffer& buffer) {
|
||||||
|
Op op = static_cast<Op>(e.v.index());
|
||||||
|
|
||||||
|
buffer.write_integral<int64_t>(static_cast<int64_t>(op) | (next_version++ << versionedFlagShift));
|
||||||
|
buffer.write_integral<int32_t>(static_cast<const int32_t>(e.get_index()));
|
||||||
|
|
||||||
|
T const* new_value = e.get_new_value();
|
||||||
|
if (new_value)
|
||||||
|
{
|
||||||
|
S::write(this->get_serialization_context(), buffer, *new_value);
|
||||||
|
}
|
||||||
|
spdlog::get("logSend")->trace(logmsg(op, next_version - 1, e.get_index(), new_value));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
get_wire()->advise(lifetime, this);
|
||||||
|
|
||||||
|
if (!optimize_nested)
|
||||||
|
{
|
||||||
|
this->view(lifetime, [this](Lifetime lf, size_t index, T const& value) {
|
||||||
|
bindPolymorphic(value, lf, this, "[" + std::to_string(index) + "]");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_wire_received(Buffer buffer) const override
|
||||||
|
{
|
||||||
|
int64_t header = (buffer.read_integral<int64_t>());
|
||||||
|
int64_t version = header >> versionedFlagShift;
|
||||||
|
Op op = static_cast<Op>((header & ((1 << versionedFlagShift) - 1L)));
|
||||||
|
int32_t index = (buffer.read_integral<int32_t>());
|
||||||
|
|
||||||
|
RD_ASSERT_MSG(version == next_version,
|
||||||
|
("Version conflict for " + to_string(location) + "}. Expected version " + std::to_string(next_version) + ", received " +
|
||||||
|
std::to_string(version) + ". Are you modifying a list from two sides?"));
|
||||||
|
|
||||||
|
next_version++;
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case Op::ADD:
|
||||||
|
{
|
||||||
|
auto value = S::read(this->get_serialization_context(), buffer);
|
||||||
|
|
||||||
|
spdlog::get("logReceived")->trace(logmsg(op, version, index, &(wrapper::get<T>(value))));
|
||||||
|
|
||||||
|
(index < 0) ? list::add(std::move(value)) : list::add(static_cast<size_t>(index), std::move(value));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Op::UPDATE:
|
||||||
|
{
|
||||||
|
auto value = S::read(this->get_serialization_context(), buffer);
|
||||||
|
|
||||||
|
spdlog::get("logReceived")->trace(logmsg(op, version, index, &(wrapper::get<T>(value))));
|
||||||
|
|
||||||
|
list::set(static_cast<size_t>(index), std::move(value));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Op::REMOVE:
|
||||||
|
{
|
||||||
|
spdlog::get("logReceived")->trace(logmsg(op, version, index));
|
||||||
|
|
||||||
|
list::removeAt(static_cast<size_t>(index));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Op::ACK:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, std::function<void(Event const&)> handler) const override
|
||||||
|
{
|
||||||
|
if (is_bound())
|
||||||
|
{
|
||||||
|
assert_threading();
|
||||||
|
}
|
||||||
|
list::advise(lifetime, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool add(WT element) const override
|
||||||
|
{
|
||||||
|
return local_change([this, element = std::move(element)]() mutable { return list::add(std::move(element)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool add(size_t index, WT element) const override
|
||||||
|
{
|
||||||
|
return local_change([this, index, element = std::move(element)]() mutable { return list::add(index, std::move(element)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remove(T const& element) const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return list::remove(element); });
|
||||||
|
}
|
||||||
|
|
||||||
|
WT removeAt(size_t index) const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return list::removeAt(index); });
|
||||||
|
}
|
||||||
|
|
||||||
|
T const& get(size_t index) const override
|
||||||
|
{
|
||||||
|
return list::get(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
WT set(size_t index, WT element) const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return list::set(index, std::move(element)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() const override
|
||||||
|
{
|
||||||
|
return local_change([&] { list::clear(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const override
|
||||||
|
{
|
||||||
|
return list::size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const override
|
||||||
|
{
|
||||||
|
return list::empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Wrapper<T>> const& getList() const override
|
||||||
|
{
|
||||||
|
return list::getList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addAll(size_t index, std::vector<WT> elements) const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return list::addAll(index, std::move(elements)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addAll(std::vector<WT> elements) const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return list::addAll(std::move(elements)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeAll(std::vector<WT> elements) const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return list::removeAll(std::move(elements)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::string to_string(RdList const& value)
|
||||||
|
{
|
||||||
|
std::string res = "[";
|
||||||
|
for (auto const& p : value)
|
||||||
|
{
|
||||||
|
res += to_string(p) + ",";
|
||||||
|
}
|
||||||
|
return res + "]";
|
||||||
|
}
|
||||||
|
// region iterators
|
||||||
|
|
||||||
|
using iterator = typename ViewableList<T>::iterator;
|
||||||
|
|
||||||
|
using reverse_iterator = typename ViewableList<T>::reverse_iterator;
|
||||||
|
// endregion
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static_assert(std::is_move_constructible<rd::RdList<int>>::value, "Is move constructible RdList<int>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_RDLIST_H
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
#ifndef RD_CPP_RDMAP_H
|
||||||
|
#define RD_CPP_RDMAP_H
|
||||||
|
|
||||||
|
#include "reactive/ViewableMap.h"
|
||||||
|
#include "base/RdReactiveBase.h"
|
||||||
|
#include "serialization/Polymorphic.h"
|
||||||
|
#include "util/shared_function.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4250)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Reactive map for connection through wire.
|
||||||
|
*
|
||||||
|
* \tparam K type of stored keys
|
||||||
|
* \tparam V type of stored values
|
||||||
|
* \tparam KS "SerDes" for keys
|
||||||
|
* \tparam VS "SerDes" for values
|
||||||
|
* \tparam KA allocator for keys
|
||||||
|
* \tparam VA allocator for values
|
||||||
|
*/
|
||||||
|
template <typename K, typename V, typename KS = Polymorphic<K>, typename VS = Polymorphic<V>, typename KA = std::allocator<K>,
|
||||||
|
typename VA = std::allocator<V>>
|
||||||
|
class RdMap final : public RdReactiveBase, public ViewableMap<K, V, KA, VA>, public ISerializable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using WK = typename IViewableMap<K, V>::WK;
|
||||||
|
using WV = typename IViewableMap<K, V>::WV;
|
||||||
|
using OV = typename IViewableMap<K, V>::OV;
|
||||||
|
|
||||||
|
using map = ViewableMap<K, V>;
|
||||||
|
mutable int64_t next_version = 0;
|
||||||
|
mutable ordered_map<K const*, int64_t, wrapper::TransparentHash<K>, wrapper::TransparentKeyEqual<K>> pendingForAck;
|
||||||
|
|
||||||
|
std::string logmsg(Op op, int64_t version, K const* key, V const* value = nullptr) const
|
||||||
|
{
|
||||||
|
return "map " + to_string(location) + " " + to_string(rdid) + ":: " + to_string(op) + ":: key = " + to_string(*key) +
|
||||||
|
((version > 0) ? " :: version = " + std::to_string(version) : "") +
|
||||||
|
" :: value = " + (value ? to_string(*value) : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string logmsg(Op op, int64_t version, K const* key, optional<WV> const& value) const
|
||||||
|
{
|
||||||
|
return logmsg(op, version, key, value ? &(wrapper::get(*value)) : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool is_master = false;
|
||||||
|
|
||||||
|
bool optimize_nested = false;
|
||||||
|
|
||||||
|
using Event = typename IViewableMap<K, V>::Event;
|
||||||
|
|
||||||
|
using key_type = K;
|
||||||
|
using value_type = V;
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
RdMap() = default;
|
||||||
|
|
||||||
|
RdMap(RdMap&&) = default;
|
||||||
|
|
||||||
|
RdMap& operator=(RdMap&&) = default;
|
||||||
|
|
||||||
|
virtual ~RdMap() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
static RdMap<K, V, KS, VS> read(SerializationCtx& /*ctx*/, Buffer& buffer)
|
||||||
|
{
|
||||||
|
RdMap<K, V, KS, VS> res;
|
||||||
|
RdId id = RdId::read(buffer);
|
||||||
|
withId(res, id);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(SerializationCtx& /*ctx*/, Buffer& buffer) const override
|
||||||
|
{
|
||||||
|
rdid.write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int32_t versionedFlagShift = 8;
|
||||||
|
|
||||||
|
void init(Lifetime lifetime) const override
|
||||||
|
{
|
||||||
|
RdBindableBase::init(lifetime);
|
||||||
|
|
||||||
|
local_change([this, lifetime]() {
|
||||||
|
advise(lifetime, [this, lifetime](Event e) {
|
||||||
|
if (!is_local_change)
|
||||||
|
return;
|
||||||
|
|
||||||
|
V const* new_value = e.get_new_value();
|
||||||
|
if (new_value)
|
||||||
|
{
|
||||||
|
const IProtocol* iProtocol = get_protocol();
|
||||||
|
const Identities* identity = iProtocol->get_identity();
|
||||||
|
identifyPolymorphic(*new_value, *identity, identity->next(rdid));
|
||||||
|
}
|
||||||
|
|
||||||
|
get_wire()->send(rdid, [this, e](Buffer& buffer) {
|
||||||
|
int32_t versionedFlag = ((is_master ? 1 : 0)) << versionedFlagShift;
|
||||||
|
Op op = static_cast<Op>(e.v.index());
|
||||||
|
|
||||||
|
buffer.write_integral<int32_t>(static_cast<int32_t>(op) | versionedFlag);
|
||||||
|
|
||||||
|
int64_t version = is_master ? ++next_version : 0L;
|
||||||
|
|
||||||
|
if (is_master)
|
||||||
|
{
|
||||||
|
pendingForAck.emplace(e.get_key(), version);
|
||||||
|
buffer.write_integral(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
KS::write(this->get_serialization_context(), buffer, *e.get_key());
|
||||||
|
|
||||||
|
V const* new_value = e.get_new_value();
|
||||||
|
if (new_value)
|
||||||
|
{
|
||||||
|
VS::write(this->get_serialization_context(), buffer, *new_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::get("logSend")->trace("SEND{}", logmsg(op, next_version - 1, e.get_key(), new_value));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
get_wire()->advise(lifetime, this);
|
||||||
|
|
||||||
|
if (!optimize_nested)
|
||||||
|
this->view(lifetime, [this](Lifetime lf, std::pair<K const*, V const*> entry) {
|
||||||
|
bindPolymorphic(entry.second, lf, this, "[" + to_string(*entry.first) + "]");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_wire_received(Buffer buffer) const override
|
||||||
|
{
|
||||||
|
int32_t header = buffer.read_integral<int32_t>();
|
||||||
|
bool msg_versioned = (header >> versionedFlagShift) != 0;
|
||||||
|
Op op = static_cast<Op>(header & ((1 << versionedFlagShift) - 1));
|
||||||
|
|
||||||
|
int64_t version = msg_versioned ? buffer.read_integral<int64_t>() : 0;
|
||||||
|
|
||||||
|
WK key = KS::read(this->get_serialization_context(), buffer);
|
||||||
|
|
||||||
|
if (op == Op::ACK)
|
||||||
|
{
|
||||||
|
std::string errmsg;
|
||||||
|
if (!msg_versioned)
|
||||||
|
{
|
||||||
|
errmsg = "Received " + to_string(Op::ACK) + " while msg hasn't versioned flag set";
|
||||||
|
}
|
||||||
|
else if (!is_master)
|
||||||
|
{
|
||||||
|
errmsg = "Received " + to_string(Op::ACK) + " when not a Master";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pendingForAck.count(key) > 0)
|
||||||
|
{
|
||||||
|
int64_t pendingVersion = pendingForAck.at(key);
|
||||||
|
if (pendingVersion < version)
|
||||||
|
{
|
||||||
|
errmsg = "Pending version " + std::to_string(pendingVersion) + " < " + to_string(Op::ACK) + " version `" +
|
||||||
|
std::to_string(version);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// side effect
|
||||||
|
if (pendingVersion == version)
|
||||||
|
{
|
||||||
|
pendingForAck.erase(key); // else we don't need to remove, silently drop
|
||||||
|
}
|
||||||
|
// return good result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errmsg = "No pending for " + to_string(Op::ACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errmsg.empty())
|
||||||
|
{
|
||||||
|
spdlog::get("logReceived")->trace(logmsg(Op::ACK, version, &(wrapper::get<K>(key))));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spdlog::get("logReceived")->error(logmsg(Op::ACK, version, &(wrapper::get<K>(key))) + " >> " + errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Buffer serialized_key;
|
||||||
|
KS::write(this->get_serialization_context(), serialized_key, wrapper::get<K>(key));
|
||||||
|
|
||||||
|
bool is_put = (op == Op::ADD || op == Op::UPDATE);
|
||||||
|
optional<WV> value;
|
||||||
|
if (is_put)
|
||||||
|
{
|
||||||
|
value = VS::read(this->get_serialization_context(), buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg_versioned || !is_master || pendingForAck.count(key) == 0)
|
||||||
|
{
|
||||||
|
spdlog::get("logReceived")->trace("RECV{}", logmsg(op, version, &(wrapper::get<K>(key)), value));
|
||||||
|
if (value.has_value())
|
||||||
|
{
|
||||||
|
map::set(std::move(key), *std::move(value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
map::remove(wrapper::get<K>(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spdlog::get("logReceived")->trace("{} >> REJECTED", logmsg(op, version, &(wrapper::get<K>(key)), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg_versioned)
|
||||||
|
{
|
||||||
|
auto writer =
|
||||||
|
util::make_shared_function([version, serialized_key = std::move(serialized_key)](Buffer& innerBuffer) mutable {
|
||||||
|
innerBuffer.write_integral<int32_t>((1u << versionedFlagShift) | static_cast<int32_t>(Op::ACK));
|
||||||
|
innerBuffer.write_integral<int64_t>(version);
|
||||||
|
// KS::write(this->get_serialization_context(), innerBuffer, wrapper::get<K>(key));
|
||||||
|
innerBuffer.write_byte_array_raw(serialized_key.getArray());
|
||||||
|
// logSend.trace(logmsg(Op::ACK, version, serialized_key));
|
||||||
|
});
|
||||||
|
get_wire()->send(rdid, std::move(writer));
|
||||||
|
if (is_master)
|
||||||
|
{
|
||||||
|
spdlog::get("logReceived")->error("Both ends are masters: {}", to_string(location));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, std::function<void(Event const&)> handler) const override
|
||||||
|
{
|
||||||
|
if (is_bound())
|
||||||
|
{
|
||||||
|
assert_threading();
|
||||||
|
}
|
||||||
|
map::advise(lifetime, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
V const* get(K const& key) const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return map::get(key); });
|
||||||
|
}
|
||||||
|
|
||||||
|
V const* set(WK key, WV value) const override
|
||||||
|
{
|
||||||
|
return local_change([&]() mutable { return map::set(std::move(key), std::move(value)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
OV remove(K const& key) const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return map::remove(key); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return map::clear(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const override
|
||||||
|
{
|
||||||
|
return map::size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const override
|
||||||
|
{
|
||||||
|
return map::empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::string to_string(RdMap const& value)
|
||||||
|
{
|
||||||
|
std::string res = "[";
|
||||||
|
for (auto it = value.begin(); it != value.end(); ++it)
|
||||||
|
{
|
||||||
|
res += to_string(it.key()) + "=>" + to_string(it.value()) + ",";
|
||||||
|
}
|
||||||
|
return res + "]";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static_assert(std::is_move_constructible<rd::RdMap<int, int>>::value, "Is move constructible RdMap<int, int>");
|
||||||
|
static_assert(std::is_move_assignable<rd::RdMap<int, int>>::value, "Is move constructible RdMap<int, int>");
|
||||||
|
static_assert(std::is_default_constructible<rd::RdMap<int, int>>::value, "Is default constructible RdMap<int, int>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_RDMAP_H
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
#ifndef RD_CPP_RDPROPERTY_H
|
||||||
|
#define RD_CPP_RDPROPERTY_H
|
||||||
|
|
||||||
|
#include "base/RdPropertyBase.h"
|
||||||
|
#include "serialization/Polymorphic.h"
|
||||||
|
#include "serialization/ISerializable.h"
|
||||||
|
#include "std/allocator.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4250)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Reactive property for connection through wire.
|
||||||
|
|
||||||
|
* \tparam T type of stored value
|
||||||
|
* \tparam S "SerDes" for value
|
||||||
|
*/
|
||||||
|
template <typename T, typename S = Polymorphic<T>, typename A = allocator<T>>
|
||||||
|
class RdProperty final : public RdPropertyBase<T, S>, public ISerializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
RdProperty() = default;
|
||||||
|
|
||||||
|
RdProperty(RdProperty const&) = delete;
|
||||||
|
|
||||||
|
RdProperty& operator=(RdProperty const&) = delete;
|
||||||
|
|
||||||
|
RdProperty(RdProperty&&) = default;
|
||||||
|
|
||||||
|
RdProperty& operator=(RdProperty&&) = default;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
explicit RdProperty(F&& value) : RdPropertyBase<T, S>(std::forward<F>(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~RdProperty() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
static RdProperty<T, S> read(SerializationCtx& ctx, Buffer& buffer)
|
||||||
|
{
|
||||||
|
RdId id = RdId::read(buffer);
|
||||||
|
bool not_null = buffer.read_bool(); // not null/
|
||||||
|
(void) not_null;
|
||||||
|
auto value = S::read(ctx, buffer);
|
||||||
|
RdProperty<T, S> property;
|
||||||
|
property.value = std::move(value);
|
||||||
|
withId(property, id);
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(SerializationCtx& ctx, Buffer& buffer) const override
|
||||||
|
{
|
||||||
|
this->rdid.write(buffer);
|
||||||
|
buffer.write_bool(true);
|
||||||
|
S::write(ctx, buffer, this->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, std::function<void(T const&)> handler) const override
|
||||||
|
{
|
||||||
|
RdPropertyBase<T, S>::advise(lifetime, std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
RdProperty<T, S>& slave()
|
||||||
|
{
|
||||||
|
this->is_master = false;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void identify(Identities const& identities, RdId const& id) const override
|
||||||
|
{
|
||||||
|
RdBindableBase::identify(identities, id);
|
||||||
|
if (!this->optimize_nested && this->has_value())
|
||||||
|
{
|
||||||
|
identifyPolymorphic(this->get(), identities, identities.next(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const RdProperty& lhs, const RdProperty& rhs)
|
||||||
|
{
|
||||||
|
return &lhs == &rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const RdProperty& lhs, const RdProperty& rhs)
|
||||||
|
{
|
||||||
|
return !(rhs == lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::string to_string(RdProperty const& value)
|
||||||
|
{
|
||||||
|
return to_string(static_cast<Property<T> const&>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static_assert(std::is_move_constructible<rd::RdProperty<int>>::value, "Is move constructible from RdProperty<int>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_RDPROPERTY_H
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
#ifndef RD_CPP_RDSET_H
|
||||||
|
#define RD_CPP_RDSET_H
|
||||||
|
|
||||||
|
#include "reactive/ViewableSet.h"
|
||||||
|
#include "base/RdReactiveBase.h"
|
||||||
|
#include "serialization/Polymorphic.h"
|
||||||
|
#include "std/allocator.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4250)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Reactive set for connection through wire.
|
||||||
|
*
|
||||||
|
* \tparam T type of stored values
|
||||||
|
* \tparam S "SerDes" for values
|
||||||
|
*/
|
||||||
|
template <typename T, typename S = Polymorphic<T>, typename A = allocator<T>>
|
||||||
|
class RdSet final : public RdReactiveBase, public ViewableSet<T, A>, public ISerializable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using WT = typename IViewableSet<T>::WT;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using set = ViewableSet<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Event = typename IViewableSet<T>::Event;
|
||||||
|
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
RdSet() = default;
|
||||||
|
|
||||||
|
RdSet(RdSet&&) = default;
|
||||||
|
|
||||||
|
RdSet& operator=(RdSet&&) = default;
|
||||||
|
|
||||||
|
virtual ~RdSet() = default;
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
static RdSet<T, S> read(SerializationCtx& /*ctx*/, Buffer& buffer)
|
||||||
|
{
|
||||||
|
RdSet<T, S> result;
|
||||||
|
RdId id = RdId::read(buffer);
|
||||||
|
withId(result, std::move(id));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(SerializationCtx& /*ctx*/, Buffer& buffer) const override
|
||||||
|
{
|
||||||
|
rdid.write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool optimize_nested = false;
|
||||||
|
|
||||||
|
void init(Lifetime lifetime) const override
|
||||||
|
{
|
||||||
|
RdBindableBase::init(lifetime);
|
||||||
|
|
||||||
|
local_change([this, lifetime] {
|
||||||
|
advise(lifetime, [this](AddRemove kind, T const& v) {
|
||||||
|
if (!is_local_change)
|
||||||
|
return;
|
||||||
|
|
||||||
|
get_wire()->send(rdid, [this, kind, &v](Buffer& buffer) {
|
||||||
|
buffer.write_enum<AddRemove>(kind);
|
||||||
|
S::write(this->get_serialization_context(), buffer, v);
|
||||||
|
|
||||||
|
spdlog::get("logSend")->trace("SENDset {} {}:: {}:: {}", to_string(location), to_string(rdid), to_string(kind), to_string(v));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
get_wire()->advise(lifetime, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_wire_received(Buffer buffer) const override
|
||||||
|
{
|
||||||
|
AddRemove kind = buffer.read_enum<AddRemove>();
|
||||||
|
auto value = S::read(this->get_serialization_context(), buffer);
|
||||||
|
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
case AddRemove::ADD:
|
||||||
|
{
|
||||||
|
set::add(std::move(value));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AddRemove::REMOVE:
|
||||||
|
{
|
||||||
|
set::remove(wrapper::get<T>(value));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool add(WT value) const override
|
||||||
|
{
|
||||||
|
return local_change([this, value = std::move(value)]() mutable { return set::add(std::move(value)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return set::clear(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remove(T const& value) const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return set::remove(value); });
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return set::size(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(T const& value) const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return set::contains(value); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const override
|
||||||
|
{
|
||||||
|
return local_change([&] { return set::empty(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, std::function<void(Event const&)> handler) const override
|
||||||
|
{
|
||||||
|
if (is_bound())
|
||||||
|
{
|
||||||
|
assert_threading();
|
||||||
|
}
|
||||||
|
set::advise(lifetime, std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addAll(std::vector<WT> elements) const override
|
||||||
|
{
|
||||||
|
return local_change([this, elements = std::move(elements)]() mutable { return set::addAll(elements); });
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::string to_string(RdSet const& value)
|
||||||
|
{
|
||||||
|
std::string res = "[";
|
||||||
|
for (auto const& p : value)
|
||||||
|
{
|
||||||
|
res += to_string(p) + ",";
|
||||||
|
}
|
||||||
|
return res + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
using IViewableSet<T>::advise;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static_assert(std::is_move_constructible<rd::RdSet<int>>::value, "Is move constructible RdSet<int>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_RDSET_H
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
#ifndef RD_CPP_RDSIGNAL_H
|
||||||
|
#define RD_CPP_RDSIGNAL_H
|
||||||
|
|
||||||
|
#include "lifetime/Lifetime.h"
|
||||||
|
#include "reactive/base/interfaces.h"
|
||||||
|
#include "scheduler/base/IScheduler.h"
|
||||||
|
#include "reactive/base/SignalX.h"
|
||||||
|
#include "base/RdReactiveBase.h"
|
||||||
|
#include "serialization/Polymorphic.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4250)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Reactive signal for connection through wire.
|
||||||
|
*
|
||||||
|
* \tparam T type of events
|
||||||
|
* \tparam S "SerDes" for events
|
||||||
|
*/
|
||||||
|
template <typename T, typename S = Polymorphic<T>>
|
||||||
|
class RdSignal final : public RdReactiveBase, public ISignal<T>, public ISerializable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using WT = typename ISignal<T>::WT;
|
||||||
|
|
||||||
|
std::string logmsg(T const& value) const
|
||||||
|
{
|
||||||
|
return "signal " + to_string(location) + " " + to_string(rdid) + ":: value = " + to_string(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable IScheduler* wire_scheduler{};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void set_wire_scheduler(IScheduler* scheduler) const
|
||||||
|
{
|
||||||
|
wire_scheduler = scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Signal<T> signal;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
RdSignal(RdSignal const&) = delete;
|
||||||
|
|
||||||
|
RdSignal& operator=(RdSignal const&) = delete;
|
||||||
|
|
||||||
|
RdSignal() = default;
|
||||||
|
|
||||||
|
RdSignal(RdSignal&&) = default;
|
||||||
|
|
||||||
|
RdSignal& operator=(RdSignal&&) = default;
|
||||||
|
|
||||||
|
virtual ~RdSignal() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
static RdSignal<T, S> read(SerializationCtx& /*ctx*/, Buffer& buffer)
|
||||||
|
{
|
||||||
|
RdSignal<T, S> res;
|
||||||
|
const RdId& id = RdId::read(buffer);
|
||||||
|
withId(res, id);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(SerializationCtx& /*ctx*/, Buffer& buffer) const override
|
||||||
|
{
|
||||||
|
rdid.write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(Lifetime lifetime) const override
|
||||||
|
{
|
||||||
|
RdReactiveBase::init(lifetime);
|
||||||
|
set_wire_scheduler(get_default_scheduler());
|
||||||
|
get_wire()->advise(lifetime, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_wire_received(Buffer buffer) const override
|
||||||
|
{
|
||||||
|
auto value = S::read(this->get_serialization_context(), buffer);
|
||||||
|
spdlog::get("logReceived")->trace("RECV{}", logmsg(wrapper::get<T>(value)));
|
||||||
|
|
||||||
|
signal.fire(wrapper::get<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
using ISignal<T>::fire;
|
||||||
|
|
||||||
|
void fire(T const& value) const override
|
||||||
|
{
|
||||||
|
// Disabling checking for bounding, to fix async signal being called unbound on another thread.
|
||||||
|
// Copying hack from rd-net
|
||||||
|
// https://github.com/JetBrains/rd/blob/d00d07c38784af5743c75ceca4bbceb92f6f8149/rd-net/RdFramework/Impl/RdSignal.cs#L72-L95
|
||||||
|
// assert_bound();
|
||||||
|
if (!async)
|
||||||
|
{
|
||||||
|
assert_threading();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (async && !is_bound()) return;
|
||||||
|
|
||||||
|
get_wire()->send(rdid, [this, &value](Buffer& buffer) {
|
||||||
|
spdlog::get("logSend")->trace("SEND{}", logmsg(value));
|
||||||
|
S::write(get_serialization_context(), buffer, value);
|
||||||
|
});
|
||||||
|
signal.fire(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
using ISource<T>::advise;
|
||||||
|
|
||||||
|
void advise(Lifetime lifetime, std::function<void(T const&)> handler) const override
|
||||||
|
{
|
||||||
|
if (is_bound())
|
||||||
|
{
|
||||||
|
assert_threading();
|
||||||
|
}
|
||||||
|
signal.advise(lifetime, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void advise_on(Lifetime lifetime, IScheduler* scheduler, F&& handler)
|
||||||
|
{
|
||||||
|
if (is_bound())
|
||||||
|
{
|
||||||
|
assert_threading();
|
||||||
|
}
|
||||||
|
set_wire_scheduler(scheduler);
|
||||||
|
signal.advise(lifetime, std::forward<F>(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
IScheduler* get_wire_scheduler() const override
|
||||||
|
{
|
||||||
|
return wire_scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::string to_string(RdSignal const&)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static_assert(std::is_move_constructible<rd::RdSignal<int>>::value, "Is not move constructible from RdSignal<int>");
|
||||||
|
|
||||||
|
#endif // RD_CPP_RDSIGNAL_H
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#include "InternRoot.h"
|
||||||
|
|
||||||
|
#include "serialization/AbstractPolymorphic.h"
|
||||||
|
#include "serialization/InternedAnySerializer.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
InternRoot::InternRoot()
|
||||||
|
{
|
||||||
|
async = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IScheduler* InternRoot::get_wire_scheduler() const
|
||||||
|
{
|
||||||
|
return &intern_scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternRoot::on_wire_received(Buffer buffer) const
|
||||||
|
{
|
||||||
|
optional<InternedAny> value = InternedAnySerializer::read(get_serialization_context(), buffer);
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int32_t remote_id = buffer.read_integral<int32_t>();
|
||||||
|
set_interned_correspondence(remote_id ^ 1, *std::move(value));
|
||||||
|
RD_ASSERT_MSG(((remote_id & 1) == 0), "Remote sent ID marked as our own, bug?");
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternRoot::bind(Lifetime lf, IRdDynamic const* parent, string_view name) const
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(!is_bound(), "Trying to bound already bound "s + to_string(this->location) + " to " + to_string(parent->get_location()))
|
||||||
|
|
||||||
|
lf->bracket(
|
||||||
|
[this, parent, &name] {
|
||||||
|
this->parent = parent;
|
||||||
|
location = parent->get_location().sub(name, ".");
|
||||||
|
},
|
||||||
|
[this] {
|
||||||
|
location = location.sub("<<unbound>>", "::");
|
||||||
|
this->parent = nullptr;
|
||||||
|
rdid = RdId::Null();
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
// if something's interned before bind
|
||||||
|
std::lock_guard<decltype(lock)> guard(lock);
|
||||||
|
my_items_lis.clear();
|
||||||
|
other_items_list.clear();
|
||||||
|
inverse_map.clear();
|
||||||
|
}
|
||||||
|
get_protocol()->get_wire()->advise(lf, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternRoot::identify(const Identities& /*identities*/, RdId const& id) const
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(rdid.isNull(), "Already has RdId: " + to_string(rdid) + ", entities: $this");
|
||||||
|
RD_ASSERT_MSG(!id.isNull(), "Assigned RdId mustn't be null, entities: $this");
|
||||||
|
|
||||||
|
rdid = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternRoot::set_interned_correspondence(int32_t id, InternedAny&& value) const
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(!is_index_owned(id), "Setting interned correspondence for object that we should have written, bug?")
|
||||||
|
|
||||||
|
std::lock_guard<decltype(lock)> guard(lock);
|
||||||
|
other_items_list[id / 2] = value;
|
||||||
|
inverse_map[value] = id;
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
#ifndef RD_CPP_INTERNROOT_H
|
||||||
|
#define RD_CPP_INTERNROOT_H
|
||||||
|
|
||||||
|
#include "base/RdReactiveBase.h"
|
||||||
|
#include "InternScheduler.h"
|
||||||
|
#include "lifetime/Lifetime.h"
|
||||||
|
#include "types/wrapper.h"
|
||||||
|
#include "serialization/RdAny.h"
|
||||||
|
#include "util/core_traits.h"
|
||||||
|
|
||||||
|
#include "tsl/ordered_map.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4250)
|
||||||
|
#pragma warning(disable : 4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
// region predeclared
|
||||||
|
|
||||||
|
class Identities;
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Node in graph for storing interned objects.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API InternRoot final : public RdReactiveBase
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// template<typename T>
|
||||||
|
mutable std::vector<InternedAny> my_items_lis;
|
||||||
|
|
||||||
|
// template<typename T>
|
||||||
|
mutable ordered_map<int32_t, InternedAny> other_items_list;
|
||||||
|
// template<typename T>
|
||||||
|
mutable ordered_map<InternedAny, int32_t, any::TransparentHash, any::TransparentKeyEqual> inverse_map;
|
||||||
|
|
||||||
|
mutable InternScheduler intern_scheduler;
|
||||||
|
|
||||||
|
mutable std::recursive_mutex lock;
|
||||||
|
|
||||||
|
void set_interned_correspondence(int32_t id, InternedAny&& value) const;
|
||||||
|
|
||||||
|
static constexpr bool is_index_owned(int32_t id);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
InternRoot();
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int32_t intern_value(Wrapper<T> value) const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Wrapper<T> un_intern_value(int32_t id) const;
|
||||||
|
|
||||||
|
IScheduler* get_wire_scheduler() const override;
|
||||||
|
|
||||||
|
void bind(Lifetime lf, IRdDynamic const* parent, string_view name) const override;
|
||||||
|
|
||||||
|
void identify(const Identities& identities, RdId const& id) const override;
|
||||||
|
|
||||||
|
void on_wire_received(Buffer buffer) const override;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#include "serialization/InternedAnySerializer.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/*template<typename T>
|
||||||
|
std::vector<value_or_wrapper<T>> InternRoot::myItemsList = {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ordered_map<int32_t, value_or_wrapper<T>> InternRoot::otherItemsList = {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ordered_map<value_or_wrapper<T>, int32_t> InternRoot::inverseMap = {};*/
|
||||||
|
|
||||||
|
constexpr bool InternRoot::is_index_owned(int32_t id)
|
||||||
|
{
|
||||||
|
return !static_cast<bool>(id & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Wrapper<T> InternRoot::un_intern_value(int32_t id) const
|
||||||
|
{
|
||||||
|
// don't need lock because value's already exists and never removes
|
||||||
|
return any::get<T>(is_index_owned(id) ? my_items_lis[id / 2] : other_items_list[id / 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int32_t InternRoot::intern_value(Wrapper<T> value) const
|
||||||
|
{
|
||||||
|
InternedAny any = any::make_interned_any<T>(value);
|
||||||
|
|
||||||
|
std::lock_guard<decltype(lock)> guard(lock);
|
||||||
|
|
||||||
|
auto it = inverse_map.find(any);
|
||||||
|
int32_t index = 0;
|
||||||
|
if (it == inverse_map.end())
|
||||||
|
{
|
||||||
|
get_protocol()->get_wire()->send(this->rdid, [this, &index, value, any](Buffer& buffer) {
|
||||||
|
InternedAnySerializer::write<T>(get_serialization_context(), buffer, wrapper::get<T>(value));
|
||||||
|
{
|
||||||
|
std::lock_guard<decltype(lock)> guard(lock);
|
||||||
|
index = static_cast<int32_t>(my_items_lis.size()) * 2;
|
||||||
|
my_items_lis.emplace_back(any);
|
||||||
|
}
|
||||||
|
buffer.write_integral<int32_t>(index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = it->second;
|
||||||
|
}
|
||||||
|
if (inverse_map.count(any) == 0)
|
||||||
|
{
|
||||||
|
inverse_map[any] = index;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_INTERNROOT_H
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#include "InternScheduler.h"
|
||||||
|
|
||||||
|
#include "guards.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
thread_local int32_t InternScheduler::active_counts = 0;
|
||||||
|
|
||||||
|
InternScheduler::InternScheduler()
|
||||||
|
{
|
||||||
|
out_of_order_execution = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternScheduler::queue(std::function<void()> action)
|
||||||
|
{
|
||||||
|
util::increment_guard<int32_t> guard(active_counts);
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InternScheduler::flush()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InternScheduler::is_active() const
|
||||||
|
{
|
||||||
|
return active_counts > 0;
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef RD_CPP_INTERNSCHEDULER_H
|
||||||
|
#define RD_CPP_INTERNSCHEDULER_H
|
||||||
|
|
||||||
|
#include "scheduler/base/IScheduler.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Scheduler for interning object. Maintains out of order execution.
|
||||||
|
*/
|
||||||
|
class InternScheduler : public IScheduler
|
||||||
|
{
|
||||||
|
static thread_local int32_t active_counts;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
InternScheduler();
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
void queue(std::function<void()> action) override;
|
||||||
|
|
||||||
|
void flush() override;
|
||||||
|
|
||||||
|
bool is_active() const override;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_INTERNSCHEDULER_H
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "protocol/Buffer.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
Buffer::Buffer() : Buffer(16)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::Buffer(size_t initialSize) : data_(initialSize)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::Buffer(ByteArray array, size_t offset) : data_(std::move(array)), offset(offset)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Buffer::get_position() const
|
||||||
|
{
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::set_position(size_t value)
|
||||||
|
{
|
||||||
|
offset = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::check_available(size_t moreSize) const
|
||||||
|
{
|
||||||
|
if (offset + moreSize > size())
|
||||||
|
{
|
||||||
|
throw std::out_of_range(
|
||||||
|
"Expected " + std::to_string(moreSize) + " bytes in buffer, only" + std::to_string(size() - offset) + "available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::read(word_t* dst, size_t size)
|
||||||
|
{
|
||||||
|
if (size == 0)
|
||||||
|
return;
|
||||||
|
check_available(size);
|
||||||
|
std::copy(&data_[offset], &data_[offset] + size, dst);
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::write(const word_t* src, size_t size)
|
||||||
|
{
|
||||||
|
if (size == 0)
|
||||||
|
return;
|
||||||
|
require_available(size);
|
||||||
|
std::copy(src, src + size, &data_[offset]);
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::require_available(size_t moreSize)
|
||||||
|
{
|
||||||
|
if (offset + moreSize >= size())
|
||||||
|
{
|
||||||
|
const size_t new_size = (std::max)(size() * 2, offset + moreSize);
|
||||||
|
data_.resize(new_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::rewind()
|
||||||
|
{
|
||||||
|
set_position(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::ByteArray Buffer::getArray() const&
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::ByteArray Buffer::getArray() &&
|
||||||
|
{
|
||||||
|
rewind();
|
||||||
|
return std::move(data_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::ByteArray Buffer::getRealArray() const&
|
||||||
|
{
|
||||||
|
auto res = getArray();
|
||||||
|
res.resize(offset);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::ByteArray Buffer::getRealArray() &&
|
||||||
|
{
|
||||||
|
auto res = std::move(data_);
|
||||||
|
res.resize(offset);
|
||||||
|
rewind();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::word_t const* Buffer::data() const
|
||||||
|
{
|
||||||
|
return data_.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::word_t* Buffer::data()
|
||||||
|
{
|
||||||
|
return data_.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::word_t const* Buffer::current_pointer() const
|
||||||
|
{
|
||||||
|
return data() + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::word_t* Buffer::current_pointer()
|
||||||
|
{
|
||||||
|
return data() + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Buffer::size() const
|
||||||
|
{
|
||||||
|
return data_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*std::string Buffer::readString() const {
|
||||||
|
auto v = readArray<uint8_t>();
|
||||||
|
return std::string(v.begin(), v.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::writeString(std::string const &value) const {
|
||||||
|
std::vector<uint8_t> v(value.begin(), value.end());
|
||||||
|
writeArray<uint8_t>(v);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
template <int>
|
||||||
|
std::wstring read_wstring_spec(Buffer& buffer)
|
||||||
|
{
|
||||||
|
auto v = buffer.read_array<std::vector, uint16_t>();
|
||||||
|
return std::wstring(v.begin(), v.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::wstring read_wstring_spec<2>(Buffer& buffer)
|
||||||
|
{
|
||||||
|
const int32_t len = buffer.read_integral<int32_t>();
|
||||||
|
RD_ASSERT_MSG(len >= 0, "read null string(length =" + std::to_string(len) + ")");
|
||||||
|
std::wstring result;
|
||||||
|
result.resize(len);
|
||||||
|
buffer.read(reinterpret_cast<Buffer::word_t*>(&result[0]), sizeof(wchar_t) * len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring Buffer::read_wstring()
|
||||||
|
{
|
||||||
|
return read_wstring_spec<sizeof(wchar_t)>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int>
|
||||||
|
void write_wstring_spec(Buffer& buffer, wstring_view value)
|
||||||
|
{
|
||||||
|
const std::vector<uint16_t> v(value.begin(), value.end());
|
||||||
|
buffer.write_array<std::vector, uint16_t>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void write_wstring_spec<2>(Buffer& buffer, wstring_view value)
|
||||||
|
{
|
||||||
|
buffer.write_integral<int32_t>(static_cast<int32_t>(value.size()));
|
||||||
|
buffer.write(reinterpret_cast<Buffer::word_t const*>(value.data()), sizeof(wchar_t) * value.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::write_wstring(std::wstring const& value)
|
||||||
|
{
|
||||||
|
write_wstring(wstring_view(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::write_char16_string(const uint16_t* data, size_t len)
|
||||||
|
{
|
||||||
|
write_integral<int32_t>(static_cast<int32_t>(len));
|
||||||
|
write(reinterpret_cast<word_t const*>(data), sizeof(uint16_t) * len);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t* Buffer::read_char16_string()
|
||||||
|
{
|
||||||
|
const int32_t len = read_integral<int32_t>();
|
||||||
|
RD_ASSERT_MSG(len >= 0, "read null string(length =" + std::to_string(len) + ")");
|
||||||
|
uint16_t * result = new uint16_t[len+1];
|
||||||
|
read(reinterpret_cast<Buffer::word_t*>(&result[0]), sizeof(uint16_t) * len);
|
||||||
|
result[len] = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::write_wstring(wstring_view value)
|
||||||
|
{
|
||||||
|
write_wstring_spec<sizeof(wchar_t)>(*this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::write_wstring(Wrapper<std::wstring> const& value)
|
||||||
|
{
|
||||||
|
write_wstring(*value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t TICKS_AT_EPOCH = 621355968000000000L;
|
||||||
|
int64_t TICKS_PER_MILLISECOND = 10000000;
|
||||||
|
|
||||||
|
DateTime Buffer::read_date_time()
|
||||||
|
{
|
||||||
|
int64_t time_in_ticks = read_integral<int64_t>();
|
||||||
|
time_t t = static_cast<time_t>(time_in_ticks / TICKS_PER_MILLISECOND - TICKS_AT_EPOCH / TICKS_PER_MILLISECOND);
|
||||||
|
return DateTime{t};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::write_date_time(DateTime const& date_time)
|
||||||
|
{
|
||||||
|
uint64_t t = date_time.seconds * TICKS_PER_MILLISECOND + TICKS_AT_EPOCH;
|
||||||
|
write_integral<int64_t>(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Buffer::read_bool()
|
||||||
|
{
|
||||||
|
const auto res = read_integral<uint8_t>();
|
||||||
|
RD_ASSERT_MSG(res == 0 || res == 1, "get byte:" + std::to_string(res) + " instead of 0 or 1");
|
||||||
|
return res == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::write_bool(bool value)
|
||||||
|
{
|
||||||
|
write_integral<word_t>(value ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t Buffer::read_char()
|
||||||
|
{
|
||||||
|
return static_cast<wchar_t>(read_integral<uint16_t>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::write_char(wchar_t value)
|
||||||
|
{
|
||||||
|
write_integral<uint16_t>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::read_byte_array(ByteArray& array)
|
||||||
|
{
|
||||||
|
const int32_t length = read_integral<int32_t>();
|
||||||
|
array.resize(length);
|
||||||
|
read_byte_array_raw(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::read_byte_array_raw(ByteArray& array)
|
||||||
|
{
|
||||||
|
read(array.data(), array.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::write_byte_array_raw(const ByteArray& array)
|
||||||
|
{
|
||||||
|
write(array.data(), array.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::ByteArray& Buffer::get_data()
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
#ifndef RD_CPP_UNSAFEBUFFER_H
|
||||||
|
#define RD_CPP_UNSAFEBUFFER_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "types/DateTime.h"
|
||||||
|
#include "util/core_util.h"
|
||||||
|
#include "types/wrapper.h"
|
||||||
|
#include "std/allocator.h"
|
||||||
|
#include "std/list.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Simple data buffer. Allows to "SerDes" plenty of types, such as integrals, arrays, etc.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API Buffer final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
friend class PkgInputStream;
|
||||||
|
|
||||||
|
using word_t = uint8_t;
|
||||||
|
|
||||||
|
using Allocator = std::allocator<word_t>;
|
||||||
|
|
||||||
|
using ByteArray = std::vector<word_t, Allocator>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <int>
|
||||||
|
friend std::wstring read_wstring_spec(Buffer&);
|
||||||
|
|
||||||
|
template <int>
|
||||||
|
friend void write_wstring_spec(Buffer&, wstring_view);
|
||||||
|
|
||||||
|
ByteArray data_;
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
// read
|
||||||
|
void read(word_t* dst, size_t size);
|
||||||
|
|
||||||
|
// write
|
||||||
|
void write(const word_t* src, size_t size);
|
||||||
|
|
||||||
|
size_t size() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
Buffer();
|
||||||
|
|
||||||
|
explicit Buffer(size_t initial_size);
|
||||||
|
|
||||||
|
explicit Buffer(ByteArray array, size_t offset = 0);
|
||||||
|
|
||||||
|
Buffer(Buffer const&) = delete;
|
||||||
|
|
||||||
|
Buffer& operator=(Buffer const&) = delete;
|
||||||
|
|
||||||
|
Buffer(Buffer&&) noexcept = default;
|
||||||
|
|
||||||
|
Buffer& operator=(Buffer&&) noexcept = default;
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
size_t get_position() const;
|
||||||
|
|
||||||
|
void set_position(size_t value);
|
||||||
|
|
||||||
|
void require_available(size_t size);
|
||||||
|
|
||||||
|
void check_available(size_t moreSize) const;
|
||||||
|
|
||||||
|
void rewind();
|
||||||
|
|
||||||
|
template <typename T, typename = typename std::enable_if_t<std::is_integral<T>::value, T>>
|
||||||
|
T read_integral()
|
||||||
|
{
|
||||||
|
T result;
|
||||||
|
read(reinterpret_cast<word_t*>(&result), sizeof(T));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = typename std::enable_if_t<std::is_integral<T>::value>>
|
||||||
|
void write_integral(T const& value)
|
||||||
|
{
|
||||||
|
write(reinterpret_cast<word_t const*>(&value), sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = typename std::enable_if_t<std::is_floating_point<T>::value, T>>
|
||||||
|
T read_floating_point()
|
||||||
|
{
|
||||||
|
T result;
|
||||||
|
read(reinterpret_cast<word_t*>(&result), sizeof(T));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = typename std::enable_if_t<std::is_floating_point<T>::value>>
|
||||||
|
void write_floating_point(T const& value)
|
||||||
|
{
|
||||||
|
write(reinterpret_cast<word_t const*>(&value), sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class, class> class C, typename T, typename A = allocator<T>,
|
||||||
|
typename = typename std::enable_if_t<util::is_pod_v<T>>>
|
||||||
|
C<T, A> read_array()
|
||||||
|
{
|
||||||
|
int32_t len = read_integral<int32_t>();
|
||||||
|
RD_ASSERT_MSG(len >= 0, "read null array(length = " + std::to_string(len) + ")");
|
||||||
|
C<T, A> result;
|
||||||
|
using rd::resize;
|
||||||
|
resize(result, len);
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
read(reinterpret_cast<word_t*>(&result[0]), sizeof(T) * len);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class, class> class C, typename T, typename A = allocator<value_or_wrapper<T>>>
|
||||||
|
C<value_or_wrapper<T>, A> read_array(std::function<value_or_wrapper<T>()> reader)
|
||||||
|
{
|
||||||
|
int32_t len = read_integral<int32_t>();
|
||||||
|
C<value_or_wrapper<T>, A> result;
|
||||||
|
using rd::resize;
|
||||||
|
resize(result, len);
|
||||||
|
for (int32_t i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
result[i] = std::move(reader());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class, class> class C, typename T, typename A = allocator<T>,
|
||||||
|
typename = typename std::enable_if_t<util::is_pod_v<T>>>
|
||||||
|
void write_array(C<T, A> const& container)
|
||||||
|
{
|
||||||
|
using rd::size;
|
||||||
|
const int32_t& len = rd::size(container);
|
||||||
|
write_integral<int32_t>(static_cast<int32_t>(len));
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
write(reinterpret_cast<word_t const*>(&container[0]), sizeof(T) * len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class, class> class C, typename T, typename A = allocator<T>,
|
||||||
|
typename = typename std::enable_if_t<!rd::util::in_heap_v<T>>>
|
||||||
|
void write_array(C<T, A> const& container, std::function<void(T const&)> writer)
|
||||||
|
{
|
||||||
|
using rd::size;
|
||||||
|
write_integral<int32_t>(size(container));
|
||||||
|
for (auto const& e : container)
|
||||||
|
{
|
||||||
|
writer(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <class, class> class C, typename T, typename A = allocator<Wrapper<T>>>
|
||||||
|
void write_array(C<Wrapper<T>, A> const& container, std::function<void(T const&)> writer)
|
||||||
|
{
|
||||||
|
using rd::size;
|
||||||
|
write_integral<int32_t>(size(container));
|
||||||
|
for (auto const& e : container)
|
||||||
|
{
|
||||||
|
writer(*e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_byte_array(ByteArray& array);
|
||||||
|
|
||||||
|
void read_byte_array_raw(ByteArray& array);
|
||||||
|
|
||||||
|
void write_byte_array_raw(ByteArray const& array);
|
||||||
|
|
||||||
|
// std::string readString() const;
|
||||||
|
|
||||||
|
// void writeString(std::string const &value) const;
|
||||||
|
|
||||||
|
bool read_bool();
|
||||||
|
|
||||||
|
void write_bool(bool value);
|
||||||
|
|
||||||
|
wchar_t read_char();
|
||||||
|
|
||||||
|
void write_char(wchar_t value);
|
||||||
|
|
||||||
|
void write_char16_string(const uint16_t* data, size_t len);
|
||||||
|
|
||||||
|
uint16_t * read_char16_string();
|
||||||
|
|
||||||
|
std::wstring read_wstring();
|
||||||
|
|
||||||
|
void write_wstring(std::wstring const& value);
|
||||||
|
|
||||||
|
void write_wstring(wstring_view value);
|
||||||
|
|
||||||
|
void write_wstring(Wrapper<std::wstring> const& value);
|
||||||
|
|
||||||
|
DateTime read_date_time();
|
||||||
|
|
||||||
|
void write_date_time(DateTime const& date_time);
|
||||||
|
|
||||||
|
template <typename T, typename = typename std::enable_if_t<util::is_enum_v<T>>>
|
||||||
|
T read_enum()
|
||||||
|
{
|
||||||
|
int32_t x = read_integral<int32_t>();
|
||||||
|
return static_cast<T>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = typename std::enable_if_t<util::is_enum_v<T>>>
|
||||||
|
void write_enum(T const& x)
|
||||||
|
{
|
||||||
|
write_integral<int32_t>(static_cast<int32_t>(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = typename std::enable_if_t<util::is_enum_v<T>>>
|
||||||
|
T read_enum_set()
|
||||||
|
{
|
||||||
|
int32_t x = read_integral<int32_t>();
|
||||||
|
return static_cast<T>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = typename std::enable_if_t<util::is_enum_v<T>>>
|
||||||
|
void write_enum_set(T const& x)
|
||||||
|
{
|
||||||
|
write_integral<int32_t>(static_cast<int32_t>(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename F, typename = typename std::enable_if_t<util::is_same_v<typename util::result_of_t<F()>, T>>>
|
||||||
|
opt_or_wrapper<T> read_nullable(F&& reader)
|
||||||
|
{
|
||||||
|
bool nullable = !read_bool();
|
||||||
|
if (nullable)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {reader()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename F,
|
||||||
|
typename = typename std::enable_if_t<util::is_same_v<typename util::result_of_t<F()>, Wrapper<T>>>>
|
||||||
|
Wrapper<T> read_nullable(F&& reader)
|
||||||
|
{
|
||||||
|
bool nullable = !read_bool();
|
||||||
|
if (nullable)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return reader();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if_t<!std::is_abstract<T>::value> write_nullable(
|
||||||
|
optional<T> const& value, std::function<void(T const&)> writer)
|
||||||
|
{
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
write_bool(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_bool(true);
|
||||||
|
writer(*value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename F>
|
||||||
|
typename std::enable_if_t<!util::is_invocable_v<F, Wrapper<T>>> write_nullable(Wrapper<T> const& value, F&& writer)
|
||||||
|
{
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
write_bool(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_bool(true);
|
||||||
|
writer(*value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename F>
|
||||||
|
typename std::enable_if_t<util::is_invocable_v<F, Wrapper<T>>> write_nullable(Wrapper<T> const& value, F&& writer)
|
||||||
|
{
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
write_bool(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_bool(true);
|
||||||
|
writer(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArray getArray() const&;
|
||||||
|
|
||||||
|
ByteArray getArray() &&;
|
||||||
|
|
||||||
|
ByteArray getRealArray() const&;
|
||||||
|
|
||||||
|
ByteArray getRealArray() &&;
|
||||||
|
|
||||||
|
word_t const* data() const;
|
||||||
|
|
||||||
|
word_t* data();
|
||||||
|
|
||||||
|
word_t const* current_pointer() const;
|
||||||
|
|
||||||
|
word_t* current_pointer();
|
||||||
|
|
||||||
|
ByteArray& get_data();
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_UNSAFEBUFFER_H
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#include "protocol/Identities.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
constexpr Identities::IdKind Identities::SERVER;
|
||||||
|
constexpr Identities::IdKind Identities::CLIENT;
|
||||||
|
|
||||||
|
Identities::Identities(IdKind dynamicKind) : id_acc(dynamicKind == IdKind::Client ? BASE_CLIENT_ID : BASE_SERVER_ID)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RdId Identities::next(const RdId& parent) const
|
||||||
|
{
|
||||||
|
RdId result = parent.mix(id_acc.fetch_add(2));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef RD_CPP_FRAMEWORK_IDENTITIES_H
|
||||||
|
#define RD_CPP_FRAMEWORK_IDENTITIES_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "protocol/RdId.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Generates unique identifiers for objects in an object graph.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API Identities
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
mutable std::atomic_int32_t id_acc;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class IdKind
|
||||||
|
{
|
||||||
|
Client,
|
||||||
|
Server
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr static IdKind SERVER = IdKind::Server;
|
||||||
|
constexpr static IdKind CLIENT = IdKind::Client;
|
||||||
|
|
||||||
|
constexpr static int32_t BASE_CLIENT_ID = RdId::MAX_STATIC_ID;
|
||||||
|
|
||||||
|
constexpr static int32_t BASE_SERVER_ID = RdId::MAX_STATIC_ID + 1;
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
explicit Identities(IdKind dynamicKind);
|
||||||
|
|
||||||
|
virtual ~Identities() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Generates the next unique identifier.
|
||||||
|
* \param parent previous id which is used for generating.
|
||||||
|
* \return unique identifier.
|
||||||
|
*/
|
||||||
|
RdId next(const RdId& parent) const;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_FRAMEWORK_IDENTITIES_H
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
#include "protocol/MessageBroker.h"
|
||||||
|
|
||||||
|
#include "base/RdReactiveBase.h"
|
||||||
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
std::shared_ptr<spdlog::logger> MessageBroker::logger =
|
||||||
|
spdlog::stderr_color_mt<spdlog::synchronous_factory>("logger", spdlog::color_mode::automatic);
|
||||||
|
|
||||||
|
static void execute(const IRdReactive* that, Buffer msg)
|
||||||
|
{
|
||||||
|
msg.read_integral<int16_t>(); // skip context
|
||||||
|
that->on_wire_received(std::move(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBroker::invoke(const RdReactiveBase* that, Buffer msg, bool sync) const
|
||||||
|
{
|
||||||
|
if (sync)
|
||||||
|
{
|
||||||
|
execute(that, std::move(msg));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto action = [this, that, message = std::move(msg)]() mutable {
|
||||||
|
bool exists_id = false;
|
||||||
|
{
|
||||||
|
std::lock_guard<decltype(lock)> guard(lock);
|
||||||
|
exists_id = subscriptions.count(that->get_id()) > 0;
|
||||||
|
}
|
||||||
|
if (exists_id)
|
||||||
|
{
|
||||||
|
execute(that, std::move(message));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger->trace("Disappeared Handler for Reactive entities with id: {}", to_string(that->get_id()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std::function<void()> function = util::make_shared_function(std::move(action));
|
||||||
|
that->get_wire_scheduler()->queue(std::move(function));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageBroker::MessageBroker(IScheduler* defaultScheduler) : default_scheduler(defaultScheduler)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBroker::dispatch(RdId id, Buffer message) const
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(!id.isNull(), "id mustn't be null")
|
||||||
|
|
||||||
|
{ // synchronized recursively
|
||||||
|
std::lock_guard<decltype(lock)> guard(lock);
|
||||||
|
RdReactiveBase const* s = subscriptions[id];
|
||||||
|
if (s == nullptr)
|
||||||
|
{
|
||||||
|
auto it = broker.find(id);
|
||||||
|
if (it == broker.end())
|
||||||
|
{
|
||||||
|
it = broker.emplace(id, Mq{}).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
broker[id].default_scheduler_messages.emplace(std::move(message));
|
||||||
|
|
||||||
|
auto action = [this, it, id]() mutable {
|
||||||
|
auto& current = it->second;
|
||||||
|
RdReactiveBase const* subscription = subscriptions[id];
|
||||||
|
|
||||||
|
optional<Buffer> message;
|
||||||
|
{
|
||||||
|
std::lock_guard<decltype(lock)> guard(lock);
|
||||||
|
if (!current.default_scheduler_messages.empty())
|
||||||
|
{
|
||||||
|
message = make_optional<Buffer>(std::move(current.default_scheduler_messages.front()));
|
||||||
|
current.default_scheduler_messages.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (subscription != nullptr)
|
||||||
|
{
|
||||||
|
if (message)
|
||||||
|
{
|
||||||
|
invoke(subscription, *std::move(message), subscription->get_wire_scheduler() == default_scheduler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger->trace("No handler for id: {}", to_string(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.default_scheduler_messages.empty())
|
||||||
|
{
|
||||||
|
auto t = std::move(broker[id]);
|
||||||
|
broker.erase(id);
|
||||||
|
for (auto& it : t.custom_scheduler_messages)
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(subscription->get_wire_scheduler() != default_scheduler,
|
||||||
|
"require equals of wire and default schedulers")
|
||||||
|
invoke(subscription, std::move(it));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std::function<void()> function = util::make_shared_function(std::move(action));
|
||||||
|
default_scheduler->queue(std::move(function));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s->get_wire_scheduler() == default_scheduler || s->get_wire_scheduler()->out_of_order_execution)
|
||||||
|
{
|
||||||
|
invoke(s, std::move(message));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto it = broker.find(id);
|
||||||
|
if (it == broker.end())
|
||||||
|
{
|
||||||
|
invoke(s, std::move(message));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Mq& mq = it->second;
|
||||||
|
mq.custom_scheduler_messages.push_back(std::move(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBroker::advise_on(Lifetime lifetime, RdReactiveBase const* entity) const
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(!entity->get_id().isNull(), ("id is null for entities: " + std::string(typeid(*entity).name())))
|
||||||
|
|
||||||
|
// advise MUST happen under default scheduler, not custom
|
||||||
|
default_scheduler->assert_thread();
|
||||||
|
|
||||||
|
std::lock_guard<decltype(lock)> guard(lock);
|
||||||
|
if (!lifetime->is_terminated())
|
||||||
|
{
|
||||||
|
auto key = entity->get_id();
|
||||||
|
subscriptions[key] = entity;
|
||||||
|
lifetime->add_action([this, key]() { subscriptions.erase(key); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#ifndef RD_CPP_MESSAGEBROKER_H
|
||||||
|
#define RD_CPP_MESSAGEBROKER_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "base/IRdReactive.h"
|
||||||
|
|
||||||
|
#include "std/unordered_map.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RdReactiveBase;
|
||||||
|
|
||||||
|
class RD_FRAMEWORK_API Mq
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
Mq() = default;
|
||||||
|
|
||||||
|
Mq(Mq const&) = delete;
|
||||||
|
|
||||||
|
Mq& operator=(Mq const&) = delete;
|
||||||
|
|
||||||
|
Mq(Mq&&) = default;
|
||||||
|
|
||||||
|
Mq& operator=(Mq&&) = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
mutable std::queue<Buffer> default_scheduler_messages;
|
||||||
|
std::vector<Buffer> custom_scheduler_messages;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RD_FRAMEWORK_API MessageBroker final
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IScheduler* default_scheduler = nullptr;
|
||||||
|
mutable rd::unordered_map<RdId, RdReactiveBase const*> subscriptions;
|
||||||
|
mutable rd::unordered_map<RdId, Mq> broker;
|
||||||
|
|
||||||
|
mutable std::recursive_mutex lock;
|
||||||
|
|
||||||
|
static std::shared_ptr<spdlog::logger> logger;
|
||||||
|
|
||||||
|
void invoke(const RdReactiveBase* that, Buffer msg, bool sync = false) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
explicit MessageBroker(IScheduler* defaultScheduler);
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
void dispatch(RdId id, Buffer message) const;
|
||||||
|
|
||||||
|
void advise_on(Lifetime lifetime, RdReactiveBase const* entity) const;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_MESSAGEBROKER_H
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#include "protocol/Protocol.h"
|
||||||
|
|
||||||
|
#include "serialization/SerializationCtx.h"
|
||||||
|
#include "intern/InternRoot.h"
|
||||||
|
|
||||||
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
std::shared_ptr<spdlog::logger> Protocol::initializationLogger =
|
||||||
|
spdlog::stderr_color_mt<spdlog::synchronous_factory>("initializationLogger", spdlog::color_mode::automatic);
|
||||||
|
|
||||||
|
constexpr string_view Protocol::InternRootName;
|
||||||
|
|
||||||
|
void Protocol::initialize() const
|
||||||
|
{
|
||||||
|
internRoot = std::make_unique<InternRoot>();
|
||||||
|
|
||||||
|
context = std::make_unique<SerializationCtx>(
|
||||||
|
serializers.get(), SerializationCtx::roots_t{{util::getPlatformIndependentHash("Protocol"), internRoot.get()}});
|
||||||
|
|
||||||
|
internRoot->set_id(RdId::Null().mix(InternRootName));
|
||||||
|
scheduler->queue([this] { internRoot->bind(lifetime, this, InternRootName); });
|
||||||
|
}
|
||||||
|
|
||||||
|
Protocol::Protocol(std::shared_ptr<Identities> identity, IScheduler* scheduler, std::shared_ptr<IWire> wire, Lifetime lifetime)
|
||||||
|
: IProtocol(std::move(identity), scheduler, std::move(wire)), lifetime(lifetime)
|
||||||
|
{
|
||||||
|
// initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Protocol::Protocol(Identities::IdKind kind, IScheduler* scheduler, std::shared_ptr<IWire> wire, Lifetime lifetime)
|
||||||
|
: IProtocol(std::make_shared<Identities>(kind), scheduler, std::move(wire)), lifetime(lifetime)
|
||||||
|
{
|
||||||
|
// initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Protocol::~Protocol() = default;
|
||||||
|
|
||||||
|
SerializationCtx& Protocol::get_serialization_context() const
|
||||||
|
{
|
||||||
|
if (!context)
|
||||||
|
{
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
return *context;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#ifndef RD_CPP_PROTOCOL_H
|
||||||
|
#define RD_CPP_PROTOCOL_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "base/IProtocol.h"
|
||||||
|
#include "protocol/Identities.h"
|
||||||
|
#include "serialization/SerializationCtx.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
// region predeclared
|
||||||
|
|
||||||
|
class SerializationCtx;
|
||||||
|
|
||||||
|
class InternRoot;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Top level node in the object graph. It stores [SerializationCtx] for polymorphic "SerDes"
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API Protocol : /*IRdDynamic, */ public IProtocol
|
||||||
|
{
|
||||||
|
constexpr static string_view InternRootName{"ProtocolInternRoot"};
|
||||||
|
|
||||||
|
Lifetime lifetime;
|
||||||
|
|
||||||
|
mutable std::unique_ptr<SerializationCtx> context;
|
||||||
|
|
||||||
|
mutable std::unique_ptr<InternRoot> internRoot;
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
private:
|
||||||
|
void initialize() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Protocol(std::shared_ptr<Identities> identity, IScheduler* scheduler, std::shared_ptr<IWire> wire, Lifetime lifetime);
|
||||||
|
|
||||||
|
Protocol(Identities::IdKind, IScheduler* scheduler, std::shared_ptr<IWire> wire, Lifetime lifetime);
|
||||||
|
|
||||||
|
Protocol(Protocol const&) = delete;
|
||||||
|
|
||||||
|
Protocol(Protocol&&) noexcept = default;
|
||||||
|
|
||||||
|
Protocol& operator=(Protocol&&) noexcept = default;
|
||||||
|
|
||||||
|
virtual ~Protocol();
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
SerializationCtx& get_serialization_context() const override;
|
||||||
|
|
||||||
|
static std::shared_ptr<spdlog::logger> initializationLogger;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_PROTOCOL_H
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#include "protocol/RdId.h"
|
||||||
|
|
||||||
|
#include "protocol/Identities.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
RdId RdId::read(Buffer& buffer)
|
||||||
|
{
|
||||||
|
const auto number = buffer.read_integral<hash_t>();
|
||||||
|
return RdId(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RdId::write(Buffer& buffer) const
|
||||||
|
{
|
||||||
|
buffer.write_integral(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string(RdId const& id)
|
||||||
|
{
|
||||||
|
return std::to_string(id.hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(RdId const& left, RdId const& right)
|
||||||
|
{
|
||||||
|
return left.hash == right.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const RdId& lhs, const RdId& rhs)
|
||||||
|
{
|
||||||
|
return !(rhs == lhs);
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
#ifndef RD_CPP_FRAMEWORK_RDID_H
|
||||||
|
#define RD_CPP_FRAMEWORK_RDID_H
|
||||||
|
|
||||||
|
#include "protocol/Buffer.h"
|
||||||
|
#include "hashing.h"
|
||||||
|
#include "std/hash.h"
|
||||||
|
|
||||||
|
#include "thirdparty.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RdId;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct RD_FRAMEWORK_API hash<RdId>
|
||||||
|
{
|
||||||
|
size_t operator()(const RdId& value) const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief An identifier of the object that participates in the object graph.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API RdId
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using hash_t = util::hash_t;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct hash<RdId>;
|
||||||
|
|
||||||
|
constexpr static hash_t NULL_ID = 0;
|
||||||
|
|
||||||
|
hash_t hash{NULL_ID};
|
||||||
|
|
||||||
|
public:
|
||||||
|
friend bool RD_FRAMEWORK_API operator==(RdId const& left, RdId const& right);
|
||||||
|
|
||||||
|
friend bool RD_FRAMEWORK_API operator!=(const RdId& lhs, const RdId& rhs);
|
||||||
|
|
||||||
|
// region ctor/dtor
|
||||||
|
constexpr RdId() = default;
|
||||||
|
|
||||||
|
constexpr RdId(const RdId& other) = default;
|
||||||
|
|
||||||
|
constexpr RdId& operator=(const RdId& other) = default;
|
||||||
|
|
||||||
|
constexpr RdId(RdId&& other) noexcept = default;
|
||||||
|
|
||||||
|
constexpr RdId& operator=(RdId&& other) noexcept = default;
|
||||||
|
|
||||||
|
explicit constexpr RdId(hash_t hash) : hash(hash)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// static const RdId NULL_ID;
|
||||||
|
|
||||||
|
static constexpr RdId Null()
|
||||||
|
{
|
||||||
|
return RdId{NULL_ID};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr int32_t MAX_STATIC_ID = 1'000'000;
|
||||||
|
|
||||||
|
static RdId read(Buffer& buffer);
|
||||||
|
|
||||||
|
void write(Buffer& buffer) const;
|
||||||
|
|
||||||
|
constexpr hash_t get_hash() const
|
||||||
|
{
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool isNull() const
|
||||||
|
{
|
||||||
|
return get_hash() == NULL_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
RdId notNull()
|
||||||
|
{
|
||||||
|
RD_ASSERT_MSG(!isNull(), "id is null");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*template<size_t N>
|
||||||
|
constexpr RdId mix(char const (&tail)[N]) const {
|
||||||
|
return RdId(util::getPlatformIndependentHash<N>(tail, static_cast<util::constexpr_hash_t>(hash)));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
constexpr RdId mix(string_view tail) const
|
||||||
|
{
|
||||||
|
return RdId(util::getPlatformIndependentHash(tail, static_cast<util::constexpr_hash_t>(hash)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*constexpr RdId mix(int32_t tail) const {
|
||||||
|
return RdId(util::getPlatformIndependentHash(tail, static_cast<util::constexpr_hash_t>(hash)));
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
constexpr RdId mix(int64_t tail) const
|
||||||
|
{
|
||||||
|
return RdId(util::getPlatformIndependentHash(tail, static_cast<util::constexpr_hash_t>(hash)));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::string RD_FRAMEWORK_API to_string(RdId const& id);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline size_t hash<RdId>::operator()(const RdId& value) const noexcept
|
||||||
|
{
|
||||||
|
return hash<RdId::hash_t>()(value.hash);
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_FRAMEWORK_RDID_H
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include "SimpleScheduler.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
void SimpleScheduler::flush()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleScheduler::queue(std::function<void()> action)
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimpleScheduler::is_active() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef RD_CPP_TESTSCHEDULER_H
|
||||||
|
#define RD_CPP_TESTSCHEDULER_H
|
||||||
|
|
||||||
|
#include "scheduler/base/IScheduler.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief simple scheduler, which immediately invoke action on queue, and is always active.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API SimpleScheduler : public IScheduler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
SimpleScheduler() = default;
|
||||||
|
|
||||||
|
virtual ~SimpleScheduler() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
void flush() override;
|
||||||
|
|
||||||
|
void queue(std::function<void()> action) override;
|
||||||
|
|
||||||
|
bool is_active() const override;
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_TESTSCHEDULER_H
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include "SingleThreadScheduler.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "ctpl_stl.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
SingleThreadScheduler::SingleThreadScheduler(Lifetime lifetime, std::string name)
|
||||||
|
: SingleThreadSchedulerBase(std::move(name)), lifetime(lifetime)
|
||||||
|
{
|
||||||
|
lifetime->add_action([this]() {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pool->stop(true);
|
||||||
|
}
|
||||||
|
catch (std::exception const& e)
|
||||||
|
{
|
||||||
|
(void)e;
|
||||||
|
log->error("Failed to terminate {}", this->name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef RD_CPP_SINGLETHREADSCHEDULER_H
|
||||||
|
#define RD_CPP_SINGLETHREADSCHEDULER_H
|
||||||
|
|
||||||
|
#include "base/SingleThreadSchedulerBase.h"
|
||||||
|
|
||||||
|
#include "lifetime/Lifetime.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RD_FRAMEWORK_API SingleThreadScheduler : public SingleThreadSchedulerBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Lifetime lifetime;
|
||||||
|
|
||||||
|
SingleThreadScheduler(Lifetime lifetime, std::string name);
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_SINGLETHREADSCHEDULER_H
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#include "SynchronousScheduler.h"
|
||||||
|
|
||||||
|
#include "guards.h"
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
static thread_local int32_t SynchronousScheduler_active_count = 0;
|
||||||
|
|
||||||
|
void SynchronousScheduler::queue(std::function<void()> action)
|
||||||
|
{
|
||||||
|
util::increment_guard<int32_t> guard(SynchronousScheduler_active_count);
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SynchronousScheduler::flush()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SynchronousScheduler::is_active() const
|
||||||
|
{
|
||||||
|
return SynchronousScheduler_active_count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SynchronousScheduler& SynchronousScheduler::Instance()
|
||||||
|
{
|
||||||
|
static SynchronousScheduler globalSynchronousScheduler;
|
||||||
|
return globalSynchronousScheduler;
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#ifndef RD_CPP_SYNCHRONOUSSCHEDULER_H
|
||||||
|
#define RD_CPP_SYNCHRONOUSSCHEDULER_H
|
||||||
|
|
||||||
|
#include "scheduler/base/IScheduler.h"
|
||||||
|
|
||||||
|
#include "guards.h"
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
class RD_FRAMEWORK_API SynchronousScheduler : public IScheduler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
SynchronousScheduler() = default;
|
||||||
|
|
||||||
|
SynchronousScheduler(SynchronousScheduler const&) = delete;
|
||||||
|
|
||||||
|
SynchronousScheduler(SynchronousScheduler&&) = delete;
|
||||||
|
|
||||||
|
virtual ~SynchronousScheduler() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
void queue(std::function<void()> action) override;
|
||||||
|
|
||||||
|
void flush() override;
|
||||||
|
|
||||||
|
bool is_active() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief global synchronous scheduler for whole application.
|
||||||
|
*/
|
||||||
|
static SynchronousScheduler& Instance();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rd
|
||||||
|
|
||||||
|
#endif // RD_CPP_SYNCHRONOUSSCHEDULER_H
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include "IScheduler.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
void IScheduler::assert_thread() const
|
||||||
|
{
|
||||||
|
if (!is_active())
|
||||||
|
{
|
||||||
|
std::ostringstream msg;
|
||||||
|
msg << "Illegal scheduler for current action. Must be " << thread_id << ", was " << std::this_thread::get_id();
|
||||||
|
spdlog::error(msg.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IScheduler::invoke_or_queue(std::function<void()> action)
|
||||||
|
{
|
||||||
|
if (is_active())
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
queue(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace rd
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
#ifndef RD_CPP_ISCHEDULER_H
|
||||||
|
#define RD_CPP_ISCHEDULER_H
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4251)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <rd_framework_export.h>
|
||||||
|
|
||||||
|
namespace rd
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Allows to queue the execution of actions on a different thread.
|
||||||
|
*/
|
||||||
|
class RD_FRAMEWORK_API IScheduler
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
std::thread::id thread_id;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// region ctor/dtor
|
||||||
|
|
||||||
|
IScheduler() = default;
|
||||||
|
|
||||||
|
virtual ~IScheduler() = default;
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Queues the execution of the given [action].
|
||||||
|
*
|
||||||
|
* \param action to be queued.
|
||||||
|
*/
|
||||||
|
virtual void queue(std::function<void()> action) = 0;
|
||||||
|
|
||||||
|
// TO-DO
|
||||||
|
bool out_of_order_execution = false;
|
||||||
|
|
||||||
|
virtual void assert_thread() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief invoke action immediately if scheduler is active, queue it otherwise.
|
||||||
|
* \param action to be invoked
|
||||||
|
*/
|
||||||
|
virtual void invoke_or_queue(std::function<void()> action);
|
||||||
|
|
||||||
|
virtual void flush() = 0;
|
||||||
|
|
||||||
|
virtual bool is_active() const = 0;
|
||||||
|
|
||||||
|
std::thread::id get_thread_id() const
|
||||||
|
{
|
||||||
|
return thread_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rd
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RD_CPP_ISCHEDULER_H
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user