forked from bkinnightskytw/event_transformer
feat: experiment map based type erasure.
This commit is contained in:
parent
e0269954ca
commit
da420c625f
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Mozilla
|
||||
AlignAfterOpenBracket: Align
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
#AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
ArrayInitializerAlignmentStyle: Left
|
||||
ColumnLimit: 120
|
||||
IndentWidth: 2
|
||||
# TabWidth: 4
|
||||
UseTab: Never
|
||||
# IndentCaseLabels: false
|
||||
MaxEmptyLinesToKeep: 2
|
||||
BreakBeforeBraces: Mozilla
|
||||
# BreakBeforeBraces: Allman
|
||||
# SpaceBeforeParens: ControlStatements
|
||||
# SpaceBeforeAssignmentOperators: true
|
||||
# SpacesInSquareBrackets: false
|
||||
# SpaceBeforeSquareBrackets: false
|
||||
# SpaceAfterAssignmentOperators: true
|
||||
# SpaceBeforeCpp11BracedList: false
|
||||
# ContinuationIndentWidth: 4
|
||||
UseCRLF: false
|
|
@ -30,3 +30,6 @@
|
|||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
build/
|
||||
*Presets.json
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"~/.conan2/p/caf*/s/src/**",
|
||||
"~/.conan2/p/sobje*/s/src/dev/**",
|
||||
"~/.conan2/p/spdlo*/s/src/**",
|
||||
"~/.conan2/p/gtest*/p/include/**"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cppStandard": "gnu++17",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"cStandard": "c23"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"ms-vscode.cmake-tools",
|
||||
"ms-vscode.cpptools-extension-pack",
|
||||
"mhutchie.git-graph",
|
||||
"ms-vscode-remote.remote-ssh",
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"bierner.markdown-mermaid",
|
||||
"yzhang.markdown-all-in-one",
|
||||
"hediet.vscode-drawio",
|
||||
"cheshirekow.cmake-format",
|
||||
"charliermarsh.ruff"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"*.h": "cpp",
|
||||
"*.hpp": "cpp",
|
||||
"*.cc": "cpp",
|
||||
"*.cpp": "cpp",
|
||||
},
|
||||
"C_Cpp.autoAddFileAssociations": false,
|
||||
"files.exclude": {
|
||||
"**/build": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/build": true
|
||||
},
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(event_transformer CXX)
|
||||
|
||||
add_library(event_transformer INTERFACE)
|
||||
target_include_directories(event_transformer INTERFACE include)
|
||||
|
||||
if(NOT BUILD_TESTING STREQUAL OFF)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
set_target_properties(event_transformer
|
||||
PROPERTIES PUBLIC_HEADER "include/event_transformer.h")
|
||||
install(TARGETS event_transformer)
|
|
@ -1,2 +1,6 @@
|
|||
# event_transformer
|
||||
Simple utility lib to be interface of event system and simplify transform between different evet lib.
|
||||
|
||||
## ref
|
||||
|
||||
- [MCGallaspy/events](https://github.com/MCGallaspy/events/)
|
|
@ -0,0 +1,70 @@
|
|||
from conan import ConanFile
|
||||
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps
|
||||
from conan.tools.build import check_min_cppstd, can_run
|
||||
|
||||
|
||||
class event_transformerRecipe(ConanFile):
|
||||
name = "event_transformer"
|
||||
version = "0.1"
|
||||
package_type = "library"
|
||||
|
||||
# Optional metadata
|
||||
license = "<Put the package license here>"
|
||||
author = "<Put your name here> <And your email here>"
|
||||
url = "<Package recipe repository url here, for issues about the package>"
|
||||
description = "<Description of event_transformer package here>"
|
||||
topics = ("<Put some tag here>", "<here>", "<and here>")
|
||||
|
||||
# Binary configuration
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
options = {"shared": [True, False], "fPIC": [True, False]}
|
||||
default_options = {"shared": False, "fPIC": True}
|
||||
|
||||
# Sources are located in the same place as this recipe, copy them to the recipe
|
||||
exports_sources = "CMakeLists.txt", "src/*", "include/*"
|
||||
|
||||
def requirements(self):
|
||||
"""
|
||||
todo clean up this thing.
|
||||
"""
|
||||
# build
|
||||
self.tool_requires("cmake/[>=3.14 <=3.26]")
|
||||
# test
|
||||
self.test_requires("gtest/[~1.13]")
|
||||
self.test_requires("sobjectizer/[~5.8]")
|
||||
self.test_requires("eventpp/[~0.1]")
|
||||
self.test_requires("sigslot/[~1.2]")
|
||||
self.test_requires("caf/[~0.19]")
|
||||
self.test_requires("spdlog/[~1.13]")
|
||||
|
||||
def validate(self):
|
||||
check_min_cppstd(self, "17")
|
||||
|
||||
def config_options(self):
|
||||
if self.settings.os == "Windows":
|
||||
self.options.rm_safe("fPIC")
|
||||
|
||||
def configure(self):
|
||||
if self.options.shared:
|
||||
self.options.rm_safe("fPIC")
|
||||
|
||||
def layout(self):
|
||||
cmake_layout(self)
|
||||
|
||||
def generate(self):
|
||||
deps = CMakeDeps(self)
|
||||
deps.generate()
|
||||
tc = CMakeToolchain(self)
|
||||
tc.generate()
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure()
|
||||
cmake.build()
|
||||
|
||||
def package(self):
|
||||
cmake = CMake(self)
|
||||
cmake.install()
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.libs = ["event_transformer"]
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
using namespace std;
|
||||
|
||||
template<class... EventTypes>
|
||||
class EventList;
|
||||
|
||||
template<>
|
||||
class EventList<>
|
||||
{};
|
||||
|
||||
/**
|
||||
* @todo addition type but without trigger ambiguous error.
|
||||
*/
|
||||
template<class Event>
|
||||
class EventList<Event> : public virtual EventList<>
|
||||
{
|
||||
public:
|
||||
virtual void dispatch(const Event&){}; //=0
|
||||
// virtual void dispatch(Event&)
|
||||
// {
|
||||
// cout << "default cb\n";
|
||||
// };
|
||||
// virtual void dispatch(const Event)
|
||||
// {
|
||||
// cout << "default cb\n";
|
||||
// };
|
||||
};
|
||||
|
||||
template<class Event, class... Others>
|
||||
class EventList<Event, Others...>
|
||||
: public EventList<Event>
|
||||
, public EventList<Others...>
|
||||
{};
|
|
@ -0,0 +1,3 @@
|
|||
#include <iostream>
|
||||
#include "event_transformer.h"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(PackageTest CXX)
|
||||
|
||||
find_package(event_transformer CONFIG REQUIRED)
|
||||
|
||||
|
||||
|
||||
add_executable(example src/example.cpp)
|
||||
target_link_libraries(example event_transformer::event_transformer)
|
|
@ -0,0 +1,26 @@
|
|||
import os
|
||||
|
||||
from conan import ConanFile
|
||||
from conan.tools.cmake import CMake, cmake_layout
|
||||
from conan.tools.build import can_run
|
||||
|
||||
|
||||
class event_transformerTestConan(ConanFile):
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
generators = "CMakeDeps", "CMakeToolchain"
|
||||
|
||||
def requirements(self):
|
||||
self.requires(self.tested_reference_str)
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure()
|
||||
cmake.build()
|
||||
|
||||
def layout(self):
|
||||
cmake_layout(self)
|
||||
|
||||
def test(self):
|
||||
if can_run(self):
|
||||
cmd = os.path.join(self.cpp.build.bindir, "example")
|
||||
self.run(cmd, env="conanrun")
|
|
@ -0,0 +1,7 @@
|
|||
#include "event_transformer.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
int main() {
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
cmake_minimum_required(VERSION 3.14)
|
||||
include(GoogleTest)
|
||||
find_package(GTest REQUIRED)
|
||||
include_directories(${GTEST_INCLUDE_DIRS})
|
||||
find_package(sobjectizer REQUIRED CONFIG)
|
||||
|
||||
add_executable(evt_trans_tests test_event_basic.cpp)
|
||||
target_link_libraries(evt_trans_tests event_transformer GTest::gtest_main
|
||||
GTest::gmock_main sobjectizer::StaticLib)
|
||||
gtest_discover_tests(evt_trans_tests)
|
|
@ -0,0 +1,255 @@
|
|||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdio.h>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
// #include <source_location>
|
||||
// #include <string_view>
|
||||
#include <chrono>
|
||||
#include <event_transformer.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <so_5/all.hpp>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
|
||||
#define TYPE_NAME(x) #x
|
||||
|
||||
namespace {
|
||||
using namespace std;
|
||||
using namespace ::testing;
|
||||
|
||||
/**
|
||||
* original required by `events` lib
|
||||
*/
|
||||
class BaseEvent
|
||||
{};
|
||||
|
||||
struct AEvent
|
||||
{
|
||||
// const string name = TYPE_NAME(AEvent); //not stable
|
||||
double speed; // parameter of event
|
||||
};
|
||||
struct BEvent : public BaseEvent
|
||||
{
|
||||
// const string name = TYPE_NAME(BEvent); //not stable
|
||||
};
|
||||
struct DEvent : public AEvent
|
||||
{}; // Events can be arbitrarily derived.
|
||||
|
||||
class Foo : public EventList<AEvent, BEvent>
|
||||
{
|
||||
public:
|
||||
void dispatch(const AEvent& evt) override
|
||||
{
|
||||
cout << "Foo is handling AEvent" << endl;
|
||||
}
|
||||
void dispatch(const BEvent& evt) override
|
||||
{
|
||||
cout << "Foo is handling BEvent" << endl;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Foo_pp : public Foo
|
||||
{
|
||||
string actor = "foo";
|
||||
|
||||
public:
|
||||
// for c++ 17
|
||||
template<typename EVT>
|
||||
void dispatch(EVT evt)
|
||||
{
|
||||
// cout << "this is additional wrapper from event name: " << evt.name << "\n";
|
||||
cout << "this is additional wrapper from event name: "
|
||||
<< "\n";
|
||||
static_cast<EventList<decltype(evt)>&>(*this).dispatch(evt);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Evt_trans_test : public Test
|
||||
{
|
||||
protected:
|
||||
const AEvent a_ev = AEvent();
|
||||
const BEvent b_ev = BEvent();
|
||||
};
|
||||
TEST_F(Evt_trans_test, create_test)
|
||||
{
|
||||
Foo foo;
|
||||
// static_cast<EventList<AEvent>>(foo).dispatch(a_ev);
|
||||
// static_cast<Foo>(foo).dispatch(a_ev);
|
||||
static_cast<EventList<AEvent>&>(foo).dispatch(a_ev);
|
||||
static_cast<EventList<BEvent>&>(foo).dispatch(b_ev);
|
||||
// foo.dispatch(a_ev);
|
||||
foo.dispatch(std::ref(a_ev));
|
||||
|
||||
printf("\n=======================splitter=======================\n\n");
|
||||
Foo_pp foo_pp;
|
||||
// static_cast<EventList<AEvent>&>(foo).dispatch(a_ev);
|
||||
foo_pp.dispatch(b_ev);
|
||||
};
|
||||
|
||||
/********************************************************************************************************/
|
||||
#include <any>
|
||||
#include <map>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
|
||||
/**
|
||||
* @details
|
||||
* I want this be interface only.
|
||||
* @todo require a dynamic map to map from rtti typeinfo to coresponding function。
|
||||
*/
|
||||
class Fake_module_process_ev : public EventList<AEvent, BEvent>
|
||||
{
|
||||
// struct Concept
|
||||
// { // (5)
|
||||
// virtual ~Concept()
|
||||
// {
|
||||
// }
|
||||
// virtual void dispatch() = 0;
|
||||
// };
|
||||
|
||||
// std::unique_ptr<Concept> pimpl_;
|
||||
|
||||
// template<typename T> // (6)
|
||||
// struct Model : Concept
|
||||
// {
|
||||
// Model(const T& t)
|
||||
// : object(t)
|
||||
// {
|
||||
// }
|
||||
// void dispatch() override
|
||||
// {
|
||||
// return object.dispatch();
|
||||
// }
|
||||
|
||||
// private:
|
||||
// T object;
|
||||
// };
|
||||
|
||||
|
||||
public:
|
||||
// /**
|
||||
// * @todo type erase
|
||||
// * @see https://www.hmoonotes.org/2023/06/cpp-type-erasure.html
|
||||
// * @see https://www.modernescpp.com/index.php/type-erasure/
|
||||
// */
|
||||
// // template<typename EVT>
|
||||
// // void dispatch(EVT evt)
|
||||
// // {
|
||||
// // // I want to override this. (type erase)
|
||||
// // std::cout << "base interface\n";
|
||||
// // };
|
||||
template<typename EVT>
|
||||
void dispatch(EVT evt)
|
||||
{
|
||||
using namespace std;
|
||||
// for (auto const& [key, val] : fmap) {
|
||||
// }
|
||||
auto func = std::any_cast<std::function<void(EVT&)>>(fmap.at(type_index(typeid(EVT))));
|
||||
func(evt); // dispatch
|
||||
};
|
||||
|
||||
protected:
|
||||
// void (*)(std::any &);
|
||||
std::map<std::type_index, std::any> fmap;
|
||||
}; // Fake_module_process_ev
|
||||
|
||||
/**
|
||||
* 用friend 會變成 `dispatch(foo, a_event);` 而非 `foo.dispatch(a_event);`
|
||||
*/
|
||||
|
||||
/**
|
||||
* I wan't this be implement.
|
||||
* 模板派生类
|
||||
*/
|
||||
// template<typename Functor>
|
||||
class So5_fake_module_process_ev : virtual public Fake_module_process_ev
|
||||
{
|
||||
so_5::mbox_t target;
|
||||
|
||||
public:
|
||||
So5_fake_module_process_ev(so_5::mbox_t target_)
|
||||
: target(target_)
|
||||
{
|
||||
using namespace std;
|
||||
// iterate over all the inherit class and generate function from functor.
|
||||
//
|
||||
std::function<void(AEvent&)> func = [this](AEvent& evt) -> void { so_5::send<AEvent>(target); };
|
||||
fmap[type_index(typeid(AEvent))] = std::any(func);
|
||||
// Functor<>;
|
||||
}
|
||||
// template<typename EVT>
|
||||
// void dispatch(EVT evt)
|
||||
// {
|
||||
// // cout << "this is additional wrapper from event name: " << evt.name << "\n";
|
||||
// cout << "this is additional wrapper from event name: "
|
||||
// << "\n";
|
||||
// // static_cast<EventList<decltype(evt)>&>(*this).dispatch(evt);
|
||||
// so_5::send<EVT>(target);
|
||||
// }
|
||||
};
|
||||
|
||||
/**
|
||||
* simulate module process which require event interface
|
||||
*/
|
||||
void
|
||||
fake_module_process(Fake_module_process_ev* evt_li_ptr)
|
||||
// fake_module_process(EventList<AEvent, BEvent>* evt_li_ptr)
|
||||
{
|
||||
std::cout << "fake module called\n";
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
const AEvent a_ev = AEvent{ .speed = 5.0 };
|
||||
evt_li_ptr->dispatch(a_ev);
|
||||
// static_cast<EventList<decltype(a_ev)>&>(*evt_li_ptr).dispatch(a_ev);
|
||||
std::cout << "fake module exist\n";
|
||||
//
|
||||
}
|
||||
class pinger final : public so_5::agent_t
|
||||
{
|
||||
so_5::mbox_t ponger_;
|
||||
|
||||
void on_pong(mhood_t<AEvent> cmd)
|
||||
{
|
||||
std::cout << "actor pinger: received a A event\n";
|
||||
}
|
||||
|
||||
public:
|
||||
pinger(context_t ctx)
|
||||
: so_5::agent_t{ std::move(ctx) }
|
||||
{
|
||||
}
|
||||
|
||||
void set_ponger(const so_5::mbox_t mbox)
|
||||
{
|
||||
ponger_ = mbox;
|
||||
}
|
||||
|
||||
void so_define_agent() override
|
||||
{
|
||||
so_subscribe_self().event(&pinger::on_pong);
|
||||
}
|
||||
};
|
||||
TEST_F(Evt_trans_test, so5_fake_module_test)
|
||||
{
|
||||
// using namespace std::literals;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
so_5::launch([](so_5::environment_t& env) {
|
||||
so_5::mbox_t m;
|
||||
env.introduce_coop([&m](so_5::coop_t& coop) {
|
||||
auto pinger_actor = coop.make_agent<pinger>();
|
||||
m = pinger_actor->so_direct_mbox();
|
||||
});
|
||||
So5_fake_module_process_ev ev_sys(m);
|
||||
fake_module_process(&ev_sys);
|
||||
// pause(200ms);
|
||||
std::this_thread::sleep_for(200ms);
|
||||
|
||||
std::cout << "Stopping..." << std::endl;
|
||||
env.stop();
|
||||
}); // end of so5 launch
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue