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