From 7bf04d5338d18e0a5397238c6470ecd9a09e9333 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Feb 2020 18:08:46 -0500 Subject: [PATCH 1/7] Adds a prototype reflective enum. I need to make this scopeable before it is acceptable. --- Analyser/Static/Macintosh/Target.hpp | 14 ++-- .../Clock Signal.xcodeproj/project.pbxproj | 2 + Reflection/Enum.h | 80 +++++++++++++++++++ 3 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 Reflection/Enum.h diff --git a/Analyser/Static/Macintosh/Target.hpp b/Analyser/Static/Macintosh/Target.hpp index 5d70748f0..d1bb2d5be 100644 --- a/Analyser/Static/Macintosh/Target.hpp +++ b/Analyser/Static/Macintosh/Target.hpp @@ -9,19 +9,17 @@ #ifndef Analyser_Static_Macintosh_Target_h #define Analyser_Static_Macintosh_Target_h +#include "../../../Reflection/Enum.h" + +ReflectiveEnum(MacintoshModel, int, Mac128k, Mac512k, Mac128ke, MacPlus); + namespace Analyser { namespace Static { namespace Macintosh { -struct Target: public ::Analyser::Static::Target { - enum class Model { - Mac128k, - Mac512k, - Mac512ke, - MacPlus - }; - Model model = Model::MacPlus; +struct Target: public ::Analyser::Static::Target { + MacintoshModel model = MacintoshModel::MacPlus; }; } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index ed74f9c91..3f246ad8d 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1138,6 +1138,7 @@ 4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = ""; }; 4B643F3E1D77B88000D431D6 /* DocumentController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentController.swift; sourceTree = ""; }; 4B644ED023F0FB55006C0CC5 /* ScanSynchroniser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ScanSynchroniser.hpp; sourceTree = ""; }; + 4B644ED123FAF162006C0CC5 /* Enum.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Enum.h; path = ../../Reflection/Enum.h; sourceTree = ""; }; 4B65085F22F4CF8D009C1100 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = ""; }; 4B670A832401CB8400D4E002 /* z80memptr.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80memptr.tap; sourceTree = ""; }; 4B670A852401CB8400D4E002 /* z80ccf.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80ccf.tap; sourceTree = ""; }; @@ -3246,6 +3247,7 @@ 4BB73E951B587A5100552FC2 = { isa = PBXGroup; children = ( + 4B644ED123FAF162006C0CC5 /* Enum.h */, 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */, 4B51F70820A521D700AFA2C1 /* Activity */, 4B8944E2201967B4007DE474 /* Analyser */, diff --git a/Reflection/Enum.h b/Reflection/Enum.h new file mode 100644 index 000000000..0b7c329c9 --- /dev/null +++ b/Reflection/Enum.h @@ -0,0 +1,80 @@ +// +// Enum.h +// Clock Signal +// +// Created by Thomas Harte on 17/02/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef Enum_h +#define Enum_h + +#include + +namespace Reflection { + +template struct Enum { + static size_t size() { + return members().size(); + } + + /*! + @returns A @c string_view name for the enum value @c e. + */ + static std::string_view toString(EnumType e) { + return members()[size_t(e)]; + } + + /*! + @returns A value of @c EnumType name for the name @c str, or @c EnumType(-1) if + the string is not found. + */ + static EnumType fromString(const std::string_view &str) { + const auto member_list = members(); + auto position = std::find(member_list.begin(), member_list.end(), str); + if(position == member_list.end()) return EnumType(-1); + return EnumType(position - member_list.begin()); + } + + /*! + @returns A vector of string_views naming the members of this enum in value order. + */ + static std::vector members() { + Enum m; + const char *const declaration = m.declaration(); + const char *d_ptr = declaration; + + std::vector result; + while(true) { + // Skip non-alphas, and exit if the terminator is found. + while(*d_ptr && !isalpha(*d_ptr)) ++d_ptr; + if(!*d_ptr) break; + + // Note the current location and proceed for all alphas and digits. + const auto start = d_ptr; + while(isalpha(*d_ptr) || isdigit(*d_ptr)) ++d_ptr; + + // Add a string view. + result.emplace_back(start, d_ptr - start); + } + + return result; + } + + private: + constexpr const char *declaration(); + +}; + +} + +/*! + Provides a very limited subset of normal enums, with the addition of reflection. + + Enum members must take default values, and this enum must be in the global scope. +*/ +#define ReflectiveEnum(Name, Type, ...) \ + enum class Name: Type { __VA_ARGS__ }; \ + template <> constexpr const char *::Reflection::Enum::declaration() { return #__VA_ARGS__; }; + +#endif /* Enum_h */ From d6c6b9bdb89b6297eecb5e3c2c09fbf71ab2dd00 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 19 Feb 2020 22:26:31 -0500 Subject: [PATCH 2/7] Gives function overloading a try. --- Analyser/Static/Macintosh/Target.hpp | 16 ++++++++++++++-- .../xcschemes/Clock Signal Kiosk.xcscheme | 1 + Reflection/Enum.h | 10 +++------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Analyser/Static/Macintosh/Target.hpp b/Analyser/Static/Macintosh/Target.hpp index d1bb2d5be..eac46acb7 100644 --- a/Analyser/Static/Macintosh/Target.hpp +++ b/Analyser/Static/Macintosh/Target.hpp @@ -11,7 +11,6 @@ #include "../../../Reflection/Enum.h" -ReflectiveEnum(MacintoshModel, int, Mac128k, Mac512k, Mac128ke, MacPlus); namespace Analyser { namespace Static { @@ -19,11 +18,24 @@ namespace Macintosh { struct Target: public ::Analyser::Static::Target { - MacintoshModel model = MacintoshModel::MacPlus; + ReflectiveEnum(Model, int, Mac128k, Mac512k, Mac512ke, MacPlus); + + Target() { +// Model m; +// printf("%s\n", __declaration(m)); + printf("%zu\n", Reflection::Enum::size()); +// for(size_t c = 0; c < Reflection::Enum::size(); ++c) { +// const auto name = Reflection::Enum::toString(Model(c)); +// printf("%.*s\n", int(name.size()), name.data()); +// } + } + + Model model = Model::MacPlus; }; } } } + #endif /* Analyser_Static_Macintosh_Target_h */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 6993d95b7..f84023923 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -34,6 +34,7 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + enableThreadSanitizer = "YES" disableMainThreadChecker = "YES" launchStyle = "0" useCustomWorkingDirectory = "NO" diff --git a/Reflection/Enum.h b/Reflection/Enum.h index 0b7c329c9..49359c271 100644 --- a/Reflection/Enum.h +++ b/Reflection/Enum.h @@ -40,8 +40,8 @@ template struct Enum { @returns A vector of string_views naming the members of this enum in value order. */ static std::vector members() { - Enum m; - const char *const declaration = m.declaration(); + EnumType m; + const char *const declaration = __declaration(m); const char *d_ptr = declaration; std::vector result; @@ -60,10 +60,6 @@ template struct Enum { return result; } - - private: - constexpr const char *declaration(); - }; } @@ -75,6 +71,6 @@ template struct Enum { */ #define ReflectiveEnum(Name, Type, ...) \ enum class Name: Type { __VA_ARGS__ }; \ - template <> constexpr const char *::Reflection::Enum::declaration() { return #__VA_ARGS__; }; + constexpr const char *__declaration(Name) { return #__VA_ARGS__; } #endif /* Enum_h */ From 5248475e738c9ad4ed98e6cf650ff2d90f247c03 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Mar 2020 11:26:17 -0400 Subject: [PATCH 3/7] Starts experimenting with declared reflection. --- Reflection/Struct.h | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Reflection/Struct.h diff --git a/Reflection/Struct.h b/Reflection/Struct.h new file mode 100644 index 000000000..250396b76 --- /dev/null +++ b/Reflection/Struct.h @@ -0,0 +1,52 @@ +// +// Struct.h +// Clock Signal +// +// Created by Thomas Harte on 06/03/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef Struct_h +#define Struct_h + +#include +#include +#include +#include + +namespace Reflection { + +class Struct { + public: + template const Type &get(const std::string &name) { + return *std::any_cast(contents_[name]); + } + + template void set(const std::string &name, const Type &value) { + *std::any_cast(contents_[name]) = value; + } + + const std::type_info &type_of(const std::string &name) { + return contents_[name].type(); + } + + std::vector all_keys() { + std::vector keys; + for(const auto &pair: contents_) { + keys.push_back(pair.first); + } + return keys; + } + + protected: + template void declare(Type *t, const std::string &name) { + contents_[name] = t; + } + + private: + std::unordered_map contents_; +}; + +} + +#endif /* Struct_h */ From 6a8c6f5a0617a6d7d207d4aaddcfa3cc2f248b87 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Mar 2020 21:23:29 -0400 Subject: [PATCH 4/7] Switches to class storage. --- Reflection/Struct.h | 53 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/Reflection/Struct.h b/Reflection/Struct.h index 250396b76..0f6e83f19 100644 --- a/Reflection/Struct.h +++ b/Reflection/Struct.h @@ -9,25 +9,32 @@ #ifndef Struct_h #define Struct_h -#include #include +#include +#include #include #include namespace Reflection { -class Struct { +template class Struct { public: - template const Type &get(const std::string &name) { - return *std::any_cast(contents_[name]); + template const Type *get(const std::string &name) { + const auto iterator = contents_.find(name); + if(iterator == contents_.end()) return nullptr; + return reinterpret_cast(reinterpret_cast(this) + iterator->second.offset); } template void set(const std::string &name, const Type &value) { - *std::any_cast(contents_[name]) = value; + const auto iterator = contents_.find(name); + if(iterator == contents_.end()) return; + *reinterpret_cast(reinterpret_cast(this) + iterator->second.offset) = value; } - const std::type_info &type_of(const std::string &name) { - return contents_[name].type(); + const std::type_info *type_of(const std::string &name) { + const auto iterator = contents_.find(name); + if(iterator == contents_.end()) return nullptr; + return iterator->second.type; } std::vector all_keys() { @@ -39,12 +46,40 @@ class Struct { } protected: + /* + This interface requires reflective structs to declare all fields; + specifically they should call: + + declare_field(&field1, "field1"); + declare_field(&field2, "field2"); + + Fields are registered in class storage. So callers can use needs_declare() + to determine whether a class of this type has already established the + reflective fields. + */ template void declare(Type *t, const std::string &name) { - contents_[name] = t; + contents_.emplace( + std::make_pair( + name, + Field(typeid(Type), reinterpret_cast(t) - reinterpret_cast(this)) + )); + } + + /*! + @returns @c true if this + */ + bool needs_declare() { + return !contents_.size(); } private: - std::unordered_map contents_; + struct Field { + const std::type_info *type; + ssize_t offset; + Field(const std::type_info &type, ssize_t offset) : + type(&type), offset(offset) {} + }; + static inline std::unordered_map contents_; }; } From 0502e6be67c92e66b0a23f583aeedcd03b0b8354 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 9 Mar 2020 22:05:31 -0400 Subject: [PATCH 5/7] Starts working towards a registration-based model of reflective enums. --- Analyser/Static/Macintosh/Target.hpp | 20 +++++++---------- Machines/Apple/Macintosh/Macintosh.cpp | 3 +++ .../Clock Signal.xcodeproj/project.pbxproj | 15 +++++++++++-- Reflection/Enum.h | 22 ++++++++++++++++--- Reflection/Struct.h | 12 ++++++++++ 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/Analyser/Static/Macintosh/Target.hpp b/Analyser/Static/Macintosh/Target.hpp index eac46acb7..5e7d4913e 100644 --- a/Analyser/Static/Macintosh/Target.hpp +++ b/Analyser/Static/Macintosh/Target.hpp @@ -10,24 +10,21 @@ #define Analyser_Static_Macintosh_Target_h #include "../../../Reflection/Enum.h" - +#include "../../../Reflection/Struct.h" namespace Analyser { namespace Static { namespace Macintosh { - -struct Target: public ::Analyser::Static::Target { - ReflectiveEnum(Model, int, Mac128k, Mac512k, Mac512ke, MacPlus); +struct Target: public ::Analyser::Static::Target, public Reflection::Struct { + ReflectableEnum(Model, int, Mac128k, Mac512k, Mac512ke, MacPlus); Target() { -// Model m; -// printf("%s\n", __declaration(m)); - printf("%zu\n", Reflection::Enum::size()); -// for(size_t c = 0; c < Reflection::Enum::size(); ++c) { -// const auto name = Reflection::Enum::toString(Model(c)); -// printf("%.*s\n", int(name.size()), name.data()); -// } + // Boilerplate for declaring fields and potential values. + if(needs_declare()) { + declare(&model, "model"); + declare_enum(&model, EnumDeclaration(Model)); + } } Model model = Model::MacPlus; @@ -37,5 +34,4 @@ struct Target: public ::Analyser::Static::Target { } } - #endif /* Analyser_Static_Macintosh_Target_h */ diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index b9ceedf4c..f8b1306c4 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -51,6 +51,9 @@ namespace { constexpr int CLOCK_RATE = 7833600; + +Analyser::Static::Macintosh::Target nothing; + } namespace Apple { diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 3f246ad8d..c7f57a85b 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1040,6 +1040,8 @@ 4B38F3471F2EC11D00D9235D /* AmstradCPC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AmstradCPC.hpp; path = AmstradCPC/AmstradCPC.hpp; sourceTree = ""; }; 4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AsyncTaskQueue.cpp; path = ../../Concurrency/AsyncTaskQueue.cpp; sourceTree = ""; }; 4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AsyncTaskQueue.hpp; path = ../../Concurrency/AsyncTaskQueue.hpp; sourceTree = ""; }; + 4B3AF7D02413470E00873C0B /* Enum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Enum.h; sourceTree = ""; }; + 4B3AF7D12413472200873C0B /* Struct.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Struct.h; sourceTree = ""; }; 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = C1540Tests.swift; sourceTree = ""; }; 4B3BA0C51D318B44005DD7A7 /* C1540Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C1540Bridge.h; sourceTree = ""; }; 4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = C1540Bridge.mm; sourceTree = ""; }; @@ -1138,7 +1140,6 @@ 4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = ""; }; 4B643F3E1D77B88000D431D6 /* DocumentController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentController.swift; sourceTree = ""; }; 4B644ED023F0FB55006C0CC5 /* ScanSynchroniser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ScanSynchroniser.hpp; sourceTree = ""; }; - 4B644ED123FAF162006C0CC5 /* Enum.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Enum.h; path = ../../Reflection/Enum.h; sourceTree = ""; }; 4B65085F22F4CF8D009C1100 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = ""; }; 4B670A832401CB8400D4E002 /* z80memptr.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80memptr.tap; sourceTree = ""; }; 4B670A852401CB8400D4E002 /* z80ccf.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80ccf.tap; sourceTree = ""; }; @@ -2195,6 +2196,16 @@ name = Concurrency; sourceTree = ""; }; + 4B3AF7CF2413470E00873C0B /* Reflection */ = { + isa = PBXGroup; + children = ( + 4B3AF7D02413470E00873C0B /* Enum.h */, + 4B3AF7D12413472200873C0B /* Struct.h */, + ); + name = Reflection; + path = ../../Reflection; + sourceTree = ""; + }; 4B3BA0C41D318B44005DD7A7 /* Bridges */ = { isa = PBXGroup; children = ( @@ -3247,7 +3258,6 @@ 4BB73E951B587A5100552FC2 = { isa = PBXGroup; children = ( - 4B644ED123FAF162006C0CC5 /* Enum.h */, 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */, 4B51F70820A521D700AFA2C1 /* Activity */, 4B8944E2201967B4007DE474 /* Analyser */, @@ -3265,6 +3275,7 @@ 4B366DFD1B5C165F0026627B /* Outputs */, 4BB73EDD1B587CA500552FC2 /* Processors */, 4BB73E9F1B587A5100552FC2 /* Products */, + 4B3AF7CF2413470E00873C0B /* Reflection */, 4B055A7B1FAE84A50060FFFF /* SDL */, 4B2409591C45DF85004DA684 /* SignalProcessing */, 4B69FB391C4D908A00B5F0AA /* Storage */, diff --git a/Reflection/Enum.h b/Reflection/Enum.h index 49359c271..781c91468 100644 --- a/Reflection/Enum.h +++ b/Reflection/Enum.h @@ -10,9 +10,17 @@ #define Enum_h #include +#include namespace Reflection { +#define ReflectableEnum(Name, Type, ...) \ + enum class Name: Type { Mac128k, Mac512k, Mac512ke, MacPlus }; \ + constexpr static const char *__declaration##Name = #__VA_ARGS__; + +#define EnumDeclaration(Name) __declaration##Name + + template struct Enum { static size_t size() { return members().size(); @@ -69,8 +77,16 @@ template struct Enum { Enum members must take default values, and this enum must be in the global scope. */ -#define ReflectiveEnum(Name, Type, ...) \ - enum class Name: Type { __VA_ARGS__ }; \ - constexpr const char *__declaration(Name) { return #__VA_ARGS__; } +//#define DefX #define X + +//#define ForwardDeclareReflectiveEnum(Namespace, Name, ...) \ +// #define HAT + +// DeclName(m) m(__VA_ARGS__) + +// constexpr const char *__declaration(Namespace::Name) { return __VA_ARGS__; } + +//#define DefineReflectiveEnum(Name, Type) \ +// enum class Name: Type { Mac128k, Mac512k, Mac512ke, MacPlus }; #endif /* Enum_h */ diff --git a/Reflection/Struct.h b/Reflection/Struct.h index 0f6e83f19..7e55d702a 100644 --- a/Reflection/Struct.h +++ b/Reflection/Struct.h @@ -57,6 +57,10 @@ template class Struct { to determine whether a class of this type has already established the reflective fields. */ + + /*! + Exposes the field pointed to by @c t for reflection as @c name. + */ template void declare(Type *t, const std::string &name) { contents_.emplace( std::make_pair( @@ -65,6 +69,14 @@ template class Struct { )); } + /*! + Provides the original declaration of an enum. + */ + template void declare_enum(Type *t, const char *declaration) { + // TODO: something. + printf("%s\n", declaration); + } + /*! @returns @c true if this */ From 238145f27f1e4c283d15af41d07e90980acad750 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 10 Mar 2020 23:36:52 -0400 Subject: [PATCH 6/7] Attempts to flesh out Reflection::Enum. --- Analyser/Static/Macintosh/Target.hpp | 2 +- Reflection/Enum.h | 99 ++++++++++++++-------------- 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/Analyser/Static/Macintosh/Target.hpp b/Analyser/Static/Macintosh/Target.hpp index 5e7d4913e..e9cbc82df 100644 --- a/Analyser/Static/Macintosh/Target.hpp +++ b/Analyser/Static/Macintosh/Target.hpp @@ -23,7 +23,7 @@ struct Target: public ::Analyser::Static::Target, public Reflection::Struct(EnumDeclaration(Model)); } } diff --git a/Reflection/Enum.h b/Reflection/Enum.h index 781c91468..9db6bb1a7 100644 --- a/Reflection/Enum.h +++ b/Reflection/Enum.h @@ -11,6 +11,10 @@ #include #include +#include +#include +#include +#include namespace Reflection { @@ -20,39 +24,12 @@ namespace Reflection { #define EnumDeclaration(Name) __declaration##Name - -template struct Enum { - static size_t size() { - return members().size(); - } - - /*! - @returns A @c string_view name for the enum value @c e. - */ - static std::string_view toString(EnumType e) { - return members()[size_t(e)]; - } - - /*! - @returns A value of @c EnumType name for the name @c str, or @c EnumType(-1) if - the string is not found. - */ - static EnumType fromString(const std::string_view &str) { - const auto member_list = members(); - auto position = std::find(member_list.begin(), member_list.end(), str); - if(position == member_list.end()) return EnumType(-1); - return EnumType(position - member_list.begin()); - } - - /*! - @returns A vector of string_views naming the members of this enum in value order. - */ - static std::vector members() { - EnumType m; - const char *const declaration = __declaration(m); +class Enum { + public: + template static void declare(const char *declaration) { const char *d_ptr = declaration; - std::vector result; + std::vector result; while(true) { // Skip non-alphas, and exit if the terminator is found. while(*d_ptr && !isalpha(*d_ptr)) ++d_ptr; @@ -63,30 +40,52 @@ template struct Enum { while(isalpha(*d_ptr) || isdigit(*d_ptr)) ++d_ptr; // Add a string view. - result.emplace_back(start, d_ptr - start); + result.emplace_back(std::string(start, size_t(d_ptr - start))); } - return result; + members_by_type_.emplace(std::make_pair(&typeid(Type), result)); } + + template size_t size() { + return size(typeid(Type)); + } + + size_t size(const std::type_info &type) { + const auto entry = members_by_type_.find(&type); + if(entry == members_by_type_.end()) return 0; + return entry->second.size(); + } + + /*! + @returns A @c string_view name for the enum value @c e. + */ + template static const std::string &toString(EnumType e) { + const auto entry = members_by_type_.find(&typeid(EnumType)); + if(entry == members_by_type_.end()) return empty_string_; + return entry->second[size_t(e)]; + } + + /*! + @returns A value of @c EnumType name for the name @c str, or @c EnumType(-1) if + the string is not found. + */ + template Type fromString(const std::string &str) { + return Type(fromString(typeid(Type), str)); + } + + size_t fromString(const std::type_info &type, const std::string &str) { + const auto entry = members_by_type_.find(&type); + if(entry == members_by_type_.end()) return 0; + const auto iterator = std::find(entry->second.begin(), entry->second.end(), str); + if(iterator == entry->second.end()) return 0; + return size_t(iterator - entry->second.begin()); + } + + private: + static inline std::unordered_map> members_by_type_; + static inline const std::string empty_string_; }; } -/*! - Provides a very limited subset of normal enums, with the addition of reflection. - - Enum members must take default values, and this enum must be in the global scope. -*/ -//#define DefX #define X - -//#define ForwardDeclareReflectiveEnum(Namespace, Name, ...) \ -// #define HAT - -// DeclName(m) m(__VA_ARGS__) - -// constexpr const char *__declaration(Namespace::Name) { return __VA_ARGS__; } - -//#define DefineReflectiveEnum(Name, Type) \ -// enum class Name: Type { Mac128k, Mac512k, Mac512ke, MacPlus }; - #endif /* Enum_h */ From a546880a65c067e4672b93ed0a1b1a0739b4370b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 11 Mar 2020 22:06:16 -0400 Subject: [PATCH 7/7] Beefs up documentation on this miniature sort-of reflection. --- Analyser/Static/Macintosh/Target.hpp | 2 +- .../Clock Signal.xcodeproj/project.pbxproj | 2 - Reflection/Enum.h | 86 ++++++++++++++++--- Reflection/Struct.h | 25 ++++-- 4 files changed, 91 insertions(+), 24 deletions(-) diff --git a/Analyser/Static/Macintosh/Target.hpp b/Analyser/Static/Macintosh/Target.hpp index e9cbc82df..86295e31a 100644 --- a/Analyser/Static/Macintosh/Target.hpp +++ b/Analyser/Static/Macintosh/Target.hpp @@ -23,7 +23,7 @@ struct Target: public ::Analyser::Static::Target, public Reflection::Struct(EnumDeclaration(Model)); + AnnounceEnum(Model); } } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index c7f57a85b..ee4db8516 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -5117,7 +5117,6 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES; CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; @@ -5164,7 +5163,6 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES; CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; diff --git a/Reflection/Enum.h b/Reflection/Enum.h index 9db6bb1a7..1410d6a4c 100644 --- a/Reflection/Enum.h +++ b/Reflection/Enum.h @@ -22,11 +22,39 @@ namespace Reflection { enum class Name: Type { Mac128k, Mac512k, Mac512ke, MacPlus }; \ constexpr static const char *__declaration##Name = #__VA_ARGS__; -#define EnumDeclaration(Name) __declaration##Name +#define EnumDeclaration(Name) #Name, __declaration##Name +#define AnnounceEnum(Name) ::Reflection::Enum::declare(EnumDeclaration(Name)) + +/*! + This provides a very slight version of enum reflection; you can introspect only: + + * enums have been registered, along with the text of their declarations; + * provided that those enums do not declare specific values for their members. + + The macros above help avoid duplication of the declaration, making this just mildly less + terrible than it might have been. + + No guarantees of speed or any other kind of efficiency are offered. +*/ class Enum { public: - template static void declare(const char *declaration) { + /*! + Registers @c name and the entries within @c declaration for the enum type @c Type. + + Assuming the caller used the macros above, a standard pattern where both things can be placed in + the same namespace might look like: + + ReflectableEnum(MyEnum, int, A, B, C); + + ... + + AnnounceEnum(MyEnum) + + If AnnounceEnum cannot be placed into the same namespace as ReflectableEnum, see the + EnumDeclaration macro. + */ + template static void declare(const char *name, const char *declaration) { const char *d_ptr = declaration; std::vector result; @@ -46,43 +74,77 @@ class Enum { members_by_type_.emplace(std::make_pair(&typeid(Type), result)); } + /*! + @returns the declared name of the enum @c Type if it has been registered; the empty string otherwise. + */ + template const std::string &name() { + return name(typeid(Type)); + } + + /*! + @returns the declared name of the enum with type_info @c type if it has been registered; the empty string otherwise. + */ + const std::string &name(const std::type_info &type) { + const auto entry = names_by_type_.find(&type); + if(entry == names_by_type_.end()) return empty_string_; + return entry->second; + } + + /*! + @returns the number of members of the enum @c Type if it has been registered; 0 otherwise. + */ template size_t size() { return size(typeid(Type)); } + /*! + @returns the number of members of the enum with type_info @c type if it has been registered; @c std::string::npos otherwise. + */ size_t size(const std::type_info &type) { const auto entry = members_by_type_.find(&type); - if(entry == members_by_type_.end()) return 0; + if(entry == members_by_type_.end()) return std::string::npos; return entry->second.size(); } /*! - @returns A @c string_view name for the enum value @c e. + @returns A @c std::string name for the enum value @c e. */ - template static const std::string &toString(EnumType e) { - const auto entry = members_by_type_.find(&typeid(EnumType)); - if(entry == members_by_type_.end()) return empty_string_; - return entry->second[size_t(e)]; + template static const std::string &toString(Type e) { + return toString(typeid(Type), size_t(e)); } /*! - @returns A value of @c EnumType name for the name @c str, or @c EnumType(-1) if - the string is not found. + @returns A @c std::string name for the enum value @c e from the enum with type_info @c type. + */ + static const std::string &toString(const std::type_info &type, size_t e) { + const auto entry = members_by_type_.find(&type); + if(entry == members_by_type_.end()) return empty_string_; + return entry->second[e]; + } + + /*! + @returns A value of @c Type for the name @c str, or @c EnumType(std::string::npos) if + the name is not found. */ template Type fromString(const std::string &str) { return Type(fromString(typeid(Type), str)); } + /*! + @returns A value for the name @c str in the enum with type_info @c type , or @c std::string::npos if + the name is not found. + */ size_t fromString(const std::type_info &type, const std::string &str) { const auto entry = members_by_type_.find(&type); - if(entry == members_by_type_.end()) return 0; + if(entry == members_by_type_.end()) return std::string::npos; const auto iterator = std::find(entry->second.begin(), entry->second.end(), str); - if(iterator == entry->second.end()) return 0; + if(iterator == entry->second.end()) return std::string::npos; return size_t(iterator - entry->second.begin()); } private: static inline std::unordered_map> members_by_type_; + static inline std::unordered_map names_by_type_; static inline const std::string empty_string_; }; diff --git a/Reflection/Struct.h b/Reflection/Struct.h index 7e55d702a..27b7c28dd 100644 --- a/Reflection/Struct.h +++ b/Reflection/Struct.h @@ -19,24 +19,39 @@ namespace Reflection { template class Struct { public: + /*! + @returns the value of type @c Type that is loaded from the offset registered for the field @c name. + It is the caller's responsibility to provide an appropriate type of data. + */ template const Type *get(const std::string &name) { const auto iterator = contents_.find(name); if(iterator == contents_.end()) return nullptr; return reinterpret_cast(reinterpret_cast(this) + iterator->second.offset); } + /*! + Stores the @c value of type @c Type to the offset registered for the field @c name. + + It is the caller's responsibility to provide an appropriate type of data. + */ template void set(const std::string &name, const Type &value) { const auto iterator = contents_.find(name); if(iterator == contents_.end()) return; *reinterpret_cast(reinterpret_cast(this) + iterator->second.offset) = value; } + /*! + @returns @c type_info for the field @c name. + */ const std::type_info *type_of(const std::string &name) { const auto iterator = contents_.find(name); if(iterator == contents_.end()) return nullptr; return iterator->second.type; } + /*! + @returns A vector of all declared fields for this struct. + */ std::vector all_keys() { std::vector keys; for(const auto &pair: contents_) { @@ -70,15 +85,7 @@ template class Struct { } /*! - Provides the original declaration of an enum. - */ - template void declare_enum(Type *t, const char *declaration) { - // TODO: something. - printf("%s\n", declaration); - } - - /*! - @returns @c true if this + @returns @c true if this subclass of @c Struct has not yet declared any fields. */ bool needs_declare() { return !contents_.size();