├── src ├── EMPTY │ ├── EMPTY.cpp │ └── CMakeLists.txt ├── test │ ├── 03_Signal │ │ ├── CMakeLists.txt │ │ └── main.cpp │ ├── 04_dirty │ │ ├── CMakeLists.txt │ │ └── main.cpp │ ├── 00_Visitor │ │ ├── CMakeLists.txt │ │ └── main.cpp │ └── 02_vtable │ │ ├── 00_basic │ │ ├── CMakeLists.txt │ │ └── main.cpp │ │ └── 01_heapobj │ │ ├── CMakeLists.txt │ │ └── main.cpp └── core │ └── CMakeLists.txt ├── config └── Config.cmake.in ├── data ├── attr.h ├── attr_app_out.h └── attr_out.h ├── include └── UDP │ ├── Visitor │ ├── Visitor.h │ ├── ncVisitor.h │ ├── cVisitor.h │ ├── ncincVisitor.h │ ├── cincVisitor.h │ ├── ncicVisitor.h │ ├── cicVisitor.h │ └── details │ │ ├── Visitor.inl │ │ ├── ncincVisitor.inl │ │ ├── cincVisitor.inl │ │ ├── ncicVisitor.inl │ │ ├── cicVisitor.inl │ │ ├── ncVisitor.inl │ │ └── cVisitor.inl │ ├── Signal │ ├── Signal.h │ ├── Connection.h │ └── details │ │ └── Signal.inl │ └── Basic │ ├── details │ ├── CustomID.inl │ └── vtable.inl │ ├── CustomID.h │ ├── vtable.h │ ├── Dirty.h │ └── Read.h ├── CMakeLists.txt ├── README.md ├── cmake └── InitUCMake.cmake └── .gitignore /src/EMPTY/EMPTY.cpp: -------------------------------------------------------------------------------- 1 | int main() {} -------------------------------------------------------------------------------- /src/test/03_Signal/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | Ubpa_GetTargetName(core "${PROJECT_SOURCE_DIR}/src/core") 2 | Ubpa_AddTarget( 3 | TEST 4 | MODE EXE 5 | LIB 6 | ${core} 7 | ) 8 | -------------------------------------------------------------------------------- /src/test/04_dirty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | Ubpa_GetTargetName(core "${PROJECT_SOURCE_DIR}/src/core") 2 | Ubpa_AddTarget( 3 | TEST 4 | MODE EXE 5 | LIB 6 | ${core} 7 | ) 8 | -------------------------------------------------------------------------------- /src/test/00_Visitor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | Ubpa_GetTargetName(core "${PROJECT_SOURCE_DIR}/src/core") 2 | Ubpa_AddTarget( 3 | TEST 4 | MODE EXE 5 | LIB 6 | ${core} 7 | ) 8 | -------------------------------------------------------------------------------- /src/test/02_vtable/00_basic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | Ubpa_GetTargetName(core "${PROJECT_SOURCE_DIR}/src/core") 2 | Ubpa_AddTarget( 3 | TEST 4 | MODE EXE 5 | LIB 6 | ${core} 7 | ) 8 | -------------------------------------------------------------------------------- /src/test/02_vtable/01_heapobj/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | Ubpa_GetTargetName(core "${PROJECT_SOURCE_DIR}/src/core") 2 | Ubpa_AddTarget( 3 | TEST 4 | MODE EXE 5 | LIB 6 | ${core} 7 | ) 8 | -------------------------------------------------------------------------------- /src/EMPTY/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | Ubpa_GetTargetName(core "${PROJECT_SOURCE_DIR}/src/core") 2 | Ubpa_AddTarget( 3 | TEST 4 | MODE EXE 5 | SOURCE 6 | "${PROJECT_SOURCE_DIR}/include/UDP" 7 | LIB 8 | ${core} 9 | ) 10 | -------------------------------------------------------------------------------- /config/Config.cmake.in: -------------------------------------------------------------------------------- 1 | message(STATUS "config @PROJECT_NAME@ @PROJECT_VERSION@ ...") 2 | 3 | @PACKAGE_INIT@ 4 | 5 | @UBPA_PACKAGE_INIT@ 6 | 7 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 8 | 9 | message(STATUS "config @PROJECT_NAME@ @PROJECT_VERSION@ done") 10 | -------------------------------------------------------------------------------- /data/attr.h: -------------------------------------------------------------------------------- 1 | namespace Ubpa { 2 | struct [[meta0("a")]][[meta1]] Cmpt { 3 | public: 4 | [[meta2("c")]] 5 | const float x; 6 | void foo(float a); 7 | private: 8 | [[meta3("d")]] 9 | static const float y; 10 | [[meta4("e")]] 11 | float z; 12 | int bar(float a, int b); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /include/UDP/Visitor/Visitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Ubpa { 4 | // [Func] 5 | // - Ret([const] void*, Args...) 6 | // - Ret(Impl::*)([const] void*, Args...)[const] 7 | // [ID] vtable, TypeID, customed ID 8 | template 9 | class Visitor; 10 | } 11 | 12 | #include "details/Visitor.inl" 13 | -------------------------------------------------------------------------------- /src/core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(c_options "") 2 | if(MSVC) 3 | list(APPEND c_options "/wd5030") 4 | endif() 5 | 6 | Ubpa_AddTarget( 7 | MODE INTERFACE 8 | LIB 9 | Ubpa::UTemplate_core 10 | Ubpa::UContainer_core 11 | Ubpa::USTL_core 12 | INC 13 | "${PROJECT_SOURCE_DIR}/include" 14 | C_OPTION 15 | ${c_options} 16 | ) 17 | -------------------------------------------------------------------------------- /src/test/03_Signal/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace Ubpa; 6 | using namespace std; 7 | 8 | int main() { 9 | Signal mouseMoved; 10 | auto connection = mouseMoved.Connect([](int x, int y) { 11 | cout << x << ", " << y << endl; 12 | }); 13 | mouseMoved.Emit(3, 4); 14 | } 15 | -------------------------------------------------------------------------------- /data/attr_app_out.h: -------------------------------------------------------------------------------- 1 | // This file is generated by AutoRefl 2 | 3 | #include 4 | 5 | namespace Ubpa::AutoRefl { 6 | void Register_Ubpa_Cmpt() { 7 | Reflection::Instance() 8 | .Register("meta0", "a") 9 | .Register("meta1", "") 10 | .Register(&Ubpa::Cmpt::x, "x") 11 | .Register("x", "meta2", "c") 12 | ; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /data/attr_out.h: -------------------------------------------------------------------------------- 1 | // This file is generated by AutoRefl 2 | 3 | #include 4 | 5 | namespace Ubpa::AutoRefl { 6 | void Register_Ubpa_Cmpt() { 7 | Reflection::Instance() 8 | .Register("meta0", "a") 9 | .Register("meta1", "") 10 | .Register(&Ubpa::Cmpt::x, "x") 11 | .Register("x", "meta2", "c") 12 | ; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/test/02_vtable/01_heapobj/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace Ubpa; 5 | using namespace std; 6 | 7 | struct A { virtual ~A() = default; }; 8 | struct B : A { 9 | protected: 10 | ~B() = default; 11 | }; 12 | 13 | int main() { 14 | A a; 15 | cout << vtable_of::get() << endl; 16 | B* b = new B; 17 | cout << vtable(b) << endl; 18 | } 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(UDP VERSION 0.7.5) 4 | message(STATUS "[Project] ${PROJECT_NAME}") 5 | 6 | include(cmake/InitUCMake.cmake) 7 | Ubpa_InitUCMake(VERSION 0.6.4) 8 | 9 | Ubpa_InitProject() 10 | 11 | Ubpa_AddDep(UTemplate 0.7.2) 12 | Ubpa_AddDep(USTL 0.2.1) 13 | 14 | Ubpa_AddSubDirsRec(include) 15 | Ubpa_AddSubDirsRec(src) 16 | 17 | Ubpa_Export( 18 | TARGET 19 | DIRECTORIES 20 | "include" 21 | ) 22 | -------------------------------------------------------------------------------- /include/UDP/Signal/Signal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Connection.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace Ubpa { 9 | template 10 | class Signal { 11 | public: 12 | template 13 | Connection Connect(Slot&& slot); 14 | 15 | void Emit(Args... args) const; 16 | 17 | void Disconnect(Connection&& connection); 18 | 19 | private: 20 | size_t id{ 0 }; 21 | std::map> slots; 22 | 23 | }; 24 | } 25 | 26 | #include "details/Signal.inl" 27 | -------------------------------------------------------------------------------- /include/UDP/Signal/Connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Ubpa { 4 | template 5 | class Signal; 6 | 7 | class Connection { 8 | public: 9 | bool IsValid() const noexcept { return id != static_cast(-1); } 10 | Connection(Connection&& c) noexcept { 11 | id = c.id; 12 | c.id = static_cast(-1); 13 | } 14 | private: 15 | Connection(size_t id) : id(id) {} 16 | Connection(const Connection& c) = delete; 17 | size_t id; 18 | 19 | template 20 | friend class Signal; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/test/02_vtable/00_basic/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace Ubpa; 6 | using namespace std; 7 | 8 | struct A { virtual ~A() = default; }; 9 | struct B : A { }; 10 | struct C : A { }; 11 | 12 | int main() { 13 | cout << vtable_of::get() << endl; 14 | cout << vtable_of::get() << endl; 15 | cout << vtable_of::get() << endl; 16 | A a; 17 | B b; 18 | cout << vtable(&a) << endl; 19 | cout << vtable_is(&a) << endl; 20 | cout << vtable_is(&a) << endl; 21 | cout << vtable_is((A*)&b) << endl; 22 | cout << vtable_is((A*)&b) << endl; 23 | } 24 | -------------------------------------------------------------------------------- /src/test/04_dirty/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace Ubpa; 6 | using namespace std; 7 | 8 | class Vec { 9 | public: 10 | Vec(float x, float y) : x{ x }, y{ y }{} 11 | void SetX(float value) { 12 | x = value; 13 | sum.SetDirty(); 14 | } 15 | void SetY(float value) { 16 | y = value; 17 | sum.SetDirty(); 18 | } 19 | float GetSum() const { return sum.Get(*this); } 20 | private: 21 | float x; 22 | float y; 23 | mutable AutoDirty sum = { [](float& s, const Vec& v) { 24 | s = v.x + v.y; 25 | } }; 26 | }; 27 | 28 | int main() { 29 | Vec v{ 1,2 }; 30 | cout << v.GetSum() << endl; 31 | v.SetX(2.f); 32 | cout << v.GetSum() << endl; 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UDP 2 | Ubpa Design Pattern 3 | 4 | ## Visitor 5 | 6 | - `Visitor 4 | 5 | #include 6 | 7 | namespace Ubpa { 8 | template 9 | template 10 | Connection Signal::Connect(Slot&& slot) { 11 | using SlotArgList = typename FuncTraits::ArgList; 12 | slots[id] = FuncExpand::template run(std::forward(slot)); 13 | return id++; 14 | } 15 | 16 | template 17 | void Signal::Emit(Args... args) const { 18 | for (auto p : slots) 19 | p.second(args...); 20 | } 21 | 22 | template 23 | void Signal::Disconnect(Connection&& connection) { 24 | slots.erase(connection.id); 25 | connection.id = static_cast(-1); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /include/UDP/Basic/details/CustomID.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../vtable.h" 4 | #include 5 | 6 | namespace Ubpa { 7 | template 8 | constexpr size_t CustomID::get() noexcept { 9 | static_assert(!std::is_const_v && !std::is_pointer_v, 10 | "GetID: must be non-const and non-pointer"); 11 | if constexpr (std::is_polymorphic_v) 12 | return reinterpret_cast(vtable_of::get()); 13 | else 14 | return Ubpa::TypeID; 15 | } 16 | 17 | template 18 | constexpr size_t CustomID::get(const T* ptr) noexcept { 19 | assert(ptr != nullptr); 20 | if constexpr (std::is_void_v || std::is_polymorphic_v) 21 | return reinterpret_cast(vtable(ptr)); 22 | else 23 | return Ubpa::TypeID; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /include/UDP/Basic/details/vtable.inl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Ubpa::detail::vtable_ { 6 | template 7 | struct Derived; 8 | } 9 | 10 | namespace Ubpa { 11 | template 12 | inline const void* vtable_of::get() noexcept { 13 | if (!value) { 14 | if constexpr (std::is_constructible_v) { 15 | T tmp; 16 | regist(&tmp); 17 | } 18 | else if constexpr (is_derived_constructible_v) { 19 | detail::vtable_::Derived tmp{}; 20 | regist(&tmp.t); 21 | } 22 | else 23 | assert("vtable_of::get: vtable regist fail"); 24 | } 25 | return value; 26 | } 27 | } 28 | 29 | namespace Ubpa::detail::vtable_ { 30 | template 31 | struct Derived : Base { 32 | Derived() {} 33 | ~Derived() {} 34 | Base t; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /include/UDP/Basic/CustomID.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Ubpa { 4 | template 5 | struct CustomID { 6 | // return custom ID of T 7 | // if T is polymorphic, then return it's virtual table 8 | // * it will create a object of T, and get the virtual table from the object 9 | // * if T is not default constructable, you should call vtable_of::regist(ptr) firstly 10 | // * or call get(ptr) 11 | // else return TypeID 12 | static constexpr size_t get() noexcept; 13 | 14 | // return custom ID of T 15 | // if T is polymorphic, then return it's virtual table getting from ptr 16 | // else return TypeID 17 | static constexpr size_t get(const T* ptr) noexcept; 18 | }; 19 | 20 | template 21 | constexpr size_t CustomIDof(const T* ptr) noexcept { 22 | return CustomID::get(ptr); 23 | } 24 | } 25 | 26 | #include "details/CustomID.inl" 27 | -------------------------------------------------------------------------------- /include/UDP/Visitor/ncVisitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Visitor.h" 4 | 5 | namespace Ubpa { 6 | // non-const visitor 7 | template 8 | class Visitor { 9 | public: 10 | inline Ret Visit(size_t ID, void* ptr, Args... args) const; 11 | template 12 | inline Ret Visit(T* ptr, Args... args) const; 13 | 14 | template 15 | inline void Register(Funcs&&... funcs); 16 | 17 | template 18 | inline void Register(size_t ID, Func&& func); 19 | 20 | inline bool IsRegistered(size_t ID) const; 21 | template 22 | inline bool IsRegistered() const; 23 | 24 | inline bool IsRegistered(const void* ptr) const; 25 | 26 | private: 27 | template 28 | inline void RegisterOne(Func&& func); 29 | 30 | protected: 31 | std::unordered_map> callbacks; // ID -> func 32 | }; 33 | } 34 | 35 | #include "details/ncVisitor.inl" 36 | -------------------------------------------------------------------------------- /include/UDP/Visitor/cVisitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Visitor.h" 4 | 5 | namespace Ubpa { 6 | // const visitor 7 | template 8 | class Visitor { 9 | public: 10 | inline Ret Visit(size_t ID, const void* ptr, Args... args) const; 11 | template 12 | inline Ret Visit(const T* ptr, Args... args) const; 13 | 14 | template 15 | inline void Register(Funcs&&... funcs); 16 | 17 | template 18 | inline void Register(size_t ID, Func&& func); 19 | 20 | inline bool IsRegistered(size_t ID) const; 21 | template 22 | inline bool IsRegistered() const; 23 | 24 | inline bool IsRegistered(const void* ptr) const; 25 | 26 | private: 27 | template 28 | inline void RegisterOne(Func&& func); 29 | 30 | protected: 31 | std::unordered_map> callbacks; // ID -> func 32 | }; 33 | } 34 | 35 | #include "details/cVisitor.inl" 36 | -------------------------------------------------------------------------------- /include/UDP/Visitor/ncincVisitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ncVisitor.h" 4 | 5 | namespace Ubpa { 6 | // non-const impl non-const visitor 7 | template 8 | class Visitor : private Visitor { 9 | public: 10 | using Visitor::Register; 11 | 12 | inline Ret Visit(size_t ID, void* ptr, Args... args); 13 | 14 | template 15 | inline Ret Visit(T* ptr, Args... args); 16 | 17 | inline bool IsRegistered(size_t ID) const; 18 | template 19 | inline bool IsRegistered() const; 20 | 21 | inline bool IsRegistered(const void* ptr) const; 22 | 23 | protected: 24 | // ImplVisit 25 | template 26 | void Register(); 27 | 28 | private: 29 | // ImplVisit 30 | template 31 | void RegisterOne(); 32 | 33 | private: 34 | std::unordered_map> impl_callbacks; 35 | }; 36 | } 37 | 38 | #include "details/ncincVisitor.inl" 39 | -------------------------------------------------------------------------------- /include/UDP/Visitor/cincVisitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ncVisitor.h" 4 | 5 | namespace Ubpa { 6 | // const impl non-const visitor 7 | template 8 | class Visitor : private Visitor { 9 | public: 10 | using Visitor::Register; 11 | 12 | inline Ret Visit(size_t ID, void* ptr, Args... args) const; 13 | 14 | template 15 | inline Ret Visit(T* ptr, Args... args) const; 16 | 17 | inline bool IsRegistered(size_t ID) const; 18 | template 19 | inline bool IsRegistered() const; 20 | 21 | inline bool IsRegistered(const void* ptr) const; 22 | 23 | protected: 24 | // ImplVisit 25 | template 26 | void Register(); 27 | 28 | private: 29 | // ImplVisit 30 | template 31 | void RegisterOne(); 32 | 33 | private: 34 | std::unordered_map> impl_callbacks; 35 | }; 36 | } 37 | 38 | #include "details/cincVisitor.inl" 39 | -------------------------------------------------------------------------------- /include/UDP/Visitor/ncicVisitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cVisitor.h" 4 | 5 | namespace Ubpa { 6 | // non-const impl const visitor 7 | template 8 | class Visitor : private Visitor { 9 | public: 10 | using Visitor::Register; 11 | 12 | inline Ret Visit(size_t ID, const void* ptr, Args... args); 13 | 14 | template 15 | inline Ret Visit(const T* ptr, Args... args); 16 | 17 | inline bool IsRegistered(size_t ID) const; 18 | template 19 | inline bool IsRegistered() const; 20 | 21 | inline bool IsRegistered(const void* ptr) const; 22 | 23 | protected: 24 | // ImplVisit 25 | template 26 | void Register(); 27 | 28 | private: 29 | // ImplVisit 30 | template 31 | void RegisterOne(); 32 | 33 | private: 34 | std::unordered_map> impl_callbacks; 35 | }; 36 | } 37 | 38 | #include "details/ncicVisitor.inl" 39 | -------------------------------------------------------------------------------- /include/UDP/Visitor/cicVisitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cVisitor.h" 4 | 5 | namespace Ubpa { 6 | // const impl const visitor 7 | template 8 | class Visitor : private Visitor { 9 | public: 10 | using Visitor::Register; 11 | 12 | inline Ret Visit(size_t ID, const void* ptr, Args... args) const; 13 | 14 | template 15 | inline Ret Visit(const T* ptr, Args... args) const; 16 | 17 | inline bool IsRegistered(size_t ID) const; 18 | template 19 | inline bool IsRegistered() const; 20 | 21 | inline bool IsRegistered(const void* ptr) const; 22 | 23 | protected: 24 | // ImplVisit 25 | template 26 | void Register(); 27 | 28 | private: 29 | // ImplVisit 30 | template 31 | void RegisterOne(); 32 | 33 | private: 34 | std::unordered_map> impl_callbacks; 35 | }; 36 | } 37 | 38 | #include "details/cicVisitor.inl" 39 | -------------------------------------------------------------------------------- /cmake/InitUCMake.cmake: -------------------------------------------------------------------------------- 1 | macro(Ubpa_InitUCMake) 2 | cmake_parse_arguments( 3 | "ARG" # prefix 4 | "" # # TRUE / FALSE 5 | "VERSION" # 6 | "" # # list 7 | ${ARGN} 8 | ) 9 | # 结果为 ARG_* 10 | # - ARG_