mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-28 21:49:27 +00:00
Adds the ability for reflective structs to limit the permitted values to enumerated properties.
This commit is contained in:
parent
394ee61c78
commit
f9ca443667
@ -38,8 +38,7 @@ class Machine {
|
||||
if(needs_declare()) {
|
||||
DeclareField(output);
|
||||
AnnounceEnumNS(Configurable, Display);
|
||||
// TODO: some way to set limited enum support for a struct?
|
||||
// In this case, to support only RGB and CompositeColour.
|
||||
limit_enum(&output, Configurable::Display::RGB, Configurable::Display::CompositeColour, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -495,7 +495,6 @@ int main(int argc, char *argv[]) {
|
||||
std::cout << "}" << std::endl << std::endl;
|
||||
|
||||
std::cout << "Further machine options:" << std::endl;
|
||||
std::cout << "(* means: a selection will be made automatically based on the file selected, if any)" << std::endl << std::endl;
|
||||
|
||||
const auto targets = Machine::TargetsByMachineName(false);
|
||||
const auto runtime_options = Machine::AllOptionsByMachineName();
|
||||
@ -520,17 +519,18 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
std::cout << machine << ":" << std::endl;
|
||||
|
||||
// Join the two lists of properties.
|
||||
// Join the two lists of properties and sort the result.
|
||||
std::vector<std::string> all_options = options_keys;
|
||||
all_options.insert(all_options.end(), target_keys.begin(), target_keys.end());
|
||||
std::sort(all_options.begin(), all_options.end());
|
||||
|
||||
for(const auto &option: all_options) {
|
||||
std::cout << '\t' << "--" << option;
|
||||
|
||||
bool is_construction_option = true;
|
||||
auto source = target_reflectable;
|
||||
auto type = target_reflectable->type_of(option);
|
||||
if(!type) {
|
||||
is_construction_option = false;
|
||||
source = options_reflectable;
|
||||
type = options_reflectable->type_of(option);
|
||||
}
|
||||
|
||||
@ -538,7 +538,7 @@ int main(int argc, char *argv[]) {
|
||||
if(!Reflection::Enum::name(*type).empty()) {
|
||||
std::cout << "={";
|
||||
bool is_first = true;
|
||||
for(const auto &value: Reflection::Enum::all_values(*type)) {
|
||||
for(const auto &value: source->values_for(option)) {
|
||||
if(!is_first) std::cout << '|';
|
||||
is_first = false;
|
||||
std::cout << value;
|
||||
@ -546,9 +546,10 @@ int main(int argc, char *argv[]) {
|
||||
std::cout << "}";
|
||||
}
|
||||
|
||||
// TODO: if not a registered enum... then assume it was a Boolean?
|
||||
// The above effectively assumes that every field is either a
|
||||
// Boolean or an enum. This may need to be revisted. It also
|
||||
// assumes no name collisions, but that's kind of unavoidable.
|
||||
|
||||
if(is_construction_option) std::cout << "\t*";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user