cpptick - reorganie files
This commit is contained in:
parent
72961f2750
commit
80a4f39f7b
117
CppTick/Inc/cpptick/argstore.hpp
Normal file
117
CppTick/Inc/cpptick/argstore.hpp
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 24/10/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace cpptick
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept TrivialClass = std::is_trivial_v<T> && std::is_trivially_destructible_v<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept CopyableClass = !TrivialClass<T> && std::copy_constructible<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept SlotArgument = TrivialClass<T> || CopyableClass<T>;
|
||||||
|
|
||||||
|
struct ArgStorage
|
||||||
|
{
|
||||||
|
std::array<char, sizeof(int) * 12> buffer;
|
||||||
|
std::array<char*, 12> pointer_buffer;
|
||||||
|
std::array<char*, 12>::iterator pointer_buffer_head;
|
||||||
|
std::size_t space_remaining;
|
||||||
|
|
||||||
|
ArgStorage() = default;
|
||||||
|
ArgStorage(const ArgStorage&) = default;
|
||||||
|
ArgStorage(ArgStorage&&) = default;
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
space_remaining = buffer.size();
|
||||||
|
pointer_buffer_head = pointer_buffer.begin();
|
||||||
|
*pointer_buffer_head = buffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<TrivialClass T>
|
||||||
|
void pushArg(T&& arg)
|
||||||
|
{
|
||||||
|
if (pointer_buffer_head == pointer_buffer.end() || space_remaining == 0)
|
||||||
|
std::exit(1);
|
||||||
|
|
||||||
|
void* memory_location = *pointer_buffer_head;
|
||||||
|
|
||||||
|
if (std::align(alignof(T), sizeof(T), memory_location, space_remaining) == nullptr)
|
||||||
|
std::exit(2);
|
||||||
|
|
||||||
|
std::memcpy(memory_location, &arg, sizeof(T));
|
||||||
|
pointer_buffer_head++;
|
||||||
|
if (pointer_buffer_head != pointer_buffer.end())
|
||||||
|
{
|
||||||
|
*pointer_buffer_head = (char*)memory_location + sizeof(T);
|
||||||
|
space_remaining -= sizeof(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<CopyableClass T>
|
||||||
|
void pushArg(T&& arg)
|
||||||
|
{
|
||||||
|
if (pointer_buffer_head == pointer_buffer.end() || space_remaining == 0)
|
||||||
|
std::exit(1);
|
||||||
|
|
||||||
|
void* memory_location = *pointer_buffer_head;
|
||||||
|
|
||||||
|
if (std::align(alignof(T), sizeof(T), memory_location, space_remaining) == nullptr)
|
||||||
|
std::exit(2);
|
||||||
|
|
||||||
|
new (memory_location) T{arg};
|
||||||
|
pointer_buffer_head++;
|
||||||
|
if (pointer_buffer_head != pointer_buffer.end())
|
||||||
|
{
|
||||||
|
*pointer_buffer_head = (char*)memory_location + sizeof(T);
|
||||||
|
space_remaining -= sizeof(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<TrivialClass T>
|
||||||
|
void cleanUp(const std::size_t)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
template<CopyableClass T>
|
||||||
|
void cleanUp(const std::size_t index)
|
||||||
|
{
|
||||||
|
T* item = reinterpret_cast<T*>(pointer_buffer[index]);
|
||||||
|
item->~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args, size_t... Is>
|
||||||
|
void cleanUp(std::index_sequence<Is...>)
|
||||||
|
{
|
||||||
|
(cleanUp<Args>(Is), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void cleanUp()
|
||||||
|
{
|
||||||
|
cleanUp<Args...>(std::make_index_sequence<sizeof...(Args)>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<SlotArgument T>
|
||||||
|
T& at(const std::size_t idx)
|
||||||
|
{
|
||||||
|
return *((T*)pointer_buffer[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<SlotArgument T>
|
||||||
|
const T& at(const std::size_t idx) const
|
||||||
|
{
|
||||||
|
return *((T*)pointer_buffer[idx]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,235 +1,9 @@
|
|||||||
//
|
//
|
||||||
// Created by erki on 12/09/23.
|
// Created by erki on 24/10/23.
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <type_traits>
|
#include "cpptick/argstore.hpp"
|
||||||
#include <memory>
|
#include "cpptick/scheduler.hpp"
|
||||||
#include <array>
|
#include "cpptick/slot.hpp"
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include <utility_function.hpp>
|
|
||||||
#include <utility_ringbuffer.hpp>
|
|
||||||
|
|
||||||
namespace cpptick
|
|
||||||
{
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept TrivialClass = std::is_trivial_v<T> && std::is_trivially_destructible_v<T>;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept CopyableClass = !TrivialClass<T> && std::copy_constructible<T>;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept SlotArgument = TrivialClass<T> || CopyableClass<T>;
|
|
||||||
|
|
||||||
struct ArgStorage
|
|
||||||
{
|
|
||||||
std::array<char, sizeof(int) * 12> buffer;
|
|
||||||
std::array<char*, 12> pointer_buffer;
|
|
||||||
std::array<char*, 12>::iterator pointer_buffer_head;
|
|
||||||
std::size_t space_remaining;
|
|
||||||
|
|
||||||
ArgStorage() = default;
|
|
||||||
ArgStorage(const ArgStorage&) = default;
|
|
||||||
ArgStorage(ArgStorage&&) = default;
|
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
space_remaining = buffer.size();
|
|
||||||
pointer_buffer_head = pointer_buffer.begin();
|
|
||||||
*pointer_buffer_head = buffer.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TrivialClass T>
|
|
||||||
void pushArg(T&& arg)
|
|
||||||
{
|
|
||||||
if (pointer_buffer_head == pointer_buffer.end() || space_remaining == 0)
|
|
||||||
std::exit(1);
|
|
||||||
|
|
||||||
void* memory_location = *pointer_buffer_head;
|
|
||||||
|
|
||||||
if (std::align(alignof(T), sizeof(T), memory_location, space_remaining) == nullptr)
|
|
||||||
std::exit(2);
|
|
||||||
|
|
||||||
std::memcpy(memory_location, &arg, sizeof(T));
|
|
||||||
pointer_buffer_head++;
|
|
||||||
if (pointer_buffer_head != pointer_buffer.end())
|
|
||||||
{
|
|
||||||
*pointer_buffer_head = (char*)memory_location + sizeof(T);
|
|
||||||
space_remaining -= sizeof(T);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<CopyableClass T>
|
|
||||||
void pushArg(T&& arg)
|
|
||||||
{
|
|
||||||
if (pointer_buffer_head == pointer_buffer.end() || space_remaining == 0)
|
|
||||||
std::exit(1);
|
|
||||||
|
|
||||||
void* memory_location = *pointer_buffer_head;
|
|
||||||
|
|
||||||
if (std::align(alignof(T), sizeof(T), memory_location, space_remaining) == nullptr)
|
|
||||||
std::exit(2);
|
|
||||||
|
|
||||||
new (memory_location) T{arg};
|
|
||||||
pointer_buffer_head++;
|
|
||||||
if (pointer_buffer_head != pointer_buffer.end())
|
|
||||||
{
|
|
||||||
*pointer_buffer_head = (char*)memory_location + sizeof(T);
|
|
||||||
space_remaining -= sizeof(T);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TrivialClass T>
|
|
||||||
void cleanUp(const std::size_t)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
template<CopyableClass T>
|
|
||||||
void cleanUp(const std::size_t index)
|
|
||||||
{
|
|
||||||
T* item = reinterpret_cast<T*>(pointer_buffer[index]);
|
|
||||||
item->~T();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args, size_t... Is>
|
|
||||||
void cleanUp(std::index_sequence<Is...>)
|
|
||||||
{
|
|
||||||
(cleanUp<Args>(Is), ...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void cleanUp()
|
|
||||||
{
|
|
||||||
cleanUp<Args...>(std::make_index_sequence<sizeof...(Args)>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<SlotArgument T>
|
|
||||||
T& at(const std::size_t idx)
|
|
||||||
{
|
|
||||||
return *((T*)pointer_buffer[idx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<SlotArgument T>
|
|
||||||
const T& at(const std::size_t idx) const
|
|
||||||
{
|
|
||||||
return *((T*)pointer_buffer[idx]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Scheduler
|
|
||||||
{
|
|
||||||
using StoredCall = std::pair<Utility::IFunction<void (ArgStorage&)>*, ArgStorage>;
|
|
||||||
Utility::Ringbuffer<StoredCall, 12> stored_calls;
|
|
||||||
|
|
||||||
void tick()
|
|
||||||
{
|
|
||||||
if (!stored_calls.empty())
|
|
||||||
{
|
|
||||||
auto* f = stored_calls.begin()->first;
|
|
||||||
auto& args = stored_calls.begin()->second;
|
|
||||||
(*f)(args);
|
|
||||||
stored_calls.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArgStorage& storeCall(Utility::IFunction<void (ArgStorage&)>* call)
|
|
||||||
{
|
|
||||||
// @todo: handle overflow...
|
|
||||||
|
|
||||||
// still constructs double.
|
|
||||||
stored_calls.emplace_back(call, ArgStorage{});
|
|
||||||
auto& storage = stored_calls.back().second;
|
|
||||||
storage.reset();
|
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename>
|
|
||||||
struct Slot;
|
|
||||||
|
|
||||||
template<typename R, SlotArgument... Args>
|
|
||||||
struct Slot<R(Args...)>
|
|
||||||
{
|
|
||||||
std::array<Utility::IFunction<R (Args...)>*, 12> signals;
|
|
||||||
Scheduler* scheduler;
|
|
||||||
|
|
||||||
Utility::FunctionOwned<Slot<R(Args...)>, void (ArgStorage&)> invoke_ptr;
|
|
||||||
|
|
||||||
Slot() = delete;
|
|
||||||
explicit Slot(Scheduler* sched)
|
|
||||||
: scheduler(sched)
|
|
||||||
, invoke_ptr(*this, &Slot<R(Args...)>::callUp)
|
|
||||||
{
|
|
||||||
signals.fill(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Slot(const Slot&) = delete;
|
|
||||||
Slot(Slot&&) = delete;
|
|
||||||
Slot& operator=(const Slot&) = delete;
|
|
||||||
Slot& operator=(Slot&&) = delete;
|
|
||||||
|
|
||||||
void connect(Utility::IFunction<R (Args...)>* signal)
|
|
||||||
{
|
|
||||||
for (auto*& callable : signals)
|
|
||||||
{
|
|
||||||
if (callable == nullptr)
|
|
||||||
{
|
|
||||||
callable = signal;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Utility::IFunction<R (Args...)>* connect(R (*func)(Args...))
|
|
||||||
{
|
|
||||||
auto* f = new Utility::Function<R (Args...)>(func);
|
|
||||||
|
|
||||||
connect(f);
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Source>
|
|
||||||
Utility::IFunction<R (Args...)>* connect(Source& src, R (Source::* func)(Args...))
|
|
||||||
{
|
|
||||||
auto* f = new Utility::FunctionOwned<Source, R (Args...)>(src, func);
|
|
||||||
|
|
||||||
connect(f);
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t... Is>
|
|
||||||
void callUpFunction(Utility::IFunction<R (Args...)>* func, ArgStorage& args, std::index_sequence<Is...>)
|
|
||||||
{
|
|
||||||
(*func)(std::forward<Args>(args.at<Args>(Is))...);
|
|
||||||
}
|
|
||||||
|
|
||||||
void callUpFunction(Utility::IFunction<R (Args...)>* func, ArgStorage& args)
|
|
||||||
{
|
|
||||||
callUpFunction(func, args, std::make_index_sequence<sizeof...(Args)>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
void callUp(ArgStorage& args)
|
|
||||||
{
|
|
||||||
for (auto* f : signals)
|
|
||||||
{
|
|
||||||
if (f)
|
|
||||||
callUpFunction(f, args);
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.cleanUp<Args...>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void invoke(Args... args)
|
|
||||||
{
|
|
||||||
ArgStorage& storage = scheduler->storeCall(&invoke_ptr);
|
|
||||||
(storage.pushArg(std::forward<Args>(args)), ...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
45
CppTick/Inc/cpptick/scheduler.hpp
Normal file
45
CppTick/Inc/cpptick/scheduler.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 24/10/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
#include <utility_function.hpp>
|
||||||
|
#include <utility_ringbuffer.hpp>
|
||||||
|
|
||||||
|
#include "cpptick/argstore.hpp"
|
||||||
|
|
||||||
|
namespace cpptick
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Scheduler
|
||||||
|
{
|
||||||
|
using StoredCall = std::pair<Utility::IFunction<void (ArgStorage&)>*, ArgStorage>;
|
||||||
|
Utility::Ringbuffer<StoredCall, 12> stored_calls;
|
||||||
|
|
||||||
|
void tick()
|
||||||
|
{
|
||||||
|
if (!stored_calls.empty())
|
||||||
|
{
|
||||||
|
auto* f = stored_calls.begin()->first;
|
||||||
|
auto& args = stored_calls.begin()->second;
|
||||||
|
(*f)(args);
|
||||||
|
stored_calls.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArgStorage& storeCall(Utility::IFunction<void (ArgStorage&)>* call)
|
||||||
|
{
|
||||||
|
// @todo: handle overflow...
|
||||||
|
|
||||||
|
// still constructs double.
|
||||||
|
stored_calls.emplace_back(call, ArgStorage{});
|
||||||
|
auto& storage = stored_calls.back().second;
|
||||||
|
storage.reset();
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
106
CppTick/Inc/cpptick/slot.hpp
Normal file
106
CppTick/Inc/cpptick/slot.hpp
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 12/09/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <utility_function.hpp>
|
||||||
|
#include <utility_ringbuffer.hpp>
|
||||||
|
|
||||||
|
#include "cpptick/argstore.hpp"
|
||||||
|
#include "cpptick/scheduler.hpp"
|
||||||
|
|
||||||
|
namespace cpptick
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename>
|
||||||
|
struct Slot;
|
||||||
|
|
||||||
|
template<typename R, SlotArgument... Args>
|
||||||
|
struct Slot<R(Args...)>
|
||||||
|
{
|
||||||
|
std::array<Utility::IFunction<R (Args...)>*, 12> signals;
|
||||||
|
Scheduler* scheduler;
|
||||||
|
|
||||||
|
Utility::FunctionOwned<Slot<R(Args...)>, void (ArgStorage&)> invoke_ptr;
|
||||||
|
|
||||||
|
Slot() = delete;
|
||||||
|
explicit Slot(Scheduler* sched)
|
||||||
|
: scheduler(sched)
|
||||||
|
, invoke_ptr(*this, &Slot<R(Args...)>::callUp)
|
||||||
|
{
|
||||||
|
signals.fill(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Slot(const Slot&) = delete;
|
||||||
|
Slot(Slot&&) = delete;
|
||||||
|
Slot& operator=(const Slot&) = delete;
|
||||||
|
Slot& operator=(Slot&&) = delete;
|
||||||
|
|
||||||
|
void connect(Utility::IFunction<R (Args...)>* signal)
|
||||||
|
{
|
||||||
|
for (auto*& callable : signals)
|
||||||
|
{
|
||||||
|
if (callable == nullptr)
|
||||||
|
{
|
||||||
|
callable = signal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Utility::IFunction<R (Args...)>* connect(R (*func)(Args...))
|
||||||
|
{
|
||||||
|
auto* f = new Utility::Function<R (Args...)>(func);
|
||||||
|
|
||||||
|
connect(f);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Source>
|
||||||
|
Utility::IFunction<R (Args...)>* connect(Source& src, R (Source::* func)(Args...))
|
||||||
|
{
|
||||||
|
auto* f = new Utility::FunctionOwned<Source, R (Args...)>(src, func);
|
||||||
|
|
||||||
|
connect(f);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t... Is>
|
||||||
|
void callUpFunction(Utility::IFunction<R (Args...)>* func, ArgStorage& args, std::index_sequence<Is...>)
|
||||||
|
{
|
||||||
|
(*func)(std::forward<Args>(args.at<Args>(Is))...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callUpFunction(Utility::IFunction<R (Args...)>* func, ArgStorage& args)
|
||||||
|
{
|
||||||
|
callUpFunction(func, args, std::make_index_sequence<sizeof...(Args)>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void callUp(ArgStorage& args)
|
||||||
|
{
|
||||||
|
for (auto* f : signals)
|
||||||
|
{
|
||||||
|
if (f)
|
||||||
|
callUpFunction(f, args);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.cleanUp<Args...>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke(Args... args)
|
||||||
|
{
|
||||||
|
ArgStorage& storage = scheduler->storeCall(&invoke_ptr);
|
||||||
|
(storage.pushArg(std::forward<Args>(args)), ...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <catch2/catch.hpp>
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
#include <cpptick/cpptick.hpp>
|
#include "cpptick/cpptick.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user