13 Commits

7 changed files with 177 additions and 127 deletions

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.27)
cmake_minimum_required(VERSION 3.18..3.27)
project(Event
VERSION 1.0
LANGUAGES CXX

View File

@@ -0,0 +1,98 @@
# Event
### A C++20 Event system inspired by C# syntax.
![](https://img.shields.io/badge/Redacted-Software-black)
![Static Badge](https://img.shields.io/badge/Lit-Based-%20)
![C++](https://img.shields.io/badge/C%2B%2B-20-blue)
[![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue)](http://unlicense.org/)
## Features
* Fast
* (TODO: Profile!)
* Flexible
* Event delegate can be of any ![FunctionObject](https://en.cppreference.com/w/cpp/named_req/FunctionObject) type.
* Supports any set of parameters via templates.
* Easy To Use
* Written in portable and standard C++, no hacks or quirks.
* Utilizes Modern C++ features for elegant design.
* Zero dependencies. Fully self-contained.
* Unrestricted
* No GNU Copyleft communism. Totally Free & Public Domain.
## Coming Soon
* Event Filters / Policy
* Asynchronous
* Thread-Safe Version
* Priority Queue
* Auto disconnecting
## Supported Compilers
* GCC 14.2.1 (RedHat)
* MSVC 19.40
## Usage
This project is designed to be integrated via [CMake Package Manager](https://github.com/cpm-cmake/CPM.cmake).
```cmake
CPMAddPackage(
NAME Event
URL https://git.redacted.cc/josh/Event/archive/Release-7.zip
)
```
This snippet will automatically download a stable release of Event into your project's build directory, so it won't clutter your sources.
Take note of the release version and make sure it's the latest available in the Releases tab above.
```cmake
target_include_directories(${PROJECT_NAME} PUBLIC ${Event_SOURCE_DIR}/include)
target_link_libraries(${PROJECT_NAME} PUBLIC Event)
```
This snippet will import the headers, and then link the library to your library/executable. You should now be able to use Event.
The C++ snippet below reads input from the terminal and fires an event that received the input message.
```cpp
#include <Event.h>
#include <EventConnection.h>
void ReadMessage(const std::string& message) {
std::cout << "Received: " << message << std::endl;
}
int main()
{
Event<std::string> OnInput; // This and the declaration below are equivalent.
BasicEvent<std::function<void(std::string)>, std::string> OnInput2;
bool running = true;
while (running) {
std::string input;
std::cin >> input;
OnInput.Invoke(input);
OnInput(input); // Call operator is defined as well.
}
return 0;
}
More advanced examples can be found in the main.cpp file.
```
## Documentation
Documentation is automatically generated from latest commit and is hosted at https://doc.redacted.cc/event .
## Contributing
Contributions to this project are welcome! If you find a bug, have a feature request, or would like to contribute code, please submit an issue or pull request to the GitHub repository.
## License
Event is expressly licensed to the Public Domain under the Unlicense. See the LICENSE file for details.
## Acknowledgements
Event is developed and maintained by Joshua O'Leary @ Redacted Software, and contributors.
Special thanks to Maxine Hayes.

View File

@@ -1,40 +0,0 @@
#pragma once
#include <functional>
//template< class RT, class CallableT, class... ArgTypes >
template<class CallableT, class... ArgTypes >
class Callable {
static_assert(std::is_invocable_v<CallableT(ArgTypes...)>);
using invoke = CallableT;
public:
invoke;
//CallableT invoke = CallableT();
//ArgTypes... args
//invoke() { /*return CallableT(args...);*/ return CallableT(RT()); }
};
template< class R, class Fn, class... ArgTypes >
class Cunt {
static_assert(std::is_invocable_r_v<R, Fn, ArgTypes...>);
};
template< class RT, class... ArgTypes>
class EventBase {
public:
//using delegate = CallableT;
using delegate = std::function<RT(ArgTypes...)>;
public:
RT Invoke(ArgTypes... args);
RT operator()(ArgTypes... args);
protected:
delegate callback;
};
};

View File

@@ -1,8 +1,8 @@
/// @file BasicEvent.hpp
/// @description Templated BasicEvent Hook Class modeled after C# events
/// @file Connection.hpp
/// @description Callback handler for event connections
/// @author Josh O'Leary - Redacted Software
/// @revision 3
/// @lastedit 2024-06-01
/// @lastedit 2024-02-21
/// @license Unlicense - Public Domain
#pragma once
@@ -11,60 +11,101 @@
#include <functional>
#include <memory>
// TODO: ThreadSafeEvent / AsyncEvent version of the class.
// TODO: ConsumableEvent - a version that can be "consumed" by a callback by returning true. It will be sunk and not passed to further callbacks.
///
template <typename delegate, typename ... Args>
class Connection;
/// BasicEvent is a templated class that can be used to raise events that can be listened in on
/// at other points in a codebase without having to tightly couple the two pieces of code.
/// @param delegate A type that can be invoked via operator() 'Call operator'.
/// @param Args... The arguments that will be expected by this event and passed to it's delegate.
/// A delegate can be any function object or function-like object. The Args are the arguments accepted by that object
template <typename delegate, typename ... Args>
class BasicEvent {
public:
using connection = Connection<delegate, Args ...>;
using event_ptr = std::shared_ptr<connection>;
/// A type that represents a handle to an active event connection.
class Connection {
public:
Connection(BasicEvent<delegate, Args...> *creator, delegate cb);
bool Disconnect(); // Breaks the event connection, but does not destroy the instance
// If improperly used could lead to run away events? - Maxine
void Invoke(Args... e);
public:
// This is made public in the case of function-like objects which store extra data.
// This allows us to access members of the object when desired. See JTest's usage of the event system for an example.
delegate callback;
private:
BasicEvent<delegate, Args...> *owner;
bool active = true;
};
using EventPtr = std::shared_ptr<Connection>;
public:
void Await(Args& ... arg);
// See JTest Unit for why making this virtual is convenient
virtual void Invoke(Args... args);
void operator()(Args... args);
connection Connect(delegate callback);
void Disconnect(connection &conn);
connection operator+=(delegate callback);
Connection Connect(delegate callback);
void Disconnect(Connection &conn);
Connection operator+=(delegate callback);
/// Also support binding callbacks that don't read in arguments, as a shorthand.
// TODO: No need to wrap the callback if the template args type is void.
Connection Connect(const std::function<void()>& default_callback);
/// Also support binding callbacks that don't read in arguments, as a shorthand.
// TODO: No need to wrap the callback if the template args type is void.
Connection operator += (const std::function<void()>& default_callback);
protected:
std::vector<event_ptr> listeners;
//uint64_t listenerCounter = 0;
std::vector<EventPtr> listeners;
};
template<typename delegate, typename... Args>
Connection<delegate, Args...> BasicEvent<delegate, Args...>::operator+=(delegate callback) { return Connect(callback); }
BasicEvent<delegate, Args...>::Connection::Connection(BasicEvent<delegate, Args...> *creator, delegate cb) : owner(creator), callback(std::move(cb)) {}
template <typename delegate, typename... Args>
void BasicEvent<delegate, Args...>::Connection::Invoke(Args... e) { callback(e...); }
template <typename delegate, typename ... Args>
bool BasicEvent<delegate, Args...>::Connection::Disconnect() {
if (active) {
owner->Disconnect(this);
active = false;
return true;
}
return false;
}
template<typename delegate, typename... Args>
void BasicEvent<delegate, Args...>::operator()(Args... args) { Invoke(args...);}
BasicEvent<delegate, Args...>::Connection BasicEvent<delegate, Args...>::operator+=(delegate callback) { return Connect(callback); }
template<typename delegate, typename ... Args>
typename BasicEvent<delegate, Args...>::Connection BasicEvent<delegate, Args...>::Connect(
const std::function<void()> &default_callback) {
delegate wrapper = [default_callback] (Args... args) { default_callback(); };
return Connect(wrapper);
}
template<typename delegate, typename ... Args>
typename BasicEvent<delegate, Args...>::Connection BasicEvent<delegate, Args...>::operator+=(
const std::function<void()> &default_callback) {
return Connect(default_callback);
}
template<typename delegate, typename... Args>
void BasicEvent<delegate, Args...>::operator()(Args... args) { Invoke(args...); }
template <typename delegate, typename ... Args>
void BasicEvent<delegate, Args...>::Invoke(Args... args) {
for (event_ptr &connection_ptr: this->listeners) {
for (EventPtr &connection_ptr: this->listeners) {
connection_ptr->Invoke(args...);
}
}
template <typename delegate, typename ... Args>
Connection<delegate, Args...> BasicEvent<delegate, Args...>::Connect(delegate callback)
{
event_ptr retval(new connection(this, callback));
BasicEvent<delegate, Args...>::Connection BasicEvent<delegate, Args...>::Connect(delegate callback) {
EventPtr retval(new Connection(this, callback));
this->listeners.push_back(retval);
return *retval;
}
template <typename delegate, typename ... Args>
void BasicEvent<delegate, Args...>::Disconnect(connection &conn) {
void BasicEvent<delegate, Args...>::Disconnect(BasicEvent<delegate, Args...>::Connection &conn) {
listeners.erase(std::remove(listeners.begin(), listeners.end(), 99), listeners.end());
}
/// Event is a generic event type. It is more often we will be using a void function rather than a function-like object.
template <typename ... Args>
using Event = BasicEvent<std::function<void(Args...)>, Args...>;

View File

@@ -1,47 +0,0 @@
/// @file Connection.hpp
/// @description Callback handler for event connections
/// @author Josh O'Leary - Redacted Software
/// @revision 3
/// @lastedit 2024-02-21
/// @license Unlicense - Public Domain
#pragma once
#include <functional>
#include <Event.h>
template <typename delegate, typename ... Args>
class BasicEvent;
/// A type that represents a handle to an active event connection.
template <typename delegate, typename ... Args>
class Connection {
private:
public:
Connection(BasicEvent<delegate, Args...> *creator, delegate cb);
bool Disconnect(); // Breaks the event connection, but does not destroy the instance
void Invoke(Args... e);
protected:
delegate callback;
private:
BasicEvent<delegate, Args...> * owner;
//delegate callback;
bool active = true;
};
template<typename delegate, typename... Args>
Connection<delegate, Args...>::Connection(BasicEvent<delegate, Args...> *creator, delegate cb) : owner(creator), callback(std::move(cb)) {}
template <typename delegate, typename... Args>
void Connection<delegate, Args...>::Invoke(Args... e) { callback(e...); }
template <typename delegate, typename ... Args>
bool Connection<delegate, Args...>::Disconnect() {
if (active) {
owner->Disconnect(this);
active = false;
return true;
}
return false;
}

View File

@@ -1,23 +1,18 @@
#include <iostream>
#include <functional>
#include <Event.h>
#include <EventConnection.h>
//#include <EventConnection.h>
//#include "bullshit.h"
#include "stack"
void ProcessMessage(const std::string& message)
{
std::cout << "Received: " << message << std::endl;
}
// TODO: Event tests here.
// TODO: BasicEvent tests here.
int main() {
Event<int> cum;
cum += [] (int i) { std::cout << i+5 << std::endl; };
cum.Invoke(69);
BasicEvent<std::function<void(std::string)>, std::string> OnMessage;
BasicEvent<std::function<void()>> VoidDelegate;
@@ -29,6 +24,10 @@ int main() {
};
auto handler2 = OnMessage += ProcessMessage;
auto handler3 = OnMessage += [&] {
std::cout << "Connect to event, with default callback, if we don't care about using the provided arguments." << std::endl;
};
while (run)
{
std::string input;

View File

@@ -1 +0,0 @@
#include <EventConnection.h>