Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion application/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ f3d_generate_cli_options(
F3D_SOURCE_DIR "${F3D_SOURCE_DIR}"
F3D_BINARY_DIR "${F3D_BINARY_DIR}"
ENABLED_CONDITIONALS ${f3d_enabled_conditionals} # Do NOT quote so this stays a list
CPP_CLI_OPTIONS_TO_SKIP input;define;reset # Do NOT quote so this stays a list
CPP_CLI_OPTIONS_TO_SKIP input # Do NOT quote so this stays a list
)

set(F3D_SOURCE_FILES
Expand Down
126 changes: 79 additions & 47 deletions application/F3DOptionsTools.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ namespace
{
/**
* True boolean options need to be filtered out in ParseCLIOptions
* Also filter out special options like `define` and `reset`
* This is the easiest, compile time way to do it
*/
constexpr std::array CLIBooleans = { "version", "help", "list-readers", "scan-plugins",
"list-rendering-backends", "define", "reset" };
"list-rendering-backends" };

//----------------------------------------------------------------------------
/**
Expand Down Expand Up @@ -275,12 +274,6 @@ F3DOptionsTools::OptionsDict F3DOptionsTools::ParseCLIOptions(
std::vector<std::shared_ptr<cxxopts::Value>> cxxoptsValues;
auto cxxoptsInputPositionals = cxxopts::value<std::vector<std::string>>(positionals);

std::vector<std::string> defines;
auto cxxoptsDefines = cxxopts::value<std::vector<std::string>>(defines);

std::vector<std::string> resets;
auto cxxoptsResets = cxxopts::value<std::vector<std::string>>(resets);

try
{
cxxopts::Options cxxOptions(execName, F3D::AppTitle);
Expand All @@ -294,8 +287,6 @@ F3DOptionsTools::OptionsDict F3DOptionsTools::ParseCLIOptions(
if (std::string(optionGroup.GroupName) == "Applicative")
{
group("input", "Input files", cxxoptsInputPositionals, "<files>");
group("D,define", "Define libf3d options", cxxoptsDefines, "libf3d.option=value");
group("R,reset", "Reset libf3d options", cxxoptsResets, "libf3d.option");
}

// Add each option to cxxopts
Expand All @@ -313,12 +304,15 @@ F3DOptionsTools::OptionsDict F3DOptionsTools::ParseCLIOptions(
std::string defaultValue;
std::string helpText(cliOption.HelpText);
std::string longName(cliOption.LongName);
std::string valueHelper(cliOption.ValueHelper);

// Recover default value from app options
auto appIter = F3DOptionsTools::DefaultAppOptions.find(longName);
if (appIter != F3DOptionsTools::DefaultAppOptions.end())
{
defaultValue = appIter->second;
// defaults are always stored as strings
assert(std::holds_alternative<std::string>(appIter->second));
defaultValue = std::get<std::string>(appIter->second);
}
else
{
Expand Down Expand Up @@ -351,7 +345,14 @@ F3DOptionsTools::OptionsDict F3DOptionsTools::ParseCLIOptions(
}

// Recover the implicit value and set it if any
cxxoptsValues.emplace_back(cxxopts::value<std::string>());
if (valueHelper == "<string_list>")
{
cxxoptsValues.emplace_back(cxxopts::value<std::vector<std::string>>());
}
else
{
cxxoptsValues.emplace_back(cxxopts::value<std::string>());
}
auto& val = cxxoptsValues.back();
if (!cliOption.ImplicitValue.empty())
{
Expand Down Expand Up @@ -438,33 +439,34 @@ F3DOptionsTools::OptionsDict F3DOptionsTools::ParseCLIOptions(
throw F3DExFailure("unknown options");
}

// Add each CLI options into a vector of string/string and return it
// Add each CLI options into a OptionsDict and return it
F3DOptionsTools::OptionsDict cliOptionsDict;
for (const auto& res : result)
{
// Discard boolean option like `--version` or `--help`
if (std::ranges::find(::CLIBooleans, res.key()) == ::CLIBooleans.end())
if (std::ranges::find(::CLIBooleans, res.key()) != ::CLIBooleans.end())
{
continue;
}
auto iter = cliOptionsDict.find(res.key());
if (iter == cliOptionsDict.end())
{
cliOptionsDict[res.key()] = res.value();
}
}

// Handle defines and add them as proper options
for (const std::string& define : defines)
{
std::string::size_type sepIdx = define.find_first_of('=');
if (sepIdx == std::string::npos)
else
{
f3d::log::warn("Could not parse a define '", define, "'");
continue;
// key already exists. Create or append to vector
Comment thread
mwestphal marked this conversation as resolved.
if (std::holds_alternative<std::vector<std::string>>(iter->second))
{
auto& vec = std::get<std::vector<std::string>>(iter->second);
vec.push_back(res.value());
}
else
{
auto& item = std::get<std::string>(iter->second);
cliOptionsDict[res.key()] = std::vector<std::string>{ item, res.value() };
}
}
cliOptionsDict[define.substr(0, sepIdx)] = define.substr(sepIdx + 1);
}

// Handles reset using the dedicated syntax
for (const std::string& reset : resets)
{
cliOptionsDict["reset-" + reset] = "";
}

return cliOptionsDict;
Expand All @@ -491,10 +493,11 @@ void F3DOptionsTools::PrintHelpPair(
}

//----------------------------------------------------------------------------
std::vector<std::pair<std::string, std::string>> F3DOptionsTools::ConvertToLibf3dOptions(
const std::string& key, const std::string& value)
std::vector<std::pair<std::string, F3DOptionsTools::OptionValue>>
F3DOptionsTools::ConvertToLibf3dOptions(const std::string& key, const OptionValue& value)
{
std::vector<std::pair<std::string, std::string>> libf3dOptions;
std::vector<std::pair<std::string, F3DOptionsTools::OptionValue>> libf3dOptions;
[[maybe_unused]] const bool is_single_valued = std::holds_alternative<std::string>(value);

// Simple one-to-one case
auto libf3dIter = F3DOptionsTools::LibOptionsNames.find(key);
Expand All @@ -506,20 +509,22 @@ std::vector<std::pair<std::string, std::string>> F3DOptionsTools::ConvertToLibf3
// anti-aliasing is handled in two options in the lib
else if (key == "anti-aliasing")
{
if (value != "none")
assert(is_single_valued);
const std::string& value_str = std::get<std::string>(value);
if (value_str != "none")
{
// Handle deprecated boolean option
bool deprecatedBooleanOption;
if (F3DOptionsTools::Parse(value, deprecatedBooleanOption))
if (F3DOptionsTools::Parse(value_str, deprecatedBooleanOption))
{
f3d::log::warn("--anti-aliasing is a now a string, please specify the type of "
"anti-aliasing or use the implicit default");
libf3dOptions.emplace_back(std::make_pair("render.effect.antialiasing.enable", value));
libf3dOptions.emplace_back(std::make_pair("render.effect.antialiasing.enable", value_str));
}
else
{
libf3dOptions.emplace_back(std::make_pair("render.effect.antialiasing.enable", "true"));
libf3dOptions.emplace_back(std::make_pair("render.effect.antialiasing.mode", value));
libf3dOptions.emplace_back(std::make_pair("render.effect.antialiasing.mode", value_str));
}
}
else
Expand All @@ -531,17 +536,21 @@ std::vector<std::pair<std::string, std::string>> F3DOptionsTools::ConvertToLibf3
// handle deprecated anti-aliasing option
else if (key == "anti-aliasing-mode")
{
assert(is_single_valued);
const std::string& value_str = std::get<std::string>(value);
f3d::log::warn("--anti-aliasing-mode is deprecated");
libf3dOptions.emplace_back(std::make_pair("render.effect.antialiasing.mode", value));
libf3dOptions.emplace_back(std::make_pair("render.effect.antialiasing.mode", value_str));
}

// blending is handled in two options in the lib
else if (key == "blending")
{
if (value != "none")
assert(is_single_valued);
const std::string& value_str = std::get<std::string>(value);
if (value_str != "none")
{
libf3dOptions.emplace_back(std::make_pair("render.effect.blending.enable", "true"));
libf3dOptions.emplace_back(std::make_pair("render.effect.blending.mode", value));
libf3dOptions.emplace_back(std::make_pair("render.effect.blending.mode", value_str));
}
else
{
Expand All @@ -552,27 +561,31 @@ std::vector<std::pair<std::string, std::string>> F3DOptionsTools::ConvertToLibf3
// handle deprecated translucency support
else if (key == "translucency-support")
{
assert(is_single_valued);
const std::string& value_str = std::get<std::string>(value);
f3d::log::warn("--translucency-support is deprecated, please use --blending instead");
libf3dOptions.emplace_back(std::make_pair("render.effect.blending.enable", value));
libf3dOptions.emplace_back(std::make_pair("render.effect.blending.enable", value_str));
}

// point sprites is handled in two options in the lib
else if (key == "point-sprites")
{
if (value != "none")
assert(is_single_valued);
const std::string& value_str = std::get<std::string>(value);
if (value_str != "none")
{
// Handle deprecated boolean option
bool deprecatedBooleanOption;
if (F3DOptionsTools::Parse(value, deprecatedBooleanOption))
{
f3d::log::warn("--point-sprites is a now a string, please specify the type of "
"point sprites to use or use the implicit default");
libf3dOptions.emplace_back(std::make_pair("model.point_sprites.enable", value));
libf3dOptions.emplace_back(std::make_pair("model.point_sprites.enable", value_str));
}
else
{
libf3dOptions.emplace_back(std::make_pair("model.point_sprites.enable", "true"));
libf3dOptions.emplace_back(std::make_pair("model.point_sprites.type", value));
libf3dOptions.emplace_back(std::make_pair("model.point_sprites.type", value_str));
}
}
else
Expand All @@ -584,19 +597,22 @@ std::vector<std::pair<std::string, std::string>> F3DOptionsTools::ConvertToLibf3
// handle deprecated point-sprites-type option
else if (key == "point-sprites-type")
{
assert(is_single_valued);
const std::string& value_str = std::get<std::string>(value);
f3d::log::warn("--point-sprites-type is deprecated");
libf3dOptions.emplace_back(std::make_pair("model.point_sprites.type", value));
libf3dOptions.emplace_back(std::make_pair("model.point_sprites.type", value_str));
}

// handle deprecated interaction-trackball option
else if (key == "interaction-trackball")
{
const std::string& value_str = std::get<std::string>(value);
f3d::log::warn(
"--interaction-trackball is deprecated, please use --interaction-style=trackball instead");
bool trackball;
if (!F3DOptionsTools::Parse(value, trackball))
if (!F3DOptionsTools::Parse(value_str, trackball))
{
f3d::log::error("Cannot parse --interaction-trackball value: " + value);
f3d::log::error("Cannot parse --interaction-trackball value: " + value_str);
}
else if (trackball)
{
Expand All @@ -612,3 +628,19 @@ std::vector<std::pair<std::string, std::string>> F3DOptionsTools::ConvertToLibf3

return libf3dOptions;
}

std::string F3DOptionsTools::ConvertToString(const F3DOptionsTools::OptionValue& optionValue)
{
if (std::holds_alternative<std::string>(optionValue))
{
return std::get<std::string>(optionValue);
}
else if (std::holds_alternative<std::vector<std::string>>(optionValue))
{
const auto& vec = std::get<std::vector<std::string>>(optionValue);
auto concatenate = [](const std::string& result, const std::string& value)
{ return result + " , " + value; };
return std::accumulate(vec.cbegin(), vec.cend(), std::string{ "" }, concatenate);
}
return "";
}
23 changes: 18 additions & 5 deletions application/F3DOptionsTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@

#include <filesystem>
#include <map>
#include <set>
#include <string>
#include <tuple>
#include <vector>

namespace F3DOptionsTools
{
using OptionsDict = std::map<std::string, std::string>;
using OptionValue = std::variant<std::vector<std::string>, std::string>;
using OptionsDict = std::map<std::string, OptionValue>;
using OptionsEntry = std::tuple<OptionsDict, std::string, std::string, std::string>;
using OptionsEntries = std::vector<OptionsEntry>;

Expand All @@ -27,7 +29,7 @@ using OptionsEntries = std::vector<OptionsEntry>;
* and ParseCLIOptions
*/
static inline const OptionsDict DefaultAppOptions = {
{ "input", "" },
{ "input", { "" } },
{ "output", "" },
{ "list-bindings", "false" },
{ "no-background", "false" },
Expand Down Expand Up @@ -167,11 +169,16 @@ static inline const std::map<std::string_view, std::string_view> CustomMappingOp
{ "point-sprites-type", "" },
};

/**
* List of CLI option names that can accept multiple values
*/
static inline const std::set<std::string_view> MultiValueOptions = { "define", "input", "reset" };

/**
* Convert a CLI options key/value to a vector of libf3d options key/value
*/
std::vector<std::pair<std::string, std::string>> ConvertToLibf3dOptions(
const std::string& key, const std::string& value);
std::vector<std::pair<std::string, F3DOptionsTools::OptionValue>> ConvertToLibf3dOptions(
const std::string& key, const OptionValue& value);

/**
* Browse through all possible option names to find one that have the smallest distance to the
Expand All @@ -196,13 +203,19 @@ F3DOptionsTools::OptionsDict ParseCLIOptions(
void PrintHelpPair(
std::string_view key, std::string_view help, int keyWidth = 10, int helpWidth = 70);

/**
* Convert the variant-based OptionValue to a string. Useful for debugging
*/
std::string ConvertToString(const OptionValue& optionValue);

/**
* Parse provided string into provided typed var.
* Return true if successful, false otherwise.
*/
template<typename T>
bool Parse(const std::string& optionString, T& option)
bool Parse(const OptionValue& optionValue, T& option)
{
const std::string optionString = ConvertToString(optionValue);
try
{
option = f3d::options::parse<T>(optionString);
Expand Down
Loading
Loading