Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an API to reset/unset options and expose it at CLI level #1640

Merged
merged 7 commits into from
Sep 30, 2024
Merged
11 changes: 10 additions & 1 deletion cmake/f3dOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ function (f3d_generate_options)
list(JOIN _options_string_setter ";\n else " _options_string_setter)
list(JOIN _options_string_getter ";\n else " _options_string_getter)
list(JOIN _options_lister ",\n " _options_lister)
list(JOIN _options_is_optional ";\n else " _options_is_optional)
list(JOIN _options_reset ";\n else " _options_reset)

configure_file(
"${_f3d_generate_options_INPUT_PUBLIC_HEADER}"
Expand Down Expand Up @@ -133,12 +135,17 @@ function(_parse_json_option _top_json)

if(_default_value_error STREQUAL "NOTFOUND")
# Use default_value
string(APPEND _options_struct "${_option_indent} ${_option_actual_type} ${_member_name} = ${_option_default_value_start}${_option_default_value}${_option_default_value_end};\n")
set(_optional_default_value_initialize "${_option_default_value_start}${_option_default_value}${_option_default_value_end}")
string(APPEND _options_struct "${_option_indent} ${_option_actual_type} ${_member_name} = ${_optional_default_value_initialize};\n")
set(_optional_getter "")
list(APPEND _options_is_optional "if (name == \"${_option_name}\") return false")
list(APPEND _options_reset "if (name == \"${_option_name}\") opt.${_option_name} = ${_optional_default_value_initialize}")
else()
# No default_value, it is an std::optional
string(APPEND _options_struct "${_option_indent} std::optional<${_option_actual_type}> ${_member_name};\n")
set(_optional_getter ".value()")
list(APPEND _options_is_optional "if (name == \"${_option_name}\") return true")
list(APPEND _options_reset "if (name == \"${_option_name}\") opt.${_option_name}.reset()")
endif()

list(APPEND _options_setter "if (name == \"${_option_name}\") opt.${_option_name} = std::get<${_option_variant_type}>(value)")
Expand Down Expand Up @@ -168,4 +175,6 @@ function(_parse_json_option _top_json)
set(_options_string_setter ${_options_string_setter} PARENT_SCOPE)
set(_options_string_getter ${_options_string_getter} PARENT_SCOPE)
set(_options_lister ${_options_lister} PARENT_SCOPE)
set(_options_is_optional ${_options_is_optional} PARENT_SCOPE)
set(_options_reset ${_options_reset} PARENT_SCOPE)
endfunction()
25 changes: 25 additions & 0 deletions library/private/options_tools.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,31 @@ std::string getAsString(const options& opt, const std::string& name)
throw options::no_value_exception("Trying to get " + name + " before it was set");
}
}

//----------------------------------------------------------------------------
/**
* Generated method, see `options::isOptional`
*/
bool isOptional(const std::string& name)
{
// clang-format off
${_options_is_optional};
// clang-format on
else throw options::inexistent_exception("Option " + name + " does not exist");
}

//----------------------------------------------------------------------------
/**
* Generated method, see `options::reset`
*/
void reset(options& opt, const std::string& name)
{
// clang-format off
${_options_reset};
// clang-format on
else throw options::inexistent_exception("Option " + name + " does not exist");
}

} // option_tools
} // f3d
#endif // f3d_options_tools_h
18 changes: 18 additions & 0 deletions library/public/options.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,24 @@ public:
*/
std::pair<std::string, unsigned int> getClosestOption(const std::string& option) const;

/**
* Returns true if the option is optional else returns false.
* Throws an options::inexistent_exception if option does not exist.
*/
bool isOptional(const std::string& option) const;

/**
* Resets the option to default value.
* Throws an options::inexistent_exception if option does not exist.
*/
void reset(const std::string& name);

/**
* Unset the option if it is optional else throws options::incompatible_exception.
* Throws an options::inexistent_exception if option does not exist.
mwestphal marked this conversation as resolved.
Show resolved Hide resolved
*/
void removeValue(const std::string& name);

mwestphal marked this conversation as resolved.
Show resolved Hide resolved
/**
* Templated parsing method used internally to parse strings.
* Implemented for:
Expand Down
25 changes: 25 additions & 0 deletions library/src/options.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,31 @@ std::pair<std::string, unsigned int> options::getClosestOption(const std::string
return ret;
}

//----------------------------------------------------------------------------
bool options::isOptional(const std::string& option) const
mwestphal marked this conversation as resolved.
Show resolved Hide resolved
{
return options_tools::isOptional(option);
}

//----------------------------------------------------------------------------
void options::reset(const std::string& name)
mwestphal marked this conversation as resolved.
Show resolved Hide resolved
{
options_tools::reset(*this, name);
}

//----------------------------------------------------------------------------
void options::removeValue(const std::string& name)
mwestphal marked this conversation as resolved.
Show resolved Hide resolved
{
if (isOptional(name))
mwestphal marked this conversation as resolved.
Show resolved Hide resolved
{
reset(name);
mwestphal marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
throw options::incompatible_exception("Option " + name + " is not not optional");
}
}

//----------------------------------------------------------------------------
template<typename T>
T options::parse(const std::string& str)
Expand Down
47 changes: 47 additions & 0 deletions library/testing/TestSDKOptions.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,52 @@ int TestSDKOptions(int argc, char* argv[])
test.expect<f3d::options::no_value_exception>("no_value_exception exception on getAsString",
[&]() { opt.getAsString("scene.animation.time"); });

f3d::options opt6{};

// Test isOptional optional values
test("isOptional with optional value", opt6.isOptional("model.scivis.array_name"));
test("isOptional with optional value", opt6.isOptional("model.scivis.range"));

// Test isOptional non-optional values
test("isOptional with non-optional value", opt6.isOptional("model.scivis.cells") == false);
test("isOptional with non-optional value", opt6.isOptional("model.scivis.enable") == false);

// Test isOptional non-existent options
test.expect<f3d::options::inexistent_exception>(
"isOptional with non-existent option", [&]() { opt6.isOptional("dummy"); });

f3d::options opt7{};

// Test reset non-optional values
opt7.model.color.opacity = 0.5;
opt7.reset("model.color.opacity");
test("reset non-optional values", opt7.model.color.opacity == 1.0);

// Test reset optional values
opt7.model.scivis.array_name = "dummy";
opt7.reset("model.scivis.array_name");
test.expect<f3d::options::no_value_exception>(
"reset non-optional values", [&]() { opt7.get("model.scivis.array_name"); });

// Test reset non-existent option
test.expect<f3d::options::inexistent_exception>(
"reset with non-existent option", [&]() { opt7.reset("dummy"); });

f3d::options opt8{};

// Test removeValue optional values
opt8.model.scivis.array_name = "dummy";
opt8.removeValue("model.scivis.array_name");
test.expect<f3d::options::no_value_exception>(
"removeValue optional values", [&]() { opt8.get("model.scivis.array_name"); });

// Test removeValue non-optional values
test.expect<f3d::options::incompatible_exception>(
"removeValue non-optional values", [&]() { opt8.removeValue("model.scivis.cells"); });

// Test removeValue non-optional values
test.expect<f3d::options::inexistent_exception>(
"removeValue non-existent option", [&]() { opt8.removeValue("dummy"); });

return EXIT_SUCCESS;
}