1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-24 12:30:17 +00:00

Makes a not-quite-correct attempt at a .description for reflective structs.

This commit is contained in:
Thomas Harte 2020-03-30 00:24:49 -04:00
parent edc553fa1d
commit 60aa383c95
3 changed files with 125 additions and 29 deletions

View File

@ -30,7 +30,7 @@ class MultiStruct: public Reflection::Struct {
} }
} }
std::vector<std::string> all_keys() final { std::vector<std::string> all_keys() const final {
std::set<std::string> keys; std::set<std::string> keys;
for(auto &options: options_) { for(auto &options: options_) {
const auto new_keys = options->all_keys(); const auto new_keys = options->all_keys();
@ -39,7 +39,7 @@ class MultiStruct: public Reflection::Struct {
return std::vector<std::string>(keys.begin(), keys.end()); return std::vector<std::string>(keys.begin(), keys.end());
} }
std::vector<std::string> values_for(const std::string &name) final { std::vector<std::string> values_for(const std::string &name) const final {
std::set<std::string> values; std::set<std::string> values;
for(auto &options: options_) { for(auto &options: options_) {
const auto new_values = options->values_for(name); const auto new_values = options->values_for(name);
@ -48,7 +48,7 @@ class MultiStruct: public Reflection::Struct {
return std::vector<std::string>(values.begin(), values.end()); return std::vector<std::string>(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_) { for(auto &options: options_) {
auto info = options->type_of(name); auto info = options->type_of(name);
if(info) return info; if(info) return info;
@ -56,7 +56,7 @@ class MultiStruct: public Reflection::Struct {
return nullptr; return nullptr;
} }
const void *get(const std::string &name) final { const void *get(const std::string &name) const final {
for(auto &options: options_) { for(auto &options: options_) {
auto value = options->get(name); auto value = options->get(name);
if(value) return value; if(value) return value;

View File

@ -9,6 +9,7 @@
#include "Struct.hpp" #include "Struct.hpp"
#include <algorithm> #include <algorithm>
#include <sstream>
// MARK: - Setters // MARK: - Setters
@ -107,18 +108,84 @@ bool Reflection::fuzzy_set(Struct &target, const std::string &name, const std::s
// MARK: - Getters // MARK: - Getters
template <typename Type> bool Reflection::get(Struct &target, const std::string &name, Type &value) { template <typename Type> bool Reflection::get(const Struct &target, const std::string &name, Type &value) {
return false;
}
template <> bool Reflection::get(Struct &target, const std::string &name, bool &value) {
const auto target_type = target.type_of(name); const auto target_type = target.type_of(name);
if(!target_type) return false; if(!target_type) return false;
if(*target_type == typeid(bool)) { if(*target_type == typeid(Type)) {
value = *reinterpret_cast<const bool *>(target.get(name)); memcpy(&value, target.get(name), sizeof(Type));
return true; return true;
} }
return false; return false;
} }
template <typename Type> 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<int_type>(*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<val_type>(*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<int>(*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<const Reflection::Struct *>(get(key));
stream << child->description();
continue;
}
}
stream << "}";
return stream.str();
}

View File

@ -24,12 +24,18 @@ namespace Reflection {
#define DeclareField(Name) declare(&Name, #Name) #define DeclareField(Name) declare(&Name, #Name)
struct Struct { struct Struct {
virtual std::vector<std::string> all_keys() = 0; virtual std::vector<std::string> all_keys() const = 0;
virtual const std::type_info *type_of(const std::string &name) = 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 void set(const std::string &name, const void *value) = 0;
virtual const void *get(const std::string &name) = 0; virtual const void *get(const std::string &name) const = 0;
virtual std::vector<std::string> values_for(const std::string &name) = 0; virtual std::vector<std::string> values_for(const std::string &name) const = 0;
virtual ~Struct() {} 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. @returns @c true if the property was successfully read; @c false otherwise.
*/ */
template <typename Type> bool get(Struct &target, const std::string &name, Type &value); template <typename Type> 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 <typename Type> Type get(const Struct &target, const std::string &name);
// TODO: move this elsewhere. It's just a sketch anyway. // TODO: move this elsewhere. It's just a sketch anyway.
@ -106,10 +117,10 @@ template <typename Owner> class StructImpl: public Struct {
@returns the value of type @c Type that is loaded from the offset registered for the field @c name. @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. 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); const auto iterator = contents_.find(name);
if(iterator == contents_.end()) return nullptr; if(iterator == contents_.end()) return nullptr;
return reinterpret_cast<uint8_t *>(this) + iterator->second.offset; return reinterpret_cast<const uint8_t *>(this) + iterator->second.offset;
} }
/*! /*!
@ -126,7 +137,7 @@ template <typename Owner> class StructImpl: public Struct {
/*! /*!
@returns @c type_info for the field @c name. @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); 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;
@ -136,7 +147,7 @@ template <typename Owner> 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; @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. the empty list otherwise.
*/ */
std::vector<std::string> values_for(const std::string &name) final { std::vector<std::string> values_for(const std::string &name) const final {
std::vector<std::string> result; std::vector<std::string> result;
// Return an empty vector if this field isn't declared. // Return an empty vector if this field isn't declared.
@ -168,7 +179,7 @@ template <typename Owner> class StructImpl: public Struct {
/*! /*!
@returns A vector of all declared fields for this struct. @returns A vector of all declared fields for this struct.
*/ */
std::vector<std::string> all_keys() final { std::vector<std::string> all_keys() const final {
std::vector<std::string> keys; std::vector<std::string> keys;
for(const auto &pair: contents_) { for(const auto &pair: contents_) {
keys.push_back(pair.first); keys.push_back(pair.first);
@ -190,14 +201,14 @@ template <typename Owner> 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 <typename Type> void declare(Type *t, const std::string &name) { template <typename Type> void declare(Type *t, const std::string &name) {
contents_.emplace( if constexpr (std::is_class<Type>()) {
std::make_pair( if(declare_reflectable(t, name)) return;
name, }
Field(typeid(Type), reinterpret_cast<uint8_t *>(t) - reinterpret_cast<uint8_t *>(this), sizeof(Type)) declare_emplace(t, name);
));
} }
/*! /*!
@ -233,7 +244,7 @@ template <typename Owner> class StructImpl: public Struct {
@returns @c true if this subclass of @c Struct has not yet declared any fields. @returns @c true if this subclass of @c Struct has not yet declared any fields.
*/ */
bool needs_declare() { bool needs_declare() {
return !contents_.size(); return contents_.empty();
} }
/*! /*!
@ -256,6 +267,24 @@ template <typename Owner> class StructImpl: public Struct {
} }
private: private:
template <typename Type> bool declare_reflectable(Type *t, const std::string &name) {
Reflection::Struct *const str = static_cast<Reflection::Struct *>(t);
if(str) {
declare_emplace(str, name);
return true;
}
return false;
}
template <typename Type> void declare_emplace(Type *t, const std::string &name) {
contents_.emplace(
std::make_pair(
name,
Field(typeid(Type), reinterpret_cast<uint8_t *>(t) - reinterpret_cast<uint8_t *>(this), sizeof(Type))
));
}
struct Field { struct Field {
const std::type_info *type; const std::type_info *type;
ssize_t offset; ssize_t offset;