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:
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user