[ox] Move each library's include to its own include dir
Build / build (push) Failing after 21s

This commit is contained in:
2026-05-06 01:06:34 -05:00
parent bd792a0d25
commit d4807cd2a0
191 changed files with 248 additions and 208 deletions
+51
View File
@@ -0,0 +1,51 @@
add_library(
OxEvent
src/signal.cpp
)
if(NOT MSVC)
target_compile_options(OxEvent PRIVATE -Wsign-conversion)
target_compile_options(OxEvent PRIVATE -Wconversion)
endif()
if(NOT OX_BARE_METAL)
set_property(
TARGET
OxEvent
PROPERTY
POSITION_INDEPENDENT_CODE ON
)
endif()
target_compile_definitions(
OxEvent PUBLIC
$<$<BOOL:${OX_USE_STDLIB}>:OX_USE_STDLIB>
$<$<BOOL:${OX_NODEBUG}>:OX_NODEBUG>
)
target_link_libraries(
OxEvent PUBLIC
OxStd
)
target_include_directories(
OxEvent PUBLIC
include
)
install(
DIRECTORY
include/ox
DESTINATION
include
)
install(
TARGETS OxEvent
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(OX_RUN_TESTS)
add_subdirectory(test)
endif()
+11
View File
@@ -0,0 +1,11 @@
/*
* Copyright 2015 - 2025 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "signal.hpp"
+416
View File
@@ -0,0 +1,416 @@
/*
* Copyright 2015 - 2025 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <ox/std/assert.hpp>
#include <ox/std/def.hpp>
#include <ox/std/defines.hpp>
#include <ox/std/error.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/trace.hpp>
#include <ox/std/vector.hpp>
namespace ox {
class SignalHandler;
#ifndef OX_OS_BareMetal
namespace detail {
template<typename T>
struct isError {
static constexpr bool value = false;
};
template<>
struct isError<Error> {
static constexpr bool value = true;
};
}
template<class... Args>
class Signal {
protected:
struct BaseSlot {
virtual ~BaseSlot() = default;
virtual void call(Args...) = 0;
virtual void cleanup(Signal*) noexcept {}
[[nodiscard]]
virtual const void *receiver() const noexcept { return nullptr; }
};
template<typename F>
struct FunctionSlot: public BaseSlot {
F f;
explicit FunctionSlot(F f) {
this->f = f;
}
void call(Args... args) final {
if constexpr(detail::isError<decltype(f(args...))>::value) {
OX_THROW_ERROR(f(args...));
} else {
f(args...);
}
}
};
template<typename T, typename Method>
struct MethodSlot: public BaseSlot {
T m_receiver = nullptr;
Method m_methodPtr;
MethodSlot(T receiver, Method methodPtr) {
m_receiver = receiver;
m_methodPtr = methodPtr;
}
void call(Args... args) final {
if constexpr(detail::isError<decltype((m_receiver->*(m_methodPtr))(args...))>::value) {
OX_THROW_ERROR((m_receiver->*(m_methodPtr))(args...));
} else {
f(args...);
}
}
void cleanup(Signal *signal) noexcept final {
std::ignore = m_receiver->destruction.disconnectSignal(signal);
//if (err) {
// oxErrorf("Signal could not notify receiver that it is being destroyed. Destruction of receiver will cause use-after-free. ({})", toStr(err));
//}
}
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
template<typename SignalT, typename Method>
struct SignalMethodSlot: public BaseSlot {
const SignalT *m_receiver = nullptr;
Method m_methodPtr;
SignalMethodSlot(const SignalT *receiver, Method methodPtr) {
m_receiver = receiver;
m_methodPtr = methodPtr;
}
void call(Args... args) final {
if constexpr(detail::isError<decltype((m_receiver->*(m_methodPtr))(args...))>::value) {
OX_THROW_ERROR((m_receiver->*(m_methodPtr))(args...));
} else {
(m_receiver->*(m_methodPtr))(args...);
}
}
void cleanup(Signal*) noexcept final {
}
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
mutable Vector<UPtr<BaseSlot>> m_slots;
public:
~Signal() noexcept;
void connect(Error(*f)(Args...)) const noexcept;
template<typename T, typename Method>
void connect(const T *receiver, Method methodPtr) const noexcept;
template<typename T, typename Method>
void connect(T *receiver, Method methodPtr) const noexcept;
template<class... SubArgs, typename Method>
void connect(const Signal<SubArgs...> *receiver, Method methodPtr) const noexcept;
template<class... SubArgs>
Error disconnectSignal(const Signal<SubArgs...> *receiver) const noexcept;
Error disconnectObject(const void *receiver) const noexcept;
[[nodiscard]]
size_t connectionCnt() const noexcept {
return m_slots.size();
}
void emit(Args... args) const;
Error emitCheckError(Args... args) const noexcept;
};
extern template class Signal<const SignalHandler*>;
template<class... Args>
Signal<Args...>::~Signal() noexcept {
for (auto &slot : m_slots) {
slot->cleanup(this);
}
}
template<class... Args>
void Signal<Args...>::connect(Error(*f)(Args...)) const noexcept {
m_slots.emplace_back(new FunctionSlot<decltype(f)>(f));
}
template<class... Args>
template<typename T, typename Method>
void Signal<Args...>::connect(const T *receiver, Method methodPtr) const noexcept {
receiver->destruction.connect(this, &Signal<Error(Args...)>::disconnectObject);
m_slots.emplace_back(new MethodSlot<const T*, Method>(receiver, methodPtr));
}
template<class... Args>
template<typename T, typename Method>
void Signal<Args...>::connect(T *receiver, Method methodPtr) const noexcept {
receiver->destruction.connect(this, &Signal<Error(Args...)>::disconnectObject);
m_slots.emplace_back(new MethodSlot<T*, Method>(receiver, methodPtr));
}
template<class... Args>
template<class... SubArgs, typename Method>
void Signal<Args...>::connect(const Signal<SubArgs...> *receiver, Method methodPtr) const noexcept {
m_slots.emplace_back(new SignalMethodSlot<Signal<SubArgs...>, Method>(receiver, methodPtr));
}
template<class... Args>
template<class... SubArgs>
Error Signal<Args...>::disconnectSignal(const Signal<SubArgs...> *receiver) const noexcept {
return disconnectObject(receiver);
}
template<class... Args>
Error Signal<Args...>::disconnectObject(const void *receiver) const noexcept {
for (auto i = 0u; i < m_slots.size(); ++i) {
const auto &slot = m_slots[i];
if (slot->receiver() == receiver) {
OX_RETURN_ERROR(m_slots.erase(i));
--i;
}
}
return ox::Error(1, "Signal::disconnectObject: Receiver was not found among this Signal's slots");
}
template<class... Args>
void Signal<Args...>::emit(Args... args) const {
for (auto &f : m_slots) {
f->call(args...);
}
}
template<class... Args>
Error Signal<Args...>::emitCheckError(Args... args) const noexcept {
try {
for (auto &f : m_slots) {
f->call(args...);
}
return {};
} catch (const ox::Exception &ex) {
return ox::Error(ex.errCode, ex.msg, ex.src);
}
}
#else
template<typename T>
class Signal;
#endif
template<class... Args>
class Signal<Error(Args...)> {
protected:
struct BaseSlot {
virtual ~BaseSlot() = default;
virtual Error call(Args...) noexcept = 0;
virtual void cleanup(Signal*) noexcept {}
[[nodiscard]]
virtual const void *receiver() const noexcept { return nullptr; }
};
struct FunctionSlot: public BaseSlot {
Error (*f)(Args...);
explicit FunctionSlot(Error (*f)(Args...)) noexcept {
this->f = f;
}
Error call(Args... args) noexcept final {
return f(ox::forward<Args>(args)...);
}
};
template<typename T, typename Method>
struct MethodSlot: public BaseSlot {
T m_receiver = nullptr;
Method m_methodPtr;
MethodSlot(T receiver, Method methodPtr) {
m_receiver = receiver;
m_methodPtr = methodPtr;
}
Error call(Args... args) noexcept final {
return (m_receiver->*(m_methodPtr))(ox::forward<Args>(args)...);
}
void cleanup(Signal *signal) noexcept final {
std::ignore = m_receiver->destruction.disconnectSignal(signal);
//oxErrorf("{}", toStr(err));
//oxAssert(err, "Signal could not notify receiver that it is being destroyed. Destruction of receiver will cause use-after-free.");
}
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
template<typename SignalT, typename Method>
struct SignalMethodSlot: public BaseSlot {
const SignalT *m_receiver = nullptr;
Method m_methodPtr;
SignalMethodSlot(const SignalT *receiver, Method methodPtr) {
m_receiver = receiver;
m_methodPtr = methodPtr;
}
Error call(Args... args) noexcept final {
return (m_receiver->*(m_methodPtr))(ox::forward<Args>(args)...);
}
void cleanup(Signal*) noexcept final {
}
[[nodiscard]]
const void *receiver() const noexcept final {
return m_receiver;
}
};
mutable Vector<UPtr<BaseSlot>> m_slots;
public:
~Signal() noexcept;
void connect(Error(*f)(Args...)) const noexcept;
template<typename T, typename Method>
void connect(const T *receiver, Method methodPtr) const noexcept;
template<typename T, typename Method>
void connect(T *receiver, Method methodPtr) const noexcept;
template<class... SubArgs, typename Method>
void connect(const Signal<SubArgs...> *receiver, Method methodPtr) const noexcept;
template<class... SubArgs>
Error disconnectSignal(const Signal<SubArgs...> *receiver) const noexcept;
Error disconnectObject(const void *receiver) const noexcept;
[[nodiscard]]
size_t connectionCnt() const noexcept {
return m_slots.size();
}
void emit(Args... args) const noexcept;
Error emitCheckError(Args... args) const noexcept;
};
extern template class Signal<Error(const SignalHandler*)>;
class SignalHandler {
public:
Signal<Error(const SignalHandler*)> destruction;
constexpr SignalHandler() noexcept = default;
SignalHandler(const SignalHandler&) = delete;
SignalHandler(SignalHandler&) = delete;
SignalHandler(SignalHandler&&) = delete;
virtual ~SignalHandler() noexcept;
};
template<class... Args>
Signal<Error(Args...)>::~Signal() noexcept {
for (auto &slot : m_slots) {
slot->cleanup(this);
}
}
template<class... Args>
void Signal<Error(Args...)>::connect(Error(*f)(Args...)) const noexcept {
m_slots.emplace_back(new FunctionSlot(f));
}
template<class... Args>
template<typename T, typename Method>
void Signal<Error(Args...)>::connect(const T *receiver, Method methodPtr) const noexcept {
receiver->destruction.connect(this, &Signal<Error(Args...)>::disconnectObject);
m_slots.emplace_back(new MethodSlot<const T*, Method>(receiver, methodPtr));
}
template<class... Args>
template<typename T, typename Method>
void Signal<Error(Args...)>::connect(T *receiver, Method methodPtr) const noexcept {
receiver->destruction.connect(this, &Signal<Error(Args...)>::disconnectObject);
m_slots.emplace_back(new MethodSlot<T*, Method>(receiver, methodPtr));
}
template<class... Args>
template<class... SubArgs, typename Method>
void Signal<Error(Args...)>::connect(const Signal<SubArgs...> *receiver, Method methodPtr) const noexcept {
m_slots.emplace_back(new SignalMethodSlot<Signal<SubArgs...>, Method>(receiver, methodPtr));
}
template<class... Args>
template<class... SubArgs>
Error Signal<Error(Args...)>::disconnectSignal(const Signal<SubArgs...> *receiver) const noexcept {
return disconnectObject(receiver);
}
template<class... Args>
Error Signal<Error(Args...)>::disconnectObject(const void *receiver) const noexcept {
for (auto i = 0u; i < m_slots.size(); ++i) {
const auto &slot = m_slots[i];
if (slot->receiver() == receiver) {
OX_RETURN_ERROR(m_slots.erase(i));
--i;
}
}
return ox::Error(1, "Signal::disconnectObject: Receiver was not found among this Signal's slots");
}
template<class... Args>
void Signal<Error(Args...)>::emit(Args... args) const noexcept {
for (auto &f : m_slots) {
std::ignore = f->call(ox::forward<Args>(args)...);
}
}
template<class... Args>
Error Signal<Error(Args...)>::emitCheckError(Args... args) const noexcept {
for (auto &f : m_slots) {
OX_RETURN_ERROR(f->call(ox::forward<Args>(args)...));
}
return {};
}
}
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright 2015 - 2025 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <ox/event/signal.hpp>
namespace ox {
#ifndef OX_OS_BareMetal
template class Signal<const SignalHandler*>;
#endif
template class Signal<Error(const SignalHandler*)>;
SignalHandler::~SignalHandler() noexcept {
destruction.emit(this);
}
}
/*
* 1. ProjectExplorer::fileOpened cannot notify StudioUI that it is being destroyed.
* 2. StudioUI tries to unsubscribe from ProjectExplorer::fileOpened upon its destruction.
* 3. Segfault
*/
+10
View File
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.10)
add_executable(
EventTest
tests.cpp
)
target_link_libraries(EventTest OxEvent)
add_test("[ox/event] Test 1" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/EventTest "test1")
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright 2015 - 2025 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#undef NDEBUG
#include <map>
#include <functional>
#include <ox/event/signal.hpp>
#include <ox/std/std.hpp>
struct TestStruct: public ox::SignalHandler {
int value = 0;
ox::Error method(int i) noexcept {
value = i;
return ox::Error(0);
}
};
std::map<ox::StringView, std::function<ox::Error()>> tests = {
{
"test1",
[] {
ox::Signal<ox::Error(int)> signal;
signal.connect([](int i) -> ox::Error {
return ox::Error(i != 5);
});
TestStruct ts;
signal.connect(&ts, &TestStruct::method);
OX_RETURN_ERROR(signal.emitCheckError(5));
OX_RETURN_ERROR(ox::Error(ts.value != 5));
return ox::Error(0);
}
},
};
int main(int argc, const char **args) {
if (argc < 2) {
oxError("Must specify test to run");
}
auto const testName = args[1];
auto const func = tests.find(testName);
if (func != tests.end()) {
oxAssert(func->second(), "Test returned Error");
return 0;
}
return -1;
}