Progress.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
/// @file AABB.hpp
|
||||
/// For complete documentation, see src/AABB.cpp
|
||||
|
||||
namespace hdk::grid {
|
||||
template <int N>
|
||||
class AABB {
|
||||
/// @todo Implement Axis Aligned Bounding Box virtual interface class or remove this file if not needed
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,297 @@
|
||||
#pragma once
|
||||
/// @file AbstractEventDispatch.hpp
|
||||
/// For complete documentation, see src/AbstractEventDispatch.cpp
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
namespace hdk::grid::eps {
|
||||
template <typename... ArgTypes> class Conduit {
|
||||
private:
|
||||
struct State;
|
||||
|
||||
public:
|
||||
typedef std::function<void(ArgTypes...)> Callback_t;
|
||||
|
||||
class Tap {
|
||||
public:
|
||||
Tap() = default;
|
||||
Tap(const Tap&) = delete;
|
||||
Tap& operator=(const Tap&) = delete;
|
||||
Tap(Tap&& other) noexcept
|
||||
: state(std::move(other.state)), id(other.id) {
|
||||
other.id = 0;
|
||||
}
|
||||
Tap& operator=(Tap&& other) noexcept {
|
||||
if (this != &other) {
|
||||
Detach();
|
||||
state = std::move(other.state);
|
||||
id = other.id;
|
||||
other.id = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IsActive() const {
|
||||
auto locked = state.lock();
|
||||
if (!locked || id == 0) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(locked->mutex);
|
||||
return locked->index.find(id) != locked->index.end();
|
||||
}
|
||||
|
||||
bool Detach() {
|
||||
auto locked = state.lock();
|
||||
if (!locked || id == 0) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(locked->mutex);
|
||||
auto it = locked->index.find(id);
|
||||
if (it == locked->index.end()) {
|
||||
id = 0;
|
||||
return false;
|
||||
}
|
||||
locked->entries.erase(it->second);
|
||||
locked->index.erase(it);
|
||||
id = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pause() {
|
||||
auto locked = state.lock();
|
||||
if (!locked || id == 0) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(locked->mutex);
|
||||
auto it = locked->index.find(id);
|
||||
if (it == locked->index.end()) {
|
||||
id = 0;
|
||||
return false;
|
||||
}
|
||||
it->second->paused = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resume() {
|
||||
auto locked = state.lock();
|
||||
if (!locked || id == 0) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(locked->mutex);
|
||||
auto it = locked->index.find(id);
|
||||
if (it == locked->index.end()) {
|
||||
id = 0;
|
||||
return false;
|
||||
}
|
||||
it->second->paused = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsPaused() const {
|
||||
auto locked = state.lock();
|
||||
if (!locked || id == 0) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(locked->mutex);
|
||||
auto it = locked->index.find(id);
|
||||
if (it == locked->index.end()) {
|
||||
return false;
|
||||
}
|
||||
return it->second->paused;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Conduit;
|
||||
Tap(std::weak_ptr<State> subscription_state, std::uint64_t subscription_id)
|
||||
: state(std::move(subscription_state)), id(subscription_id) { }
|
||||
|
||||
std::weak_ptr<State> state;
|
||||
std::uint64_t id = 0;
|
||||
};
|
||||
|
||||
virtual ~Conduit() = default;
|
||||
|
||||
virtual Tap Attach(Callback_t callback) {
|
||||
std::lock_guard<std::mutex> guard(state->mutex);
|
||||
const std::uint64_t id = state->next_id++;
|
||||
auto it = state->entries.insert(state->entries.end(), Entry{ id, std::move(callback), false });
|
||||
state->index.emplace(id, it);
|
||||
return Tap(state, id);
|
||||
}
|
||||
|
||||
virtual bool Detach(std::uint64_t id) {
|
||||
if (id == 0) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(state->mutex);
|
||||
auto it = state->index.find(id);
|
||||
if (it == state->index.end()) {
|
||||
return false;
|
||||
}
|
||||
state->entries.erase(it->second);
|
||||
state->index.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool Pause(std::uint64_t id) {
|
||||
if (id == 0) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(state->mutex);
|
||||
auto it = state->index.find(id);
|
||||
if (it == state->index.end()) {
|
||||
return false;
|
||||
}
|
||||
it->second->paused = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool Resume(std::uint64_t id) {
|
||||
if (id == 0) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(state->mutex);
|
||||
auto it = state->index.find(id);
|
||||
if (it == state->index.end()) {
|
||||
return false;
|
||||
}
|
||||
it->second->paused = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool IsPaused(std::uint64_t id) const {
|
||||
if (id == 0) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(state->mutex);
|
||||
auto it = state->index.find(id);
|
||||
if (it == state->index.end()) {
|
||||
return false;
|
||||
}
|
||||
return it->second->paused;
|
||||
}
|
||||
|
||||
virtual void Trigger(ArgTypes... args) const {
|
||||
std::vector<Callback_t> callbacks;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(state->mutex);
|
||||
callbacks.reserve(state->entries.size());
|
||||
for (const auto& entry : state->entries) {
|
||||
if (!entry.paused) {
|
||||
callbacks.push_back(entry.callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& callback : callbacks) {
|
||||
callback(args...);
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(ArgTypes... args) const { Trigger(args...); }
|
||||
Tap operator%(Callback_t callback) { return Attach(std::move(callback)); }
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
std::uint64_t id;
|
||||
Callback_t callback;
|
||||
bool paused;
|
||||
};
|
||||
|
||||
struct State {
|
||||
std::mutex mutex;
|
||||
std::list<Entry> entries;
|
||||
std::unordered_map<std::uint64_t, typename std::list<Entry>::iterator> index;
|
||||
std::uint64_t next_id = 1;
|
||||
};
|
||||
|
||||
std::shared_ptr<State> state = std::make_shared<State>();
|
||||
};
|
||||
|
||||
class TapScope {
|
||||
public:
|
||||
TapScope() = default;
|
||||
TapScope(const TapScope&) = delete;
|
||||
TapScope& operator=(const TapScope&) = delete;
|
||||
TapScope(TapScope&&) = delete;
|
||||
TapScope& operator=(TapScope&&) = delete;
|
||||
~TapScope() { DetachAll(); }
|
||||
|
||||
template <typename TapT> void Add(TapT tap) {
|
||||
if (!tap.IsActive()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
entries.emplace_back(std::make_unique<TapEntry<TapT>>(std::move(tap)));
|
||||
}
|
||||
|
||||
template <typename TapT> TapScope& operator<<(TapT tap) {
|
||||
Add(std::move(tap));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DetachAll() {
|
||||
std::vector<std::unique_ptr<TapEntryBase>> to_run;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
to_run.swap(entries);
|
||||
}
|
||||
for (auto& tap : to_run) {
|
||||
tap->Detach();
|
||||
}
|
||||
}
|
||||
|
||||
void PauseAll() {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
for (auto& tap : entries) {
|
||||
tap->Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void ResumeAll() {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
for (auto& tap : entries) {
|
||||
tap->Resume();
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
entries.clear();
|
||||
}
|
||||
|
||||
std::size_t Size() const {
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
struct TapEntryBase {
|
||||
virtual ~TapEntryBase() = default;
|
||||
virtual void Detach() = 0;
|
||||
virtual void Pause() = 0;
|
||||
virtual void Resume() = 0;
|
||||
};
|
||||
|
||||
template <typename TapT> struct TapEntry : TapEntryBase {
|
||||
explicit TapEntry(TapT&& tap_value)
|
||||
: tap(std::move(tap_value)) { }
|
||||
|
||||
void Detach() override { tap.Detach(); }
|
||||
void Pause() override { tap.Pause(); }
|
||||
void Resume() override { tap.Resume(); }
|
||||
|
||||
TapT tap;
|
||||
};
|
||||
|
||||
mutable std::mutex mutex;
|
||||
std::vector<std::unique_ptr<TapEntryBase>> entries;
|
||||
|
||||
};
|
||||
} // namespace hdk::grid::eps
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
/// @file PrimitiveWrapper.hpp
|
||||
/// For complete documentation, see src/PrimitiveWrapper.cpp
|
||||
//
|
||||
namespace hdk::grid {
|
||||
template <typename T>
|
||||
class PrimitiveWrapper {
|
||||
private:
|
||||
T primitive_value;
|
||||
public:
|
||||
PrimitiveWrapper() = delete;
|
||||
PrimitiveWrapper(T value) : primitive_value(value) {}
|
||||
operator T() const { return primitive_value; }
|
||||
PrimitiveWrapper& operator=(T newValue) {
|
||||
primitive_value = newValue;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,160 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
/// @file SharedPtrWrapper.hpp
|
||||
/// For complete documentation, see src/SharedPtrWrapper.cpp
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace hdk::grid {
|
||||
/**
|
||||
* @brief Reference-counted wrapper for shared pointers to structures.
|
||||
* @remarks
|
||||
* Has no default constructor. Must be constructed with a value or explicitly set initialize to nullptr.
|
||||
* Evaluates to false if shared pointer is null, true otherwise.
|
||||
* Will automatically cast to the `StructType*`, allowing it to be used in C API calls that expect raw pointers, while still benefiting from the reference counting and caching mechanisms provided by the wrapper.
|
||||
*/
|
||||
template <typename StructType>
|
||||
class SharedPtrWrapper {
|
||||
public:
|
||||
/** Type alias for a shared pointer to the managed structure. **/
|
||||
using SharedPtr = std::shared_ptr<StructType>;
|
||||
/** Type alias for a weak pointer to the managed structure. **/
|
||||
using WeakPtr = std::weak_ptr<StructType>;
|
||||
/** Type alias for the cache map that maps raw pointers to weak pointers. **/
|
||||
using SharedCacheMap = std::unordered_map<StructType*, WeakPtr>;
|
||||
/** Function type for deleting the managed structure. **/
|
||||
using DeleteFunc = std::function<void(StructType*)>;
|
||||
static void null_deleter(StructType*) { }
|
||||
/** Default constructor deleted. You must declare a value or declare it to be null on construction. **/
|
||||
SharedPtrWrapper() = delete;
|
||||
/** Null constructor (same as default constructor). **/
|
||||
SharedPtrWrapper(std::nullptr_t)
|
||||
: shared_struct(nullptr)
|
||||
{
|
||||
}
|
||||
/** Constructor that takes a shared pointer and a delete function. **/
|
||||
SharedPtrWrapper(StructType* struct_ptr, DeleteFunc deleteFunc)
|
||||
: shared_struct(struct_ptr, deleteFunc)
|
||||
{
|
||||
template <typename StructType> class SharedPtrWrapper {
|
||||
public:
|
||||
using SharedPtr = std::shared_ptr<StructType>;
|
||||
using WeakPtr = std::weak_ptr<StructType>;
|
||||
using SharedCacheMap = std::unordered_map<StructType*, WeakPtr>;
|
||||
using DeleteFunc = std::function<void(StructType*)>;
|
||||
static void null_deleter(StructType*) { }
|
||||
SharedPtrWrapper() = delete;
|
||||
SharedPtrWrapper(std::nullptr_t)
|
||||
: shared_struct(nullptr) { }
|
||||
SharedPtrWrapper(StructType* struct_ptr, DeleteFunc deleteFunc)
|
||||
: shared_struct(struct_ptr, deleteFunc) {
|
||||
// Cache the shared pointer to ensure consistent reference counting for the same raw pointer.
|
||||
get_shared_weak_ptr_cache()[struct_ptr] = shared_struct;
|
||||
}
|
||||
/** Move constructor. **/
|
||||
SharedPtrWrapper(SharedPtrWrapper&& other) noexcept
|
||||
: shared_struct(std::move(other.shared_struct))
|
||||
{
|
||||
}
|
||||
/** Existing Shared Pointer */
|
||||
SharedPtrWrapper(SharedPtr shared_ptr)
|
||||
}
|
||||
SharedPtrWrapper(SharedPtrWrapper&& other) noexcept
|
||||
: shared_struct(std::move(other.shared_struct)) { }
|
||||
/** Existing Shared Pointer */
|
||||
SharedPtrWrapper(SharedPtr shared_ptr)
|
||||
|
||||
{
|
||||
{
|
||||
shared_struct = shared_ptr;
|
||||
if ((shared_ptr.get() != nullptr) && !is_cached(shared_ptr.get())) {
|
||||
get_shared_weak_ptr_cache()[shared_ptr.get()] = shared_struct;
|
||||
get_shared_weak_ptr_cache()[shared_ptr.get()] = shared_struct;
|
||||
}
|
||||
}
|
||||
/** Move assignment operator. **/
|
||||
SharedPtrWrapper& operator=(SharedPtrWrapper&& other) noexcept
|
||||
{
|
||||
}
|
||||
SharedPtrWrapper& operator=(SharedPtrWrapper&& other) noexcept {
|
||||
if (this != &other) {
|
||||
shared_struct = std::move(other.shared_struct);
|
||||
shared_struct = std::move(other.shared_struct);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
/** Copy constructor. **/
|
||||
SharedPtrWrapper(const SharedPtrWrapper& other)
|
||||
: shared_struct(other.shared_struct)
|
||||
{
|
||||
}
|
||||
/** Copy assignment operator. **/
|
||||
SharedPtrWrapper& operator=(const SharedPtrWrapper& other)
|
||||
{
|
||||
}
|
||||
SharedPtrWrapper(const SharedPtrWrapper& other)
|
||||
: shared_struct(other.shared_struct) { }
|
||||
SharedPtrWrapper& operator=(const SharedPtrWrapper& other) {
|
||||
if (this != &other) {
|
||||
shared_struct = other.shared_struct;
|
||||
shared_struct = other.shared_struct;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
/** Conversion operator to allow automatic casting to StructType*. **/
|
||||
operator StructType*() const
|
||||
{
|
||||
return shared_struct.get();
|
||||
}
|
||||
/** Set this instance to nullptr. **/
|
||||
SharedPtrWrapper& operator=(std::nullptr_t)
|
||||
{
|
||||
}
|
||||
operator StructType*() const { return shared_struct.get(); }
|
||||
SharedPtrWrapper& operator=(std::nullptr_t) {
|
||||
shared_struct.reset();
|
||||
return *this;
|
||||
}
|
||||
/** Conversion operator to allow automatic casting to bool. **/
|
||||
operator bool() const
|
||||
{
|
||||
return shared_struct != nullptr;
|
||||
}
|
||||
}
|
||||
operator bool() const { return shared_struct != nullptr; }
|
||||
StructType* operator->() const { return shared_struct.get(); }
|
||||
private: // internal use only.
|
||||
SharedPtr shared_struct { nullptr };
|
||||
|
||||
private: // internal use only.
|
||||
/** The value we are wrapping. **/
|
||||
SharedPtr shared_struct{nullptr};
|
||||
|
||||
protected: // For use by derived classes.
|
||||
/** So that we provide consistent reference counting on the same pointer we need to have a cache of shared pointers. This is because if we create multiple shared pointers to the same raw pointer, they will have separate reference counts and may lead to double deletion. **/
|
||||
static SharedCacheMap& get_shared_weak_ptr_cache()
|
||||
{
|
||||
protected: // For use by derived classes.
|
||||
static SharedCacheMap& get_shared_weak_ptr_cache() {
|
||||
static SharedCacheMap cache_map;
|
||||
return cache_map;
|
||||
}
|
||||
/** Helper function to check if a raw pointer is already cached and has a valid shared pointer. **/
|
||||
static bool is_cached(StructType* ptr)
|
||||
{
|
||||
}
|
||||
static bool is_cached(StructType* ptr) {
|
||||
auto& cache = get_shared_weak_ptr_cache();
|
||||
return cache.find(ptr) != cache.end() && !cache[ptr].expired();
|
||||
}
|
||||
/** @brief helper: if cached returns existing, else returns non-owning shared pointer without caching it.
|
||||
* @remarks
|
||||
* The idea is that if we are already managing this pointer
|
||||
* we should return the existing shared pointer to ensure consistent reference counting
|
||||
* but if we are not managing this pointer, we can create a new shared pointer with a null deleter
|
||||
* so that we can still return a shared pointer without taking ownership of the pointer.
|
||||
* @return Always a valid shared pointer to `ptr`, but it may be a non-owning shared pointer if `ptr` is not already cached.
|
||||
***/
|
||||
static SharedPtr get_or_view(StructType* ptr)
|
||||
{
|
||||
}
|
||||
static SharedPtr get_or_view(StructType* ptr) {
|
||||
/** if no pointer */
|
||||
if (!ptr) {
|
||||
/** then return a non-owning null */
|
||||
return SharedPtr(nullptr, null_deleter);
|
||||
/** then return a non-owning null */
|
||||
return SharedPtr(nullptr, null_deleter);
|
||||
}
|
||||
|
||||
if (is_cached(ptr)) {
|
||||
return get_shared_weak_ptr_cache()[ptr].lock();
|
||||
return get_shared_weak_ptr_cache()[ptr].lock();
|
||||
} else {
|
||||
SharedPtr new_shared(ptr, null_deleter);
|
||||
// cache[ptr] = new_shared;
|
||||
return new_shared;
|
||||
SharedPtr new_shared(ptr, null_deleter);
|
||||
// cache[ptr] = new_shared;
|
||||
return new_shared;
|
||||
}
|
||||
}
|
||||
/** @brief helper: if cached returns existing, else constructs shared pointer with deleter and caches.
|
||||
* @remarks
|
||||
* The idea is that if we are already managing this pointer
|
||||
* we should return the existing shared pointer to ensure consistent reference counting
|
||||
* but if we are not managing this pointer, we can create a new shared pointer taking ownership.
|
||||
* When this pointer is given in the future, as long as it is still valid, will return the same shared pointer.
|
||||
* @param ptr the pointer to either get or create for
|
||||
* @param deleteFunc the delete function used if creating
|
||||
* @return SharedPtr either precached or created and cached
|
||||
*/
|
||||
static SharedPtr get_or_cache(StructType* ptr, DeleteFunc deleteFunc)
|
||||
{
|
||||
}
|
||||
static SharedPtr get_or_cache(StructType* ptr, DeleteFunc deleteFunc) {
|
||||
/** If no ptr, no need to do anything else */
|
||||
if (!ptr) {
|
||||
return SharedPtr(nullptr, deleteFunc);
|
||||
return SharedPtr(nullptr, deleteFunc);
|
||||
}
|
||||
auto& cache = get_shared_weak_ptr_cache();
|
||||
if (is_cached(ptr)) {
|
||||
return cache[ptr].lock();
|
||||
return cache[ptr].lock();
|
||||
} else {
|
||||
SharedPtr new_shared(ptr, deleteFunc);
|
||||
cache[ptr] = new_shared;
|
||||
return new_shared;
|
||||
SharedPtr new_shared(ptr, deleteFunc);
|
||||
cache[ptr] = new_shared;
|
||||
return new_shared;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user