1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-09 17:29:36 +00:00

Applies parsed arguments.

This commit is contained in:
Thomas Harte 2020-03-18 22:31:32 -04:00
parent 311458f41f
commit 615ea2f573
5 changed files with 119 additions and 59 deletions

View File

@ -82,7 +82,7 @@
</CommandLineArgument>
<CommandLineArgument
argument = "--speed=5"
isEnabled = "YES">
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "--rompath=/Users/thomasharte/Projects/CLK/ROMImages"
@ -90,6 +90,10 @@
</CommandLineArgument>
<CommandLineArgument
argument = "--help"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "--model=cpc6128"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>

View File

@ -39,9 +39,6 @@
namespace {
/// Takes an enum-style camel-case string (e.g. Mac128k) and
//std::string
struct MachineRunner {
MachineRunner() {
frame_lock_.clear();
@ -473,7 +470,7 @@ int main(int argc, char *argv[]) {
SDL_Window *window = nullptr;
// Attempt to parse arguments.
ParsedArguments arguments = parse_arguments(argc, argv);
const ParsedArguments arguments = parse_arguments(argc, argv);
// This may be printed either as
const std::string usage_suffix = " [file or --new={machine}] [OPTIONS] [--rompath={path to ROMs}] [--speed={speed multiplier, e.g. 1.5}] [--logical-keyboard]";
@ -622,12 +619,13 @@ int main(int argc, char *argv[]) {
"/usr/local/share/CLK/",
"/usr/share/CLK/"
};
if(arguments.selections.find("rompath") != arguments.selections.end()) {
const std::string user_path = arguments.selections["rompath"];
if(user_path.back() != '/') {
paths.push_back(user_path + "/");
const auto rompath = arguments.selections.find("rompath");
if(rompath != arguments.selections.end()) {
if(rompath->second.back() != '/') {
paths.push_back(rompath->second + "/");
} else {
paths.push_back(user_path);
paths.push_back(rompath->second);
}
}
@ -662,6 +660,20 @@ int main(int argc, char *argv[]) {
return results;
};
// Apply all command-line options to the targets.
for(auto &target: targets) {
auto reflectable_target = dynamic_cast<Reflection::Struct *>(target.get());
if(!reflectable_target) continue;
for(const auto &argument: arguments.selections) {
if(argument.second.empty()) {
Reflection::set<bool>(*reflectable_target, argument.first, true);
} else {
Reflection::fuzzy_set(*reflectable_target, argument.first, argument.second);
}
}
}
// Create and configure a machine.
::Machine::Error error;
std::mutex machine_mutex;
@ -685,19 +697,36 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
// Apply all command-line options to the machines.
auto configurable = machine->configurable_device();
if(configurable) {
const auto options = configurable->get_options();
for(const auto &argument: arguments.selections) {
if(argument.second.empty()) {
Reflection::set<bool>(*options, argument.first, true);
} else {
Reflection::fuzzy_set(*options, argument.first, argument.second);
}
}
configurable->set_options(options);
}
// Apply the speed multiplier, if one was requested.
if(arguments.selections.find("speed") != arguments.selections.end()) {
// const char *speed_string = arguments.selections["speed"]->list_selection()->value.c_str();
// char *end;
// double speed = strtod(speed_string, &end);
//
// if(size_t(end - speed_string) != strlen(speed_string)) {
// std::cerr << "Unable to parse speed: " << speed_string << std::endl;
// } else if(speed <= 0.0) {
// std::cerr << "Cannot run at speed " << speed_string << "; speeds must be positive." << std::endl;
// } else {
// machine_runner.set_speed_multiplier(speed);
// }
const auto speed_argument = arguments.selections.find("speed");
if(speed_argument != arguments.selections.end()) {
const char *speed_string = speed_argument->second.c_str();
char *end;
double speed = strtod(speed_string, &end);
if(size_t(end - speed_string) != strlen(speed_string)) {
std::cerr << "Unable to parse speed: " << speed_string << std::endl;
} else if(speed <= 0.0) {
std::cerr << "Cannot run at speed " << speed_string << "; speeds must be positive." << std::endl;
} else {
machine_runner.set_speed_multiplier(speed);
}
}
// Check whether a 'logical' keyboard has been requested.
@ -775,31 +804,6 @@ int main(int argc, char *argv[]) {
int window_width, window_height;
SDL_GetWindowSize(window, &window_width, &window_height);
/* Configurable::Device *const configurable_device = machine->configurable_device();
if(configurable_device) {
// Establish user-friendly options by default.
configurable_device->set_selections(configurable_device->get_user_friendly_selections());
// Consider transcoding any list selections that map to Boolean options.
for(const auto &option: configurable_device->get_options()) {
// Check for a corresponding selection.
auto selection = arguments.selections.find(option->short_name);
if(selection != arguments.selections.end()) {
// Transcode selection if necessary.
if(dynamic_cast<Configurable::BooleanOption *>(option.get())) {
arguments.selections[selection->first] = std::unique_ptr<Configurable::Selection>(selection->second->boolean_selection());
}
if(dynamic_cast<Configurable::ListOption *>(option.get())) {
arguments.selections[selection->first] = std::unique_ptr<Configurable::Selection>(selection->second->list_selection());
}
}
}
// Apply the user's actual selections to final the defaults.
configurable_device->set_selections(arguments.selections);
}*/
// If this is a joystick machine, check for and open attached joysticks.
/*!
Provides a wrapper for SDL_Joystick pointers that can keep track

View File

@ -113,13 +113,13 @@ class Enum {
@returns A @c std::string name for the enum value @c e.
*/
template <typename Type> static const std::string &to_string(Type e) {
return to_string(typeid(Type), size_t(e));
return to_string(typeid(Type), int(e));
}
/*!
@returns A @c std::string name for the enum value @c e from the enum with type_info @c type.
*/
static const std::string &to_string(std::type_index type, size_t e) {
static const std::string &to_string(std::type_index type, int e) {
const auto entry = members_by_type_.find(type);
if(entry == members_by_type_.end()) return empty_string_;
return entry->second[e];
@ -150,15 +150,15 @@ class Enum {
}
/*!
@returns A value for the name @c str in the enum with type_info @c type , or @c std::string::npos if
@returns A value for the name @c str in the enum with type_info @c type , or @c -1 if
the name is not found.
*/
static size_t from_string(std::type_index type, const std::string &str) {
static int from_string(std::type_index type, const std::string &str) {
const auto entry = members_by_type_.find(type);
if(entry == members_by_type_.end()) return std::string::npos;
if(entry == members_by_type_.end()) return -1;
const auto iterator = std::find(entry->second.begin(), entry->second.end(), str);
if(iterator == entry->second.end()) return std::string::npos;
return size_t(iterator - entry->second.begin());
if(iterator == entry->second.end()) return -1;
return int(iterator - entry->second.begin());
}
private:

View File

@ -8,6 +8,8 @@
#include "Struct.h"
#include <algorithm>
// MARK: - Setters
template <> bool Reflection::set(Struct &target, const std::string &name, int value) {
@ -38,13 +40,11 @@ template <> bool Reflection::set(Struct &target, const std::string &name, const
return false;
}
const auto enum_value = Reflection::Enum::from_string(*target_type, value);
if(enum_value == std::string::npos) {
const int enum_value = Reflection::Enum::from_string(*target_type, value);
if(enum_value < 0) {
return false;
}
int int_value = int(enum_value);
target.set(name, &int_value);
target.set(name, &enum_value);
return true;
}
@ -54,9 +54,54 @@ template <> bool Reflection::set(Struct &target, const std::string &name, const
return set<const std::string &>(target, name, string);
}
template <> bool Reflection::set(Struct &target, const std::string &name, bool value) {
const auto target_type = target.type_of(name);
if(!target_type) return false;
if(*target_type == typeid(bool)) {
target.set(name, &value);;
}
return false;
}
// MARK: - Fuzzy setter
bool Reflection::fuzzy_set(Struct &target, const std::string &name, const std::string &value) {
const auto target_type = target.type_of(name);
if(!target_type) return false;
// If the target is a registered enum, ttry to convert the value. Failing that,
// try to match without case sensitivity.
if(Reflection::Enum::size(*target_type)) {
const int from_string = Reflection::Enum::from_string(*target_type, value);
if(from_string >= 0) {
target.set(name, &from_string);
return true;
}
const auto all_values = Reflection::Enum::all_values(*target_type);
const auto value_location = std::find_if(all_values.begin(), all_values.end(),
[&value] (const auto &entry) {
if(value.size() != entry.size()) return false;
const char *v = value.c_str();
const char *e = entry.c_str();
while(*v) {
if(tolower(*v) != tolower(*e)) return false;
++v;
++e;
}
return true;
});
if(value_location != all_values.end()) {
const int offset = int(value_location - all_values.begin());
target.set(name, &offset);
return true;
}
return false;
}
return false;
}

View File

@ -56,6 +56,13 @@ template <> bool set(Struct &target, const std::string &name, int value);
template <> bool set(Struct &target, const std::string &name, const std::string &value);
template <> bool set(Struct &target, const std::string &name, const char *value);
/*!
Setting a bool:
* to a bool, copies the value.
*/
template <> bool set(Struct &target, const std::string &name, bool value);
/*!
Fuzzy-set attempts to set any property based on a string value. This is intended to allow input provided by the user.