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()) {
|
if(needs_declare()) {
|
||||||
DeclareField(output);
|
DeclareField(output);
|
||||||
AnnounceEnumNS(Configurable, Display);
|
AnnounceEnumNS(Configurable, Display);
|
||||||
// TODO: some way to set limited enum support for a struct?
|
limit_enum(&output, Configurable::Display::RGB, Configurable::Display::CompositeColour, -1);
|
||||||
// In this case, to support only RGB and CompositeColour.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -495,7 +495,6 @@ int main(int argc, char *argv[]) {
|
|||||||
std::cout << "}" << std::endl << std::endl;
|
std::cout << "}" << std::endl << std::endl;
|
||||||
|
|
||||||
std::cout << "Further machine options:" << 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 targets = Machine::TargetsByMachineName(false);
|
||||||
const auto runtime_options = Machine::AllOptionsByMachineName();
|
const auto runtime_options = Machine::AllOptionsByMachineName();
|
||||||
@ -520,17 +519,18 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
std::cout << machine << ":" << std::endl;
|
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;
|
std::vector<std::string> all_options = options_keys;
|
||||||
all_options.insert(all_options.end(), target_keys.begin(), target_keys.end());
|
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) {
|
for(const auto &option: all_options) {
|
||||||
std::cout << '\t' << "--" << option;
|
std::cout << '\t' << "--" << option;
|
||||||
|
|
||||||
bool is_construction_option = true;
|
auto source = target_reflectable;
|
||||||
auto type = target_reflectable->type_of(option);
|
auto type = target_reflectable->type_of(option);
|
||||||
if(!type) {
|
if(!type) {
|
||||||
is_construction_option = false;
|
source = options_reflectable;
|
||||||
type = options_reflectable->type_of(option);
|
type = options_reflectable->type_of(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,7 +538,7 @@ int main(int argc, char *argv[]) {
|
|||||||
if(!Reflection::Enum::name(*type).empty()) {
|
if(!Reflection::Enum::name(*type).empty()) {
|
||||||
std::cout << "={";
|
std::cout << "={";
|
||||||
bool is_first = true;
|
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 << '|';
|
if(!is_first) std::cout << '|';
|
||||||
is_first = false;
|
is_first = false;
|
||||||
std::cout << value;
|
std::cout << value;
|
||||||
@ -546,9 +546,10 @@ int main(int argc, char *argv[]) {
|
|||||||
std::cout << "}";
|
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;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#ifndef Struct_h
|
#ifndef Struct_h
|
||||||
#define Struct_h
|
#define Struct_h
|
||||||
|
|
||||||
|
#include <cstdarg>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
@ -27,6 +28,7 @@ struct Struct {
|
|||||||
virtual const std::type_info *type_of(const std::string &name) = 0;
|
virtual const std::type_info *type_of(const std::string &name) = 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) = 0;
|
||||||
|
virtual std::vector<std::string> values_for(const std::string &name) = 0;
|
||||||
virtual ~Struct() {}
|
virtual ~Struct() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -123,6 +125,39 @@ template <typename Owner> class StructImpl: public Struct {
|
|||||||
return iterator->second.type;
|
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.
|
@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.
|
@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();
|
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:
|
private:
|
||||||
struct Field {
|
struct Field {
|
||||||
const std::type_info *type;
|
const std::type_info *type;
|
||||||
@ -174,6 +257,7 @@ template <typename Owner> class StructImpl: public Struct {
|
|||||||
type(&type), offset(offset), size(size) {}
|
type(&type), offset(offset), size(size) {}
|
||||||
};
|
};
|
||||||
static inline std::unordered_map<std::string, Field> contents_;
|
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