diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp index c77ff6de0..c151d52a7 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.cpp @@ -22,7 +22,7 @@ MultiConfigurable::MultiConfigurable(const std::vector &options) { } -std::unique_ptr MultiConfigurable::get_options(OptionsType type) { +std::unique_ptr MultiConfigurable::get_options() { // TODO: this'll need to mash options together, maybe? Or just take the front? return nullptr; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.hpp index 7d4672987..88c264e52 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.hpp @@ -30,7 +30,7 @@ class MultiConfigurable: public Configurable::Device { // Below is the standard Configurable::Device interface; see there for documentation. void set_options(const std::unique_ptr &options) final; - std::unique_ptr get_options(OptionsType type) final; + std::unique_ptr get_options() final; // std::vector> get_options() final; // void set_selections(const Configurable::SelectionSet &selection_by_option) final; // Configurable::SelectionSet get_accurate_selections() final; diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index 05e1fb2e0..0f7013dc9 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -87,6 +87,7 @@ template class MOS6560 { void set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } Outputs::Display::ScanStatus get_scaled_scan_status() const { return crt_.get_scaled_scan_status() / 4.0f; } void set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); } + Outputs::Display::DisplayType get_display_type() { return crt_.get_display_type(); } Outputs::Speaker::Speaker *get_speaker() { return &speaker_; } void set_high_frequency_cutoff(float cutoff) { diff --git a/Configurable/Configurable.hpp b/Configurable/Configurable.hpp index e2ed66628..cbb037ff3 100644 --- a/Configurable/Configurable.hpp +++ b/Configurable/Configurable.hpp @@ -17,27 +17,28 @@ namespace Configurable { /*! A Configurable::Device provides a reflective struct listing the available runtime options for this machine. - It can provide it populated with 'accurate' options, 'user-friendly' options or just whatever the user - currently has selected. + You can ordinarily either get or set a machine's current options, or else construct a new instance of + its options with one of the OptionsTypes defined below. +*/ +struct Device { + /// Sets the current options. The caller must ensure that the object passed in is either an instance of the machine's + /// Options struct, or else was previously returned by get_options. + virtual void set_options(const std::unique_ptr &options) = 0; + /// @returns An options object + virtual std::unique_ptr get_options() = 0; +}; + +/*! 'Accurate' options should correspond to the way that this device was usually used during its lifespan. E.g. a ColecoVision might accurately be given composite output. 'User-friendly' options should be more like those that a user today might most expect from an emulator. E.g. the ColecoVision might bump itself up to S-Video output. */ -struct Device { - /// Sets the current options. - virtual void set_options(const std::unique_ptr &options) = 0; - - enum class OptionsType { - Current, - Accurate, - UserFriendly - }; - - /// @returns Options of type @c type. - virtual std::unique_ptr get_options(OptionsType type) = 0; +enum class OptionsType { + Accurate, + UserFriendly }; } diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 956dc62de..85910387b 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -40,16 +40,6 @@ namespace AmstradCPC { -//std::vector> get_options() { -// return Configurable::standard_options( -// Configurable::StandardOptions(Configurable::DisplayRGB | Configurable::DisplayCompositeColour) -// ); -//} - -std::unique_ptr get_options() { - return nullptr; -} - /*! Models the CPC's interrupt timer. Inputs are vsync, hsync, interrupt acknowledge and reset, and its output is simply yes or no on whether an interupt is currently requested. Internally it uses a counter with a period @@ -358,6 +348,11 @@ class CRTCBusHandler { crt_.set_display_type(display_type); } + /// Gets the type of display. + Outputs::Display::DisplayType get_display_type() { + return crt_.get_display_type(); + } + /*! Sets the next video mode. Per the documentation, mode changes take effect only at the end of line, not immediately. So next means "as of the end of this line". @@ -1049,6 +1044,11 @@ template class ConcreteMachine: crtc_bus_handler_.set_display_type(display_type); } + /// A CRTMachine function; gets the output display type. + Outputs::Display::DisplayType get_display_type() { + return crtc_bus_handler_.get_display_type(); + } + /// @returns the speaker in use. Outputs::Speaker::Speaker *get_speaker() final { return ay_.get_speaker(); @@ -1118,34 +1118,16 @@ template class ConcreteMachine: } // MARK: - Configuration options. - std::unique_ptr get_options(OptionsType type) final { - return nullptr; + std::unique_ptr get_options() final { + auto options = std::make_unique(Configurable::OptionsType::UserFriendly); + options->output = get_video_signal_configurable(); + return options; } - void set_options(const std::unique_ptr &options) { + void set_options(const std::unique_ptr &str) { + const auto options = dynamic_cast(str.get()); + set_video_signal_configurable(options->output); } -// std::vector> get_options() final { -// return AmstradCPC::get_options(); -// } -// -// void set_selections(const Configurable::SelectionSet &selections_by_option) final { -// Configurable::Display display; -// if(Configurable::get_display(selections_by_option, display)) { -// set_video_signal_configurable(display); -// } -// } -// -// Configurable::SelectionSet get_accurate_selections() final { -// Configurable::SelectionSet selection_set; -// Configurable::append_display_selection(selection_set, Configurable::Display::RGB); -// return selection_set; -// } -// -// Configurable::SelectionSet get_user_friendly_selections() final { -// Configurable::SelectionSet selection_set; -// Configurable::append_display_selection(selection_set, Configurable::Display::RGB); -// return selection_set; -// } // MARK: - Joysticks const std::vector> &get_joysticks() final { diff --git a/Machines/AmstradCPC/AmstradCPC.hpp b/Machines/AmstradCPC/AmstradCPC.hpp index 98eece1f5..8e8e843c7 100644 --- a/Machines/AmstradCPC/AmstradCPC.hpp +++ b/Machines/AmstradCPC/AmstradCPC.hpp @@ -9,7 +9,8 @@ #ifndef AmstradCPC_hpp #define AmstradCPC_hpp -#include "../../Reflection/Struct.h" +#include "../../Configurable/Configurable.hpp" +#include "../../Configurable/StandardOptions.hpp" #include "../../Analyser/Static/StaticAnalyser.hpp" #include "../ROMMachine.hpp" @@ -17,9 +18,6 @@ namespace AmstradCPC { -/// @returns The options available for an Amstrad CPC. -std::unique_ptr get_options(); - /*! Models an Amstrad CPC. */ @@ -29,6 +27,22 @@ class Machine { /// Creates and returns an Amstrad CPC. static Machine *AmstradCPC(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher); + + /// Defines the runtime options available for a ZX80/81. + class Options: public Reflection::StructImpl { + public: + Configurable::Display output = Configurable::Display::RGB; + + Options(Configurable::OptionsType type) { + // Declare fields if necessary. + 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. + } + } + }; }; } diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index 3c7fd9d3d..f1a6db001 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -869,7 +869,7 @@ template class ConcreteMachine: } // MARK:: Configuration options. - std::unique_ptr get_options(OptionsType type) final { + std::unique_ptr get_options() final { return nullptr; } diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 59fb68239..c71fcf103 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -527,7 +527,7 @@ template class ConcreteMachin } // MARK: - Configuration options. - std::unique_ptr get_options(OptionsType type) final { + std::unique_ptr get_options() final { return nullptr; } diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index afcc3182c..6008ad075 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -682,7 +682,7 @@ class ConcreteMachine: } // MARK: - Configuration options. - std::unique_ptr get_options(OptionsType type) final { + std::unique_ptr get_options() final { return nullptr; } diff --git a/Machines/CRTMachine.hpp b/Machines/CRTMachine.hpp index 1e57575c1..fc86a7379 100644 --- a/Machines/CRTMachine.hpp +++ b/Machines/CRTMachine.hpp @@ -181,10 +181,28 @@ class Machine { } /*! - Forwards the video signal to the target returned by get_crt(). + Maps back from Outputs::Display::VideoSignal to Configurable::Display, + calling @c get_display_type for the input. + */ + Configurable::Display get_video_signal_configurable() { + switch(get_display_type()) { + default: + case Outputs::Display::DisplayType::RGB: return Configurable::Display::RGB; + case Outputs::Display::DisplayType::SVideo: return Configurable::Display::SVideo; + case Outputs::Display::DisplayType::CompositeColour: return Configurable::Display::CompositeColour; + case Outputs::Display::DisplayType::CompositeMonochrome: return Configurable::Display::CompositeMonochrome; + } + } + + /*! + Sets the display type. */ virtual void set_display_type(Outputs::Display::DisplayType display_type) {} + /*! + Gets the display type. + */ + virtual Outputs::Display::DisplayType get_display_type() { return Outputs::Display::DisplayType::RGB; } private: double clock_rate_ = 1.0; diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 0d5c5847b..45b875ffc 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -373,7 +373,7 @@ class ConcreteMachine: } // MARK: - Configuration options. - std::unique_ptr get_options(OptionsType type) final { + std::unique_ptr get_options() final { return nullptr; } diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index b07c522b5..2984544b6 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -682,7 +682,7 @@ class ConcreteMachine: } // MARK: - Configuration options. - std::unique_ptr get_options(OptionsType type) final { + std::unique_ptr get_options() final { return nullptr; } diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 7546d98ce..2f67b3c12 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -454,7 +454,7 @@ class ConcreteMachine: } // MARK: - Configuration options. - std::unique_ptr get_options(OptionsType type) final { + std::unique_ptr get_options() final { return nullptr; } diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 38313ea3e..0ca91006e 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -653,7 +653,7 @@ class ConcreteMachine: } // MARK: - Configuration options. - std::unique_ptr get_options(OptionsType type) final { + std::unique_ptr get_options() final { return nullptr; } diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 5fb0cb70b..614dc7d0f 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -378,7 +378,7 @@ class ConcreteMachine: } // MARK: - Configuration options. - std::unique_ptr get_options(OptionsType type) final { + std::unique_ptr get_options() final { return nullptr; } diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 7417e6ef2..8931be836 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -627,7 +627,7 @@ template class Co } // MARK: - Configuration options. - std::unique_ptr get_options(OptionsType type) final { + std::unique_ptr get_options() final { return nullptr; } diff --git a/Machines/Utility/MachineForTarget.cpp b/Machines/Utility/MachineForTarget.cpp index baa9e515b..7a50e60f8 100644 --- a/Machines/Utility/MachineForTarget.cpp +++ b/Machines/Utility/MachineForTarget.cpp @@ -176,7 +176,6 @@ std::vector Machine::AllMachines(bool meaningful_without_media_only std::map> Machine::AllOptionsByMachineName() { std::map> options; -// options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::AmstradCPC), AmstradCPC::get_options())); // options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::AppleII), Apple::II::get_options())); // options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::AtariST), Atari::ST::get_options())); // options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::ColecoVision), Coleco::Vision::get_options())); @@ -186,7 +185,14 @@ std::map> Machine::AllOptionsBy // options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::MSX), MSX::get_options())); // options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Oric), Oric::get_options())); // options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Vic20), Commodore::Vic20::get_options())); - options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::ZX8081), ZX8081::get_options())); + +#define Emplace(machine, class) \ + options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::machine), std::make_unique(Configurable::OptionsType::UserFriendly))); + + Emplace(AmstradCPC, AmstradCPC::Machine); + Emplace(ZX8081, ZX8081::Machine); + +#undef Emplace return options; } diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 4a47e40b6..6030b2a08 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -50,22 +50,6 @@ enum ROMType: uint8_t { ZX80 = 0, ZX81 }; -struct Options: public Reflection::StructImpl { - bool automatic_tape_motor_control = true; - bool quickload = true; - - Options() { - if(needs_declare()) { - DeclareField(automatic_tape_motor_control); - DeclareField(quickload); - } - } -}; - -std::unique_ptr get_options() { - return std::make_unique(); -} - template class ConcreteMachine: public CRTMachine::Machine, public MediaTarget::Machine, @@ -422,31 +406,18 @@ template class ConcreteMachine: } // MARK: - Configuration options. - std::unique_ptr get_options(OptionsType type) final { - auto options = std::make_unique(); - switch(type) { - case OptionsType::Current: - options->automatic_tape_motor_control = use_automatic_tape_motor_control_; - options->quickload = allow_fast_tape_hack_; - break; - case OptionsType::Accurate: - case OptionsType::UserFriendly: - options->automatic_tape_motor_control = - options->quickload = type == OptionsType::UserFriendly; - break; - } + std::unique_ptr get_options() final { + auto options = std::make_unique(Configurable::OptionsType::UserFriendly); // OptionsType is arbitrary, but not optional. + options->automatic_tape_motor_control = use_automatic_tape_motor_control_; + options->quickload = allow_fast_tape_hack_; return options; } - void set_options(const std::unique_ptr &options) { - if(Reflection::get(*options, "quickload", allow_fast_tape_hack_)) { - set_use_fast_tape(); - } - - bool autotapemotor; - if(Reflection::get(*options, "automatic_tape_motor_control", autotapemotor)) { - set_use_automatic_tape_motor_control(autotapemotor); - } + void set_options(const std::unique_ptr &str) { + const auto options = dynamic_cast(str.get()); + set_use_automatic_tape_motor_control(options->automatic_tape_motor_control); + allow_fast_tape_hack_ = options->quickload; + set_use_fast_tape(); } private: diff --git a/Machines/ZX8081/ZX8081.hpp b/Machines/ZX8081/ZX8081.hpp index f35d2b9c7..1bf00b34b 100644 --- a/Machines/ZX8081/ZX8081.hpp +++ b/Machines/ZX8081/ZX8081.hpp @@ -9,7 +9,7 @@ #ifndef ZX8081_hpp #define ZX8081_hpp -#include "../../Reflection/Struct.h" +#include "../../Configurable/Configurable.hpp" #include "../../Analyser/Static/StaticAnalyser.hpp" #include "../ROMMachine.hpp" @@ -17,9 +17,7 @@ namespace ZX8081 { -/// @returns The options available for a ZX80 or ZX81. -std::unique_ptr get_options(); - +/// The ZX80/81 machine. class Machine { public: virtual ~Machine(); @@ -28,6 +26,24 @@ class Machine { virtual void set_tape_is_playing(bool is_playing) = 0; virtual bool get_tape_is_playing() = 0; + + /// Defines the runtime options available for a ZX80/81. + class Options: public Reflection::StructImpl { + public: + bool automatic_tape_motor_control; + bool quickload; + + Options(Configurable::OptionsType type): + automatic_tape_motor_control(type == Configurable::OptionsType::UserFriendly), + quickload(automatic_tape_motor_control) { + + // Declare fields if necessary. + if(needs_declare()) { + DeclareField(automatic_tape_motor_control); + DeclareField(quickload); + } + } + }; }; } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index aac0802ab..b1163abdb 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -90,7 +90,7 @@ + isEnabled = "YES"> diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index d7cf2c4b0..b387b0c00 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -497,39 +497,42 @@ int main(int argc, char *argv[]) { 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 all_options = Machine::AllOptionsByMachineName(); -// for(const auto &machine_options: all_options) { -// std::cout << machine_options.first << ":" << std::endl; -// for(const auto &option: machine_options.second) { -// std::cout << '\t' << "--" << option->short_name; -// -// Configurable::ListOption *list_option = dynamic_cast(option.get()); -// if(list_option) { -// std::cout << "={"; -// bool is_first = true; -// for(const auto &option: list_option->options) { -// if(!is_first) std::cout << '|'; -// is_first = false; -// std::cout << option; -// } -// std::cout << "}"; -// } -// std::cout << std::endl; -// } -// std::cout << std::endl; -// } - const auto targets = Machine::TargetsByMachineName(false); - for(const auto &target: targets) { - const auto reflectable = dynamic_cast(target.second.get()); - if(!reflectable) continue; - const auto all_keys = reflectable->all_keys(); - if(all_keys.empty()) continue; + const auto runtime_options = Machine::AllOptionsByMachineName(); + const auto machine_names = Machine::AllMachines(false, true); + for(const auto &machine: machine_names) { + const auto target = targets.find(machine); + const auto options = runtime_options.find(machine); - std::cout << target.first << ":" << std::endl; - for(const auto &option: reflectable->all_keys()) { + const auto target_reflectable = dynamic_cast(target != targets.end() ? target->second.get() : nullptr); + const auto options_reflectable = dynamic_cast(options != runtime_options.end() ? options->second.get() : nullptr); + + // Don't print a section for this machine if it has no construction and no runtime options objects. + if(!target_reflectable && !options_reflectable) continue; + + const auto target_keys = target_reflectable ? target_reflectable->all_keys() : std::vector(); + const auto options_keys = options_reflectable ? options_reflectable->all_keys() : std::vector(); + + // Don't print a section for this machine if it doesn't actually have any options. + if(target_keys.empty() && options_keys.empty()) { + continue; + } + + std::cout << machine << ":" << std::endl; + + // Join the two lists of properties. + std::vector all_options = options_keys; + all_options.insert(all_options.end(), target_keys.begin(), target_keys.end()); + + for(const auto &option: all_options) { std::cout << '\t' << "--" << option; - const auto type = reflectable->type_of(option); + + bool is_construction_option = true; + auto type = target_reflectable->type_of(option); + if(!type) { + is_construction_option = false; + type = options_reflectable->type_of(option); + } // Is this a registered enum? If so, list options. if(!Reflection::Enum::name(*type).empty()) { @@ -544,7 +547,9 @@ int main(int argc, char *argv[]) { } // TODO: if not a registered enum... then assume it was a Boolean? - std::cout << "\t*" << std::endl; + + if(is_construction_option) std::cout << "\t*"; + std::cout << std::endl; } std::cout << std::endl; diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index b624fa6cf..8f5b0321f 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -93,6 +93,10 @@ void CRT::set_display_type(Outputs::Display::DisplayType display_type) { scan_target_->set_modals(scan_target_modals_); } +Outputs::Display::DisplayType CRT::get_display_type() { + return scan_target_modals_.display_type; +} + void CRT::set_phase_linked_luminance_offset(float offset) { scan_target_modals_.input_data_tweaks.phase_linked_luminance_offset = offset; scan_target_->set_modals(scan_target_modals_); diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index be07a50ba..9988968f5 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -298,6 +298,9 @@ class CRT { /*! Sets the display type that will be nominated to the scan target. */ void set_display_type(Outputs::Display::DisplayType); + /*! Gets the last display type provided to set_display_type. */ + Outputs::Display::DisplayType get_display_type(); + /*! Sets the offset to apply to phase when using the PhaseLinkedLuminance8 input data type. */ void set_phase_linked_luminance_offset(float); diff --git a/Reflection/Enum.h b/Reflection/Enum.h index 890032062..412fb955a 100644 --- a/Reflection/Enum.h +++ b/Reflection/Enum.h @@ -26,6 +26,7 @@ namespace Reflection { #define EnumDeclaration(Name) #Name, __declaration##Name #define AnnounceEnum(Name) ::Reflection::Enum::declare(EnumDeclaration(Name)) +#define AnnounceEnumNS(Namespace, Name) ::Reflection::Enum::declare(#Name, Namespace::__declaration##Name) /*! This provides a very slight version of enum reflection; you can introspect only: