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

options: Add options::format template method #1857

Merged
merged 4 commits into from
Jan 3, 2025
Merged
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
37 changes: 15 additions & 22 deletions library/private/options_tools.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -194,35 +194,31 @@ std::string parse(const std::string& str)
// TODO Improve string generation
//----------------------------------------------------------------------------
/**
* Generic templated string generation from provided value
* rely on std::to_string
* Format provided var into a string from provided boolean
* using boolalpha formatting, eg: "true" or "false"
*/
template<typename T>
std::string format(const T& var)
std::string format(bool var)
{
return std::to_string(var);
std::stringstream stream;
stream << std::boolalpha << var;
return stream.str();
}

//----------------------------------------------------------------------------
/**
* Format provided var into a string from provided boolean
* using boolalpha formatting, eg: "true" or "false"
* Format provided var into a string from provided int
*/
template<>
std::string format(const bool& var)
std::string format(int var)
{
std::stringstream stream;
stream << std::boolalpha << var;
return stream.str();
return std::to_string(var);
}

//----------------------------------------------------------------------------
/**
* Format provided var into a string from provided double
* using ostringstream with std::noshowpoint
*/
template<>
std::string format(const double& var)
std::string format(double var)
{
std::ostringstream stream;
stream << std::noshowpoint << var;
Expand All @@ -234,19 +230,16 @@ std::string format(const double& var)
* Format provided var into a string from provided ratio_t
* rely on format(double&)
*/
template<>
std::string format(const ratio_t& var)
std::string format(ratio_t var)
{
// TODO generate a proper ratio string
double val = var;
return options_tools::format(val);
return options_tools::format(static_cast<double>(var));
}

//----------------------------------------------------------------------------
/**
* Generate (returns) a string from provided string
*/
template<>
std::string format(const std::string& var)
{
return var;
Expand All @@ -257,12 +250,12 @@ std::string format(const std::string& var)
* Format provided var into a string from provided double vector
* rely on format(double&) and add `, ` between the double values
*/
template<>
std::string format(const std::vector<double>& var)
template<typename T>
std::string format(const std::vector<T>& var)
{
std::ostringstream stream;
unsigned int i = 0;
for (auto& elem : var)
for (const T& elem : var)
{
stream << ((i > 0) ? "," : "") << options_tools::format(elem);
i++;
Expand Down
8 changes: 8 additions & 0 deletions library/public/options.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ public:
template<typename T>
[[nodiscard]] static T parse(const std::string& str);

/**
* Templated parsing method used internally to format var into strings.
* Implemented for the different supported types,
* see PARSING.md for more info.
*/
template<typename T>
[[nodiscard]] static std::string format(const T& var);

/**
* An exception that can be thrown by the options
* when parsing of a string into an option value fails
Expand Down
10 changes: 9 additions & 1 deletion library/src/options.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,17 @@ T options::parse(const std::string& str)
return options_tools::parse<T>(str);
}

//----------------------------------------------------------------------------
template<typename T>
std::string options::format(const T& var)
{
return options_tools::format(var);
}

//----------------------------------------------------------------------------
#define F3D_DECL_TYPE_INTERNAL(TYPE) \
template F3D_EXPORT TYPE options::parse<TYPE>(const std::string& str)
template F3D_EXPORT TYPE options::parse<TYPE>(const std::string& str); \
template F3D_EXPORT std::string options::format<TYPE>(const TYPE& val)
#define F3D_DECL_TYPE(TYPE) \
F3D_DECL_TYPE_INTERNAL(TYPE); \
F3D_DECL_TYPE_INTERNAL(std::vector<TYPE>)
Expand Down
36 changes: 33 additions & 3 deletions library/testing/TestSDKOptionsIO.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class ParsingTest : public PseudoUnitTest
template<typename T>
void parse(const std::string& label, const std::string& input, const T& expected)
{
PseudoUnitTest::operator()(label + " `" + input + "`", [&]() {
PseudoUnitTest::operator()("parse: " + label + " `" + input + "`", [&]() {
const T actual = f3d::options::parse<T>(input);
if (actual != expected)
{
Expand All @@ -23,8 +23,20 @@ class ParsingTest : public PseudoUnitTest
template<typename T, typename E>
void parse_expect(const std::string& label, const std::string& input)
{
PseudoUnitTest::expect<E>(
label + " `" + input + "`", [&]() { std::ignore = f3d::options::parse<T>(input); });
PseudoUnitTest::expect<E>("parse exception: " + label + " `" + input + "`",
[&]() { std::ignore = f3d::options::parse<T>(input); });
}

template<typename T>
void format(const std::string& label, const T& input, const std::string& expected)
{
PseudoUnitTest::operator()("format: " + label + " `" + expected + "`", [&]() {
const std::string actual = f3d::options::format<T>(input);
if (actual != expected)
{
throw this->comparisonMessage(actual, expected, "!=");
}
});
}
};

Expand All @@ -42,22 +54,31 @@ int TestSDKOptionsIO(int argc, char* argv[])
test.parse<bool>("bool", "0", false);
test.parse_expect<bool, parsing_exception>("invalid bool", "foo");

test.format<bool>("bool", true, "true");
test.format<bool>("bool", false, "false");

test.parse<int>("int", "123", 123);
test.parse<int>("int", "-123", -123);
test.parse<int>("int", "+123", +123);
test.parse_expect<int, parsing_exception>("invalid int", "1.2");
test.parse_expect<int, parsing_exception>("invalid int", "abc");
test.parse_expect<int, parsing_exception>("invalid int",
"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
test.format<int>("int", 123, "123");
test.format<int>("int", -123, "-123");

test.parse<double>("double", "123", 123.0);
test.parse<double>("double", "-123.45", -123.45);
test.parse<double>("double", "+1e-3", 0.001);
test.parse_expect<double, parsing_exception>("invalid double", "1.2.3");
test.parse_expect<double, parsing_exception>("invalid double", "abc");
test.format<double>("double", 0.001, "0.001");
test.format<double>("double", -123.45, "-123.45");

test.parse<std::string>("std::string", "foobar", "foobar");
test.parse<std::string>("std::string", " foobar ", "foobar");
test.format<std::string>("std::string", "foobar", "foobar");
test.format<std::string>("std::string", " foobar ", " foobar ");

test.parse<f3d::ratio_t>("ratio_t", "0.1234", 0.1234);
test.parse<f3d::ratio_t>("ratio_t", "12.34%", 0.1234);
Expand All @@ -66,13 +87,22 @@ int TestSDKOptionsIO(int argc, char* argv[])
test.parse_expect<f3d::ratio_t, parsing_exception>("invalid ratio_t", "12.34&");
test.parse<f3d::ratio_t>("ratio_t", "-2/-3.5", 2.0 / 3.5);
test.parse_expect<f3d::ratio_t, parsing_exception>("invalid ratio_t", "1/2/3");
test.format<f3d::ratio_t>("ratio_t", .1234, "0.1234");

test.parse<std::vector<int>>("std::vector<int>", "1, 2, 3", { 1, 2, 3 });
test.parse<std::vector<int>>("std::vector<int>", "1,2,3", { 1, 2, 3 });
test.format<std::vector<int>>("std::vector<int>", { 1, 2, 3 }, "1,2,3");

test.parse<std::vector<double>>("std::vector<double>", "0.1,0.2,0.3", { 0.1, 0.2, 0.3 });
test.parse<std::vector<double>>("std::vector<double>", " 0.1, 0.2 , 0.3 ", { 0.1, 0.2, 0.3 });
test.format<std::vector<double>>("std::vector<double>", { 0.1, 0.2, 0.3 }, "0.1,0.2,0.3");

test.parse<std::vector<std::string>>(
"std::vector<std::string>", "foo,bar,baz", { "foo", "bar", "baz" });
test.parse<std::vector<std::string>>(
"std::vector<std::string>", " foo, bar , baz ", { "foo", "bar", "baz" });
test.format<std::vector<std::string>>(
"std::vector<std::string>", { "foo", "bar", "baz" }, "foo,bar,baz");

return test.result();
}
Loading