1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00

Beefs up documentation on this miniature sort-of reflection.

This commit is contained in:
Thomas Harte 2020-03-11 22:06:16 -04:00
parent 7e8b86e9bb
commit 044a2b67e1
4 changed files with 91 additions and 24 deletions

View File

@ -23,7 +23,7 @@ struct Target: public ::Analyser::Static::Target, public Reflection::Struct<Targ
// Boilerplate for declaring fields and potential values. // Boilerplate for declaring fields and potential values.
if(needs_declare()) { if(needs_declare()) {
declare(&model, "model"); declare(&model, "model");
Reflection::Enum::declare<Model>(EnumDeclaration(Model)); AnnounceEnum(Model);
} }
} }

View File

@ -5117,7 +5117,6 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements"; CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
@ -5164,7 +5163,6 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements"; CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;

View File

@ -22,11 +22,39 @@ namespace Reflection {
enum class Name: Type { Mac128k, Mac512k, Mac512ke, MacPlus }; \ enum class Name: Type { Mac128k, Mac512k, Mac512ke, MacPlus }; \
constexpr static const char *__declaration##Name = #__VA_ARGS__; 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<Name>(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 { class Enum {
public: public:
template <typename Type> 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 <typename Type> static void declare(const char *name, const char *declaration) {
const char *d_ptr = declaration; const char *d_ptr = declaration;
std::vector<std::string> result; std::vector<std::string> result;
@ -46,43 +74,77 @@ class Enum {
members_by_type_.emplace(std::make_pair(&typeid(Type), result)); 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 <typename Type> 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 <typename Type> size_t size() { template <typename Type> size_t size() {
return size(typeid(Type)); 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) { size_t size(const std::type_info &type) {
const auto entry = members_by_type_.find(&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(); 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 <typename EnumType> static const std::string &toString(EnumType e) { template <typename Type> static const std::string &toString(Type e) {
const auto entry = members_by_type_.find(&typeid(EnumType)); return toString(typeid(Type), size_t(e));
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 @returns A @c std::string name for the enum value @c e from the enum with type_info @c type.
the string is not found. */
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 <typename Type> Type fromString(const std::string &str) { template <typename Type> Type fromString(const std::string &str) {
return Type(fromString(typeid(Type), 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) { size_t fromString(const std::type_info &type, const std::string &str) {
const auto entry = members_by_type_.find(&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;
const auto iterator = std::find(entry->second.begin(), entry->second.end(), str); 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()); return size_t(iterator - entry->second.begin());
} }
private: private:
static inline std::unordered_map<const std::type_info *, std::vector<std::string>> members_by_type_; static inline std::unordered_map<const std::type_info *, std::vector<std::string>> members_by_type_;
static inline std::unordered_map<const std::type_info *, std::string> names_by_type_;
static inline const std::string empty_string_; static inline const std::string empty_string_;
}; };

View File

@ -19,24 +19,39 @@ namespace Reflection {
template <typename Owner> class Struct { template <typename Owner> class Struct {
public: 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 <typename Type> const Type *get(const std::string &name) { template <typename Type> const Type *get(const std::string &name) {
const auto iterator = contents_.find(name); const auto iterator = contents_.find(name);
if(iterator == contents_.end()) return nullptr; if(iterator == contents_.end()) return nullptr;
return reinterpret_cast<Type *>(reinterpret_cast<uint8_t *>(this) + iterator->second.offset); return reinterpret_cast<Type *>(reinterpret_cast<uint8_t *>(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 <typename Type> void set(const std::string &name, const Type &value) { template <typename Type> void set(const std::string &name, const Type &value) {
const auto iterator = contents_.find(name); const auto iterator = contents_.find(name);
if(iterator == contents_.end()) return; if(iterator == contents_.end()) return;
*reinterpret_cast<Type *>(reinterpret_cast<uint8_t *>(this) + iterator->second.offset) = value; *reinterpret_cast<Type *>(reinterpret_cast<uint8_t *>(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 std::type_info *type_of(const std::string &name) {
const auto iterator = contents_.find(name); const auto iterator = contents_.find(name);
if(iterator == contents_.end()) return nullptr; if(iterator == contents_.end()) return nullptr;
return iterator->second.type; return iterator->second.type;
} }
/*!
@returns A vector of all declared fields for this struct.
*/
std::vector<std::string> all_keys() { std::vector<std::string> all_keys() {
std::vector<std::string> keys; std::vector<std::string> keys;
for(const auto &pair: contents_) { for(const auto &pair: contents_) {
@ -70,15 +85,7 @@ template <typename Owner> class Struct {
} }
/*! /*!
Provides the original declaration of an enum. @returns @c true if this subclass of @c Struct has not yet declared any fields.
*/
template <typename Type> void declare_enum(Type *t, const char *declaration) {
// TODO: something.
printf("%s\n", declaration);
}
/*!
@returns @c true if this
*/ */
bool needs_declare() { bool needs_declare() {
return !contents_.size(); return !contents_.size();