removed unused methods(BeginPlay, Tick) and added rider plugin

This commit is contained in:
Caleb Buhungiro
2025-07-05 15:04:21 +08:00
parent a98fd4b2a7
commit 58a7fc2f55
416 changed files with 64917 additions and 16 deletions

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -0,0 +1 @@
/」」<EFBDA3><EFBDA3>n怒退<E68092>吭n

View 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
}

View 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));
}
}
}

View 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

View 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;
};

View File

@@ -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 */

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,5 @@
#include "IRdWireable.h"
namespace rd
{
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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