mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-25 03:32:01 +00:00
Makes a not-quite-correct attempt at a .description for reflective structs.
This commit is contained in:
parent
edc553fa1d
commit
60aa383c95
@ -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;
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user