From c6baec1432d70df66bab5326f8a06cb9f31ecb80 Mon Sep 17 00:00:00 2001 From: Yao Yue Date: Sun, 13 Mar 2016 03:10:08 -0700 Subject: [PATCH] support floating-point number in option value --- include/cc_option.h | 8 +++++++ src/cc_option.c | 32 +++++++++++++++++++++++++ test/option/CMakeLists.txt | 1 + test/option/check_option.c | 49 +++++++++++++++++++++++++++++++------- 4 files changed, 82 insertions(+), 8 deletions(-) diff --git a/include/cc_option.h b/include/cc_option.h index 8b1e56a5a..d54b835bb 100644 --- a/include/cc_option.h +++ b/include/cc_option.h @@ -46,6 +46,7 @@ extern "C" { #define OPTION_TYPE_BOOL_VAR vbool #define OPTION_TYPE_UINT_VAR vuint +#define OPTION_TYPE_FPN_VAR vfpn #define OPTION_TYPE_STR_VAR vstr #define OPTION_DECLARE(_name, _type, _default, _description) \ @@ -62,6 +63,7 @@ extern "C" { typedef enum option_type { OPTION_TYPE_BOOL, OPTION_TYPE_UINT, + OPTION_TYPE_FPN, OPTION_TYPE_STR, OPTION_TYPE_SENTINEL } option_type_e; @@ -72,6 +74,7 @@ extern char *option_type_str[]; typedef union option_val { bool vbool; uintmax_t vuint; + double vfpn; char *vstr; } option_val_u; @@ -95,6 +98,11 @@ option_uint(struct option *opt) { return opt->val.vuint; } +static inline double +option_fpn(struct option *opt) { + return opt->val.vfpn; +} + static inline char * option_str(struct option *opt) { return opt->val.vstr; diff --git a/src/cc_option.c b/src/cc_option.c index 0c300db7c..bd8ebd906 100644 --- a/src/cc_option.c +++ b/src/cc_option.c @@ -322,6 +322,31 @@ _option_parse_uint(struct option *opt, const char *val_str) return CC_OK; } +static rstatus_i +_option_parse_fpn(struct option *opt, const char *val_str) +{ + /* TODO: handle expressions similar to what's allowed with integers */ + double val = 0; + char *loc; + + val = strtod(val_str, &loc); + if (errno == ERANGE) { + log_stderr("option value %s out of range for double type", val_str); + return CC_ERROR; + } + + if (*loc != '\0') { + log_stderr("option value %s could not be fully parsed, check char at " + "offset %ld", val_str, loc - val_str); + return CC_ERROR; + } + + opt->set = true; + opt->val.vfpn = val; + + return CC_OK; +} + static rstatus_i _option_parse_str(struct option *opt, const char *val_str) { @@ -358,6 +383,10 @@ option_default(struct option *opt) opt->val.vuint = opt->default_val.vuint; break; + case OPTION_TYPE_FPN: + opt->val.vfpn = opt->default_val.vfpn; + break; + case OPTION_TYPE_STR: if (opt->default_val.vstr == NULL) { opt->val.vstr = NULL; @@ -390,6 +419,9 @@ option_set(struct option *opt, char *val_str) case OPTION_TYPE_UINT: return _option_parse_uint(opt, val_str); + case OPTION_TYPE_FPN: + return _option_parse_fpn(opt, val_str); + case OPTION_TYPE_STR: return _option_parse_str(opt, val_str); diff --git a/test/option/CMakeLists.txt b/test/option/CMakeLists.txt index 8c4faeeaf..951980f85 100644 --- a/test/option/CMakeLists.txt +++ b/test/option/CMakeLists.txt @@ -5,6 +5,7 @@ set(source check_${suite}.c) add_executable(${test_name} ${source}) target_link_libraries(${test_name} ccommon-static ${CHECK_LIBRARIES}) +target_link_libraries(${test_name} m) add_dependencies(check ${test_name}) add_test(${test_name} ${test_name}) diff --git a/test/option/check_option.c b/test/option/check_option.c index d9a76e5a6..8bf68b25a 100644 --- a/test/option/check_option.c +++ b/test/option/check_option.c @@ -2,6 +2,7 @@ #include +#include #include #include @@ -73,31 +74,57 @@ START_TEST(test_parse_uinteger) ck_assert_int_eq(opt.set, false); opt.set = false; - opt.val.vuint = false; + opt.val.vuint = 0; ck_assert_int_eq(option_set(&opt, "1"), CC_OK); ck_assert_int_eq(opt.val.vuint, 1); ck_assert_int_eq(opt.set, true); opt.set = false; - opt.val.vuint = false; + opt.val.vuint = 0; ck_assert_int_eq(option_set(&opt, "1 + 1"), CC_OK); ck_assert_int_eq(opt.val.vuint, 2); ck_assert_int_eq(opt.set, true); opt.set = false; - opt.val.vuint = false; + opt.val.vuint = 0; ck_assert_int_eq(option_set(&opt, "1 + 2 * 3"), CC_OK); ck_assert_int_eq(opt.val.vuint, 7); ck_assert_int_eq(opt.set, true); opt.set = false; - opt.val.vuint = false; + opt.val.vuint = 0; ck_assert_int_eq(option_set(&opt, "(1 + 2) * 3"), CC_OK); ck_assert_int_eq(opt.val.vuint, 9); ck_assert_int_eq(opt.set, true); } END_TEST +START_TEST(test_parse_float) +{ + struct option opt; + opt.type = OPTION_TYPE_FPN; + opt.set = false; + + ck_assert_int_ne(option_set(&opt, "invalid"), CC_OK); + ck_assert_int_eq(opt.set, false); + + ck_assert_int_ne(option_set(&opt, "1.25ab"), CC_OK); + ck_assert_int_eq(opt.set, false); + + opt.set = false; + opt.val.vfpn = 0; + ck_assert_int_eq(option_set(&opt, "1.25"), CC_OK); + ck_assert_msg(fabs(opt.val.vfpn - 1.25) < 1e-5, "value = %f", opt.val.vfpn); + ck_assert_int_eq(opt.set, true); + + opt.set = false; + opt.val.vfpn = 0; + ck_assert_int_eq(option_set(&opt, "-1"), CC_OK); + ck_assert(fabs(opt.val.vfpn + 1) < 1e-5); + ck_assert_int_eq(opt.set, true); +} +END_TEST + START_TEST(test_parse_string) { struct option opt; @@ -154,9 +181,10 @@ tmpname_destroy(char *path) START_TEST(test_load_file) { #define TEST_OPTION(ACTION) \ - ACTION( boolean, OPTION_TYPE_BOOL, true, "it may be true of false" )\ - ACTION( string, OPTION_TYPE_STR, "foo", "it is a sequence of bytes" )\ - ACTION( uinteger, OPTION_TYPE_UINT, 2, "it is a non-negative integer number" ) + ACTION( boolean, OPTION_TYPE_BOOL, true, "it may be true of false" )\ + ACTION( uinteger, OPTION_TYPE_UINT, 2, "it is a non-negative integer number" )\ + ACTION( fpn, OPTION_TYPE_FPN, 1.25, "it is a floating point number" )\ + ACTION( string, OPTION_TYPE_STR, "foo", "it is a sequence of bytes" ) #define SETTING(ACTION) \ TEST_OPTION(ACTION) @@ -175,8 +203,9 @@ START_TEST(test_load_file) tmpname = tmpname_create( "boolean: no\n" - "string: bar\n" "uinteger: 3\n" + "fpn: 2.5\n" + "string: bar\n" ); ck_assert_ptr_ne(tmpname, NULL); @@ -187,12 +216,15 @@ START_TEST(test_load_file) CC_OK); ck_assert_int_eq(setting.boolean.val.vbool, true); ck_assert_int_eq(setting.uinteger.val.vuint, 2); + ck_assert_msg(fabs(setting.fpn.val.vfpn - 1.25) < 1e-5, "value = %f", + setting.fpn.val.vfpn); ck_assert_str_eq(setting.string.val.vstr, "foo"); ck_assert_int_eq(option_load_file(fp, (struct option *)&setting, nopt), CC_OK); ck_assert_int_eq(setting.boolean.val.vbool, false); ck_assert_int_eq(setting.uinteger.val.vuint, 3); + ck_assert(fabs(setting.fpn.val.vfpn - 2.5) < 1e-5); ck_assert_str_eq(setting.string.val.vstr, "bar"); option_free((struct option *)&setting, nopt); @@ -215,6 +247,7 @@ option_suite(void) TCase *tc_option = tcase_create("option test"); tcase_add_test(tc_option, test_parse_bool); tcase_add_test(tc_option, test_parse_uinteger); + tcase_add_test(tc_option, test_parse_float); tcase_add_test(tc_option, test_parse_string); tcase_add_test(tc_option, test_load_file); suite_add_tcase(s, tc_option);