From 60aa383c95aa1074798379e96d12fdc0ac0b4726 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Mar 2020 00:24:49 -0400 Subject: [PATCH] Makes a not-quite-correct attempt at a .description for reflective structs. --- .../Implementation/MultiConfigurable.cpp | 8 +- Reflection/Struct.cpp | 81 +++++++++++++++++-- Reflection/Struct.hpp | 65 ++++++++++----- 3 files changed, 125 insertions(+), 29 deletions(-) diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp index 6688debba..55bf935ba 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp @@ -30,7 +30,7 @@ class MultiStruct: public Reflection::Struct { } } - std::vector all_keys() final { + std::vector all_keys() const final { std::set keys; for(auto &options: options_) { const auto new_keys = options->all_keys(); @@ -39,7 +39,7 @@ class MultiStruct: public Reflection::Struct { return std::vector(keys.begin(), keys.end()); } - std::vector values_for(const std::string &name) final { + std::vector values_for(const std::string &name) const final { std::set values; for(auto &options: options_) { const auto new_values = options->values_for(name); @@ -48,7 +48,7 @@ class MultiStruct: public Reflection::Struct { return std::vector(values.begin(), values.end()); } - const std::type_info *type_of(const std::string &name) final { + const std::type_info *type_of(const std::string &name) const final { for(auto &options: options_) { auto info = options->type_of(name); if(info) return info; @@ -56,7 +56,7 @@ class MultiStruct: public Reflection::Struct { return nullptr; } - const void *get(const std::string &name) final { + const void *get(const std::string &name) const final { for(auto &options: options_) { auto value = options->get(name); if(value) return value; diff --git a/Reflection/Struct.cpp b/Reflection/Struct.cpp index 19096e0ad..6597a8c63 100644 --- a/Reflection/Struct.cpp +++ b/Reflection/Struct.cpp @@ -9,6 +9,7 @@ #include "Struct.hpp" #include +#include // MARK: - Setters @@ -107,18 +108,84 @@ bool Reflection::fuzzy_set(Struct &target, const std::string &name, const std::s // MARK: - Getters -template bool Reflection::get(Struct &target, const std::string &name, Type &value) { - return false; -} - -template <> bool Reflection::get(Struct &target, const std::string &name, bool &value) { +template bool Reflection::get(const Struct &target, const std::string &name, Type &value) { const auto target_type = target.type_of(name); if(!target_type) return false; - if(*target_type == typeid(bool)) { - value = *reinterpret_cast(target.get(name)); + if(*target_type == typeid(Type)) { + memcpy(&value, target.get(name), sizeof(Type)); return true; } return false; } + +template Type Reflection::get(const Struct &target, const std::string &name) { + Type value; + get(target, name, value); + return value; +} + +// MARK: - Description + +std::string Reflection::Struct::description() const { + std::ostringstream stream; + + stream << "{"; + + bool is_first = true; + for(const auto &key: all_keys()) { + if(!is_first) stream << ", "; + is_first = false; + stream << key << ": "; + + const auto type = type_of(key); + + // Output Bools as yes/no. + if(*type == typeid(bool)) { + bool value; + ::Reflection::get(*this, key, value); + stream << (value ? "true" : "false"); + continue; + } + + // Output Ints of all sizes as hex. +#define OutputIntC(int_type, cast_type) if(*type == typeid(int_type)) { stream << std::hex << cast_type(::Reflection::get(*this, key)); continue; } +#define OutputInt(int_type) OutputIntC(int_type, int_type) + OutputIntC(int8_t, int16_t); + OutputIntC(uint8_t, uint16_t); + OutputInt(int16_t); + OutputInt(uint16_t); + OutputInt(int32_t); + OutputInt(uint32_t); + OutputInt(int64_t); + OutputInt(uint64_t); +#undef OutputInt + + // Output floats and strings natively. +#define OutputNative(val_type) if(*type == typeid(val_type)) { stream << ::Reflection::get(*this, key); continue; } + OutputNative(float); + OutputNative(double); + OutputNative(char *); + OutputNative(std::string); +#undef OutputNAtive + + // Output the current value of any enums. + if(!Enum::name(*type).empty()) { + const int value = ::Reflection::get(*this, key); + stream << Enum::to_string(*type, value); + continue; + } + + // Recurse to deal with embedded objects. + if(*type == typeid(Reflection::Struct)) { + const Reflection::Struct *const child = reinterpret_cast(get(key)); + stream << child->description(); + continue; + } + } + + stream << "}"; + + return stream.str(); +} diff --git a/Reflection/Struct.hpp b/Reflection/Struct.hpp index e6423deae..f60ce06c1 100644 --- a/Reflection/Struct.hpp +++ b/Reflection/Struct.hpp @@ -24,12 +24,18 @@ namespace Reflection { #define DeclareField(Name) declare(&Name, #Name) struct Struct { - virtual std::vector all_keys() = 0; - virtual const std::type_info *type_of(const std::string &name) = 0; + virtual std::vector all_keys() const = 0; + virtual const std::type_info *type_of(const std::string &name) const = 0; virtual void set(const std::string &name, const void *value) = 0; - virtual const void *get(const std::string &name) = 0; - virtual std::vector values_for(const std::string &name) = 0; + virtual const void *get(const std::string &name) const = 0; + virtual std::vector values_for(const std::string &name) const = 0; virtual ~Struct() {} + + /*! + @returns A string describing this struct. This string has no guaranteed layout, may not be + sufficiently formed for a formal language parser, etc. + */ + std::string description() const; }; /*! @@ -86,9 +92,14 @@ bool fuzzy_set(Struct &target, const std::string &name, const std::string &value @returns @c true if the property was successfully read; @c false otherwise. */ -template bool get(Struct &target, const std::string &name, Type &value); +template bool get(const Struct &target, const std::string &name, Type &value); -template <> bool get(Struct &target, const std::string &name, bool &value); +/*! + Attempts to get the property @c name to @c value ; will perform limited type conversions. + + @returns @c true if the property was successfully read; a default-constructed instance of Type otherwise. +*/ +template Type get(const Struct &target, const std::string &name); // TODO: move this elsewhere. It's just a sketch anyway. @@ -106,10 +117,10 @@ template class StructImpl: public Struct { @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. */ - const void *get(const std::string &name) final { + const void *get(const std::string &name) const final { const auto iterator = contents_.find(name); if(iterator == contents_.end()) return nullptr; - return reinterpret_cast(this) + iterator->second.offset; + return reinterpret_cast(this) + iterator->second.offset; } /*! @@ -126,7 +137,7 @@ template class StructImpl: public Struct { /*! @returns @c type_info for the field @c name. */ - const std::type_info *type_of(const std::string &name) final { + const std::type_info *type_of(const std::string &name) const final { const auto iterator = contents_.find(name); if(iterator == contents_.end()) return nullptr; return iterator->second.type; @@ -136,7 +147,7 @@ template class StructImpl: public Struct { @returns a list of the valid enum value names for field @c name if it is a declared enum field of this struct; the empty list otherwise. */ - std::vector values_for(const std::string &name) final { + std::vector values_for(const std::string &name) const final { std::vector result; // Return an empty vector if this field isn't declared. @@ -168,7 +179,7 @@ template class StructImpl: public Struct { /*! @returns A vector of all declared fields for this struct. */ - std::vector all_keys() final { + std::vector all_keys() const final { std::vector keys; for(const auto &pair: contents_) { keys.push_back(pair.first); @@ -190,14 +201,14 @@ template class StructImpl: public Struct { */ /*! - Exposes the field pointed to by @c t for reflection as @c name. + Exposes the field pointed to by @c t for reflection as @c name. If @c t is itself a Reflection::Struct, + it'll be the struct that's exposed. */ template void declare(Type *t, const std::string &name) { - contents_.emplace( - std::make_pair( - name, - Field(typeid(Type), reinterpret_cast(t) - reinterpret_cast(this), sizeof(Type)) - )); + if constexpr (std::is_class()) { + if(declare_reflectable(t, name)) return; + } + declare_emplace(t, name); } /*! @@ -233,7 +244,7 @@ template class StructImpl: public Struct { @returns @c true if this subclass of @c Struct has not yet declared any fields. */ bool needs_declare() { - return !contents_.size(); + return contents_.empty(); } /*! @@ -256,6 +267,24 @@ template class StructImpl: public Struct { } private: + template bool declare_reflectable(Type *t, const std::string &name) { + Reflection::Struct *const str = static_cast(t); + if(str) { + declare_emplace(str, name); + return true; + } + + return false; + } + + template void declare_emplace(Type *t, const std::string &name) { + contents_.emplace( + std::make_pair( + name, + Field(typeid(Type), reinterpret_cast(t) - reinterpret_cast(this), sizeof(Type)) + )); + } + struct Field { const std::type_info *type; ssize_t offset;