1
0
mirror of https://github.com/TomHarte/CLK.git synced 2026-04-20 10:17:05 +00:00

Adds the ability for reflective structs to limit the permitted values to enumerated properties.

This commit is contained in:
Thomas Harte
2020-03-17 21:44:04 -04:00
parent 394ee61c78
commit f9ca443667
3 changed files with 93 additions and 9 deletions
+84
View File
@@ -9,6 +9,7 @@
#ifndef Struct_h
#define Struct_h
#include <cstdarg>
#include <cstring>
#include <string>
#include <typeindex>
@@ -27,6 +28,7 @@ struct Struct {
virtual const std::type_info *type_of(const std::string &name) = 0;
virtual void set(const std::string &name, const void *value) = 0;
virtual const void *get(const std::string &name) = 0;
virtual std::vector<std::string> values_for(const std::string &name) = 0;
virtual ~Struct() {}
};
@@ -123,6 +125,39 @@ template <typename Owner> class StructImpl: public Struct {
return iterator->second.type;
}
/*!
@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<std::string> values_for(const std::string &name) final {
std::vector<std::string> result;
// Return an empty vector if this field isn't declared.
const auto type = type_of(name);
if(!type) return result;
// Also return an empty vector if this field isn't a registered enum.
const auto all_values = Enum::all_values(*type);
if(all_values.empty()) return result;
// If no restriction is stored, return all values.
const auto permitted_values = permitted_enum_values_.find(name);
if(permitted_values == permitted_enum_values_.end()) return all_values;
// Compile a vector of only those values the stored set indicates.
auto value = all_values.begin();
auto flag = permitted_values->second.begin();
while(value != all_values.end() && flag != permitted_values->second.end()) {
if(*flag) {
result.push_back(*value);
}
++flag;
++value;
}
return result;
}
/*!
@returns A vector of all declared fields for this struct.
*/
@@ -158,6 +193,35 @@ template <typename Owner> class StructImpl: public Struct {
));
}
/*!
If @c t is a previously-declared field that links to a declared enum then the variable
arguments provide a list of the acceptable values for that field. The list should be terminated
with a value of -1.
*/
template <typename Type> void limit_enum(Type *t, ...) {
const auto name = name_of(t);
if(name.empty()) return;
// The default vector size of '8' isn't especially scientific,
// but I feel like it's a good choice.
std::vector<bool> permitted_values(8);
va_list list;
va_start(list, t);
while(true) {
const int next = va_arg(list, int);
if(next < 0) break;
if(permitted_values.size() <= next) {
permitted_values.resize(permitted_values.size() << 1);
}
permitted_values[next] = true;
}
va_end(list);
permitted_enum_values_.emplace(std::make_pair(name, permitted_values));
}
/*!
@returns @c true if this subclass of @c Struct has not yet declared any fields.
*/
@@ -165,6 +229,25 @@ template <typename Owner> class StructImpl: public Struct {
return !contents_.size();
}
/*!
Performs a reverse lookup from field to name.
*/
std::string name_of(void *field) {
const ssize_t offset = reinterpret_cast<uint8_t *>(field) - reinterpret_cast<uint8_t *>(this);
auto iterator = contents_.begin();
while(iterator != contents_.end()) {
if(iterator->second.offset == offset) break;
++iterator;
}
if(iterator != contents_.end()) {
return iterator->first;
} else {
return "";
}
}
private:
struct Field {
const std::type_info *type;
@@ -174,6 +257,7 @@ template <typename Owner> class StructImpl: public Struct {
type(&type), offset(offset), size(size) {}
};
static inline std::unordered_map<std::string, Field> contents_;
static inline std::unordered_map<std::string, std::vector<bool>> permitted_enum_values_;
};
}