From 5d4ffbf2db794af799b8c5727fb6c65c079195ac Mon Sep 17 00:00:00 2001 From: Daniel Aarno Date: Sat, 18 May 2013 15:01:46 +0200 Subject: [PATCH] Make all types have ValueLike traits by default. This allows new types to be added without any fuzz (no need to specify the traits) if it has operator>>. It also removes the need to manually specify the ArgTrait for all built in types (such as long, bool, char, float etc). ArgTraits now works in the following way: 1) If there exists a specialization of ArgTraits for type X, use it. 2) If no specialization exists but X has the typename X::ValueCategory, use the specialization for X::ValueCategory. 3) If neither (1) nor (2) defines the trait, use the default which is ValueLike. --- examples/Makefile.am | 4 +- examples/test24.cpp | 43 ++++++++++ include/tclap/ArgTraits.h | 43 ++++++++-- include/tclap/StandardTraits.h | 152 +-------------------------------- tests/Makefile.am | 6 +- tests/runtests.sh | 2 +- tests/test83.out | 1 + tests/test83.sh | 13 +++ 8 files changed, 106 insertions(+), 158 deletions(-) create mode 100644 examples/test24.cpp create mode 100644 tests/test83.out create mode 100755 tests/test83.sh diff --git a/examples/Makefile.am b/examples/Makefile.am index f75025f..bb15fc7 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,7 +1,8 @@ noinst_PROGRAMS = test1 test2 test3 test4 test5 test6 test7 test8 test9 \ test10 test11 test12 test13 test14 test15 test16 \ - test17 test18 test19 test20 test21 test22 test23 + test17 test18 test19 test20 test21 test22 test23 \ + test24 test1_SOURCES = test1.cpp test2_SOURCES = test2.cpp @@ -26,6 +27,7 @@ test20_SOURCES = test20.cpp test21_SOURCES = test21.cpp test22_SOURCES = test22.cpp test23_SOURCES = test23.cpp +test24_SOURCES = test24.cpp AM_CPPFLAGS = -I$(top_srcdir)/include diff --git a/examples/test24.cpp b/examples/test24.cpp new file mode 100644 index 0000000..d98ef49 --- /dev/null +++ b/examples/test24.cpp @@ -0,0 +1,43 @@ +#include "tclap/CmdLine.h" +#include + +using namespace TCLAP; + +// Define a simple 3D vector type +struct Vect3D { + double v[3]; + + std::ostream& print(std::ostream &os) const + { + std::copy(v, v + 3, std::ostream_iterator(os, " ")); + return os; + } +}; + +// operator>> will be used to assign to the vector since the default +// is that all types are ValueLike. +std::istream &operator>>(std::istream &is, Vect3D &v) +{ + if (!(is >> v.v[0] >> v.v[1] >> v.v[2])) + throw TCLAP::ArgParseException(" Argument is not a 3D vector"); + + return is; +} + +int main(int argc, char *argv[]) +{ + CmdLine cmd("Command description message", ' ', "0.9"); + ValueArg vec("v", "vect", "vector", + true, Vect3D(), "3D vector", cmd); + + try { + cmd.parse(argc, argv); + } catch(std::exception &e) { + std::cout << e.what() << std::endl; + return EXIT_FAILURE; + } + + vec.getValue().print(std::cout); + std::cout << std::endl; +} + diff --git a/include/tclap/ArgTraits.h b/include/tclap/ArgTraits.h index 0b2c18f..df1fe50 100644 --- a/include/tclap/ArgTraits.h +++ b/include/tclap/ArgTraits.h @@ -73,13 +73,46 @@ struct ValueLikeTrait { * Arg traits are used to get compile type specialization when parsing * argument values. Using an ArgTraits you can specify the way that * values gets assigned to any particular type during parsing. The two - * supported types are StringLike and ValueLike. + * supported types are StringLike and ValueLike. ValueLike is the + * default and means that operator>> will be used to assign values to + * the type. */ template -struct ArgTraits { - typedef typename T::ValueCategory ValueCategory; - virtual ~ArgTraits() {} - //typedef ValueLike ValueCategory; +class ArgTraits { + // This is a bit silly, but what we want to do is: + // 1) If there exists a specialization of ArgTraits for type X, + // use it. + // + // 2) If no specialization exists but X has the typename + // X::ValueCategory, use the specialization for X::ValueCategory. + // + // 3) If neither (1) nor (2) defines the trait, use the default + // which is ValueLike. + + // This is the "how": + // + // test(0) (where 0 is the NULL ptr) will match + // test(typename C::ValueCategory*) iff type T has the + // corresponding typedef. If it does not test(...) will be + // matched. This allows us to determine if T::ValueCategory + // exists by checking the sizeof for the test function (return + // value must have different sizeof). + template static short test(typename C::ValueCategory*); + template static long test(...); + static const bool hasTrait = sizeof(test(0)) == sizeof(short); + + template + struct DefaultArgTrait { + typedef ValueLike ValueCategory; + }; + + template + struct DefaultArgTrait { + typedef typename C::ValueCategory ValueCategory; + }; + +public: + typedef typename DefaultArgTrait::ValueCategory ValueCategory; }; #endif diff --git a/include/tclap/StandardTraits.h b/include/tclap/StandardTraits.h index 46d7f6f..de28422 100644 --- a/include/tclap/StandardTraits.h +++ b/include/tclap/StandardTraits.h @@ -41,156 +41,10 @@ namespace TCLAP { -// ====================================================================== -// Integer types -// ====================================================================== +// Integer types (signed, unsigned and bool) and floating point types all +// have value-like semantics. -/** - * longs have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - -/** - * ints have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - -/** - * shorts have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - -/** - * chars have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - -#ifdef HAVE_LONG_LONG -/** - * long longs have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; -#endif - -// ====================================================================== -// Unsigned integer types -// ====================================================================== - -/** - * unsigned longs have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - -/** - * unsigned ints have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - -/** - * unsigned shorts have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - -/** - * unsigned chars have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - -// Microsoft implements size_t awkwardly. -#if defined(_MSC_VER) && defined(_M_X64) -/** - * size_ts have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; -#endif - - -#ifdef HAVE_LONG_LONG -/** - * unsigned long longs have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; -#endif - -// ====================================================================== -// Float types -// ====================================================================== - -/** - * floats have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - -/** - * doubles have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - -// ====================================================================== -// Other types -// ====================================================================== - -/** - * bools have value-like semantics. - */ -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; - - -/** - * wchar_ts have value-like semantics. - */ -#ifndef TCLAP_DONT_DECLARE_WCHAR_T_ARGTRAITS -template<> -struct ArgTraits { - typedef ValueLike ValueCategory; -}; -#endif - -/** - * Strings have string like argument traits. - */ +// Strings have string like argument traits. template<> struct ArgTraits { typedef StringLike ValueCategory; diff --git a/tests/Makefile.am b/tests/Makefile.am index d3953d0..4a87563 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -81,7 +81,8 @@ TESTS = test1.sh \ test79.sh \ test80.sh \ test81.sh \ - test82.sh + test82.sh \ + test83.sh EXTRA_DIST = $(TESTS) \ test1.out \ @@ -165,6 +166,7 @@ EXTRA_DIST = $(TESTS) \ test79.out \ test80.out \ test81.out \ - test82.out + test82.out \ + test83.out CLEANFILES = tmp.out diff --git a/tests/runtests.sh b/tests/runtests.sh index 977dd11..8e80939 100755 --- a/tests/runtests.sh +++ b/tests/runtests.sh @@ -2,7 +2,7 @@ let "suc = 0" let "fail = 0" -NUMTEST=82 +NUMTEST=83 for (( tno = 1 ; $tno <= $NUMTEST ; tno = $tno + 1 )); do ./testCheck.sh $tno diff --git a/tests/test83.out b/tests/test83.out new file mode 100644 index 0000000..46786e9 --- /dev/null +++ b/tests/test83.out @@ -0,0 +1 @@ +1 2 3 diff --git a/tests/test83.sh b/tests/test83.sh new file mode 100755 index 0000000..b8a258a --- /dev/null +++ b/tests/test83.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# this tests whether all required args are listed as +# missing when no arguments are specified +# failure +../examples/test24 -v "1 2 3" > tmp.out 2>&1 + +if cmp -s tmp.out $srcdir/test84.out; then + exit 0 +else + exit 1 +fi +