parameter.hpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. // (C) Copyright Gennadiy Rozental 2001.
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. // See http://www.boost.org/libs/test for the library home page.
  6. //
  7. // File : $RCSfile$
  8. //
  9. // Version : $Revision$
  10. //
  11. // Description : formal parameter definition
  12. // ***************************************************************************
  13. #ifndef BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP
  14. #define BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP
  15. // Boost.Test Runtime parameters
  16. #include <boost/test/utils/runtime/fwd.hpp>
  17. #include <boost/test/utils/runtime/modifier.hpp>
  18. #include <boost/test/utils/runtime/argument.hpp>
  19. #include <boost/test/utils/runtime/argument_factory.hpp>
  20. // Boost.Test
  21. #include <boost/test/utils/class_properties.hpp>
  22. #include <boost/test/utils/foreach.hpp>
  23. #include <boost/test/utils/setcolor.hpp>
  24. // Boost
  25. #include <boost/function.hpp>
  26. #include <boost/algorithm/cxx11/all_of.hpp>
  27. // STL
  28. #include <algorithm>
  29. #include <boost/test/detail/suppress_warnings.hpp>
  30. namespace boost {
  31. namespace runtime {
  32. inline
  33. std::ostream& commandline_pretty_print(
  34. std::ostream& ostr,
  35. std::string const& prefix,
  36. std::string const& to_print) {
  37. const int split_at = 80;
  38. std::string::size_type current = 0;
  39. while(current < to_print.size()) {
  40. // discards spaces at the beginning
  41. std::string::size_type startpos = to_print.find_first_not_of(" \t\n", current);
  42. current += startpos - current;
  43. bool has_more_lines = (current + split_at) < to_print.size();
  44. if(has_more_lines) {
  45. std::string::size_type endpos = to_print.find_last_of(" \t\n", current + split_at);
  46. std::string sub(to_print.substr(current, endpos - current));
  47. ostr << prefix << sub;
  48. ostr << "\n";
  49. current += endpos - current;
  50. }
  51. else
  52. {
  53. ostr << prefix << to_print.substr(current, split_at);
  54. current += split_at;
  55. }
  56. }
  57. return ostr;
  58. }
  59. // ************************************************************************** //
  60. // ************** runtime::parameter_cla_id ************** //
  61. // ************************************************************************** //
  62. // set of attributes identifying the parameter in the command line
  63. struct parameter_cla_id {
  64. parameter_cla_id( cstring prefix, cstring tag, cstring value_separator, bool negatable )
  65. : m_prefix( prefix.begin(), prefix.size() )
  66. , m_tag( tag.begin(), tag.size() )
  67. , m_value_separator( value_separator.begin(), value_separator.size() )
  68. , m_negatable( negatable )
  69. {
  70. BOOST_TEST_I_ASSRT( algorithm::all_of( m_prefix.begin(), m_prefix.end(), valid_prefix_char ),
  71. invalid_cla_id() << "Parameter " << m_tag
  72. << " has invalid characters in prefix." );
  73. BOOST_TEST_I_ASSRT( algorithm::all_of( m_tag.begin(), m_tag.end(), valid_name_char ),
  74. invalid_cla_id() << "Parameter " << m_tag
  75. << " has invalid characters in name." );
  76. BOOST_TEST_I_ASSRT( algorithm::all_of( m_value_separator.begin(), m_value_separator.end(), valid_separator_char ),
  77. invalid_cla_id() << "Parameter " << m_tag
  78. << " has invalid characters in value separator." );
  79. }
  80. static bool valid_prefix_char( char c )
  81. {
  82. return c == '-' || c == '/' ;
  83. }
  84. static bool valid_separator_char( char c )
  85. {
  86. return c == '=' || c == ':' || c == ' ' || c == '\0';
  87. }
  88. static bool valid_name_char( char c )
  89. {
  90. return std::isalnum( c ) || c == '+' || c == '_' || c == '?';
  91. }
  92. std::string m_prefix;
  93. std::string m_tag;
  94. std::string m_value_separator;
  95. bool m_negatable;
  96. };
  97. typedef std::vector<parameter_cla_id> param_cla_ids;
  98. // ************************************************************************** //
  99. // ************** runtime::basic_param ************** //
  100. // ************************************************************************** //
  101. cstring const help_prefix("////");
  102. class basic_param {
  103. typedef function<void (cstring)> callback_type;
  104. typedef unit_test::readwrite_property<bool> bool_property;
  105. protected:
  106. /// Constructor with modifiers
  107. template<typename Modifiers>
  108. basic_param( cstring name, bool is_optional, bool is_repeatable, Modifiers const& m )
  109. : p_name( name.begin(), name.end() )
  110. , p_description( nfp::opt_get( m, description, std::string() ) )
  111. , p_help( nfp::opt_get( m, runtime::help, std::string() ) )
  112. , p_env_var( nfp::opt_get( m, env_var, std::string() ) )
  113. , p_value_hint( nfp::opt_get( m, value_hint, std::string() ) )
  114. , p_optional( is_optional )
  115. , p_repeatable( is_repeatable )
  116. , p_has_optional_value( m.has( optional_value ) )
  117. , p_has_default_value( m.has( default_value ) || is_repeatable )
  118. , p_callback( nfp::opt_get( m, callback, callback_type() ) )
  119. {
  120. add_cla_id( help_prefix, name, ":" );
  121. }
  122. public:
  123. virtual ~basic_param() {}
  124. // Pubic properties
  125. std::string const p_name;
  126. std::string const p_description;
  127. std::string const p_help;
  128. std::string const p_env_var;
  129. std::string const p_value_hint;
  130. bool const p_optional;
  131. bool const p_repeatable;
  132. bool_property p_has_optional_value;
  133. bool_property p_has_default_value;
  134. callback_type const p_callback;
  135. /// interface for cloning typed parameters
  136. virtual basic_param_ptr clone() const = 0;
  137. /// Access methods
  138. param_cla_ids const& cla_ids() const { return m_cla_ids; }
  139. void add_cla_id( cstring prefix, cstring tag, cstring value_separator )
  140. {
  141. add_cla_id_impl( prefix, tag, value_separator, false, true );
  142. }
  143. /// interface for producing argument values for this parameter
  144. virtual void produce_argument( cstring token, bool negative_form, arguments_store& store ) const = 0;
  145. virtual void produce_default( arguments_store& store ) const = 0;
  146. /// interfaces for help message reporting
  147. virtual void usage( std::ostream& ostr, cstring negation_prefix_, bool use_color = true )
  148. {
  149. namespace utils = unit_test::utils;
  150. namespace ut_detail = unit_test::ut_detail;
  151. //
  152. ostr << " ";
  153. {
  154. BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::GREEN );
  155. ostr << p_name;
  156. }
  157. ostr << '\n';
  158. if( !p_description.empty() ) {
  159. commandline_pretty_print(ostr, " ", p_description) << '\n';
  160. }
  161. BOOST_TEST_FOREACH( parameter_cla_id const&, id, cla_ids() ) {
  162. if( id.m_prefix == help_prefix )
  163. continue;
  164. ostr << " " << id.m_prefix;
  165. if( id.m_negatable )
  166. cla_name_help( ostr, id.m_tag, negation_prefix_, use_color );
  167. else
  168. cla_name_help( ostr, id.m_tag, "", use_color );
  169. BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::YELLOW );
  170. bool optional_value_ = false;
  171. if( p_has_optional_value ) {
  172. optional_value_ = true;
  173. ostr << '[';
  174. }
  175. if( id.m_value_separator.empty() )
  176. ostr << ' ';
  177. else {
  178. ostr << id.m_value_separator;
  179. }
  180. value_help( ostr );
  181. if( optional_value_ )
  182. ostr << ']';
  183. ostr << '\n';
  184. }
  185. }
  186. virtual void help( std::ostream& ostr, cstring negation_prefix_, bool use_color = true )
  187. {
  188. usage( ostr, negation_prefix_, use_color );
  189. if( !p_help.empty() ) {
  190. ostr << '\n';
  191. commandline_pretty_print(ostr, " ", p_help);
  192. }
  193. }
  194. protected:
  195. void add_cla_id_impl( cstring prefix,
  196. cstring tag,
  197. cstring value_separator,
  198. bool negatable,
  199. bool validate_value_separator )
  200. {
  201. BOOST_TEST_I_ASSRT( !tag.is_empty(),
  202. invalid_cla_id() << "Parameter can't have an empty name." );
  203. BOOST_TEST_I_ASSRT( !prefix.is_empty(),
  204. invalid_cla_id() << "Parameter " << tag
  205. << " can't have an empty prefix." );
  206. BOOST_TEST_I_ASSRT( !value_separator.is_empty(),
  207. invalid_cla_id() << "Parameter " << tag
  208. << " can't have an empty value separator." );
  209. // We trim value separator from all the spaces, so token end will indicate separator
  210. value_separator.trim();
  211. BOOST_TEST_I_ASSRT( !validate_value_separator || !value_separator.is_empty() || !p_has_optional_value,
  212. invalid_cla_id() << "Parameter " << tag
  213. << " with optional value attribute can't use space as value separator." );
  214. m_cla_ids.push_back( parameter_cla_id( prefix, tag, value_separator, negatable ) );
  215. }
  216. private:
  217. /// interface for usage/help customization
  218. virtual void cla_name_help( std::ostream& ostr, cstring cla_tag, cstring /*negation_prefix_*/, bool /*use_color*/ = true) const
  219. {
  220. ostr << cla_tag;
  221. }
  222. virtual void value_help( std::ostream& ostr ) const
  223. {
  224. if( p_value_hint.empty() )
  225. ostr << "<value>";
  226. else
  227. ostr << p_value_hint;
  228. }
  229. // Data members
  230. param_cla_ids m_cla_ids;
  231. };
  232. // ************************************************************************** //
  233. // ************** runtime::parameter ************** //
  234. // ************************************************************************** //
  235. enum args_amount {
  236. OPTIONAL_PARAM, // 0-1
  237. REQUIRED_PARAM, // exactly 1
  238. REPEATABLE_PARAM // 0-N
  239. };
  240. //____________________________________________________________________________//
  241. template<typename ValueType, args_amount a = runtime::OPTIONAL_PARAM, bool is_enum = false>
  242. class parameter : public basic_param {
  243. public:
  244. /// Constructor with modifiers
  245. #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
  246. template<typename Modifiers=nfp::no_params_type>
  247. parameter( cstring name, Modifiers const& m = nfp::no_params )
  248. #else
  249. template<typename Modifiers>
  250. parameter( cstring name, Modifiers const& m )
  251. #endif
  252. : basic_param( name, a != runtime::REQUIRED_PARAM, a == runtime::REPEATABLE_PARAM, m )
  253. , m_arg_factory( m )
  254. {
  255. BOOST_TEST_I_ASSRT( !m.has( default_value ) || a == runtime::OPTIONAL_PARAM,
  256. invalid_param_spec() << "Parameter " << name
  257. << " is not optional and can't have default_value." );
  258. BOOST_TEST_I_ASSRT( !m.has( optional_value ) || !this->p_repeatable,
  259. invalid_param_spec() << "Parameter " << name
  260. << " is repeatable and can't have optional_value." );
  261. }
  262. private:
  263. basic_param_ptr clone() const BOOST_OVERRIDE
  264. {
  265. return basic_param_ptr( new parameter( *this ) );
  266. }
  267. void produce_argument( cstring token, bool , arguments_store& store ) const BOOST_OVERRIDE
  268. {
  269. m_arg_factory.produce_argument( token, this->p_name, store );
  270. }
  271. void produce_default( arguments_store& store ) const BOOST_OVERRIDE
  272. {
  273. if( !this->p_has_default_value )
  274. return;
  275. m_arg_factory.produce_default( this->p_name, store );
  276. }
  277. // Data members
  278. typedef argument_factory<ValueType, is_enum, a == runtime::REPEATABLE_PARAM> factory_t;
  279. factory_t m_arg_factory;
  280. };
  281. //____________________________________________________________________________//
  282. class option : public basic_param {
  283. public:
  284. /// Constructor with modifiers
  285. #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
  286. template<typename Modifiers=nfp::no_params_type>
  287. option( cstring name, Modifiers const& m = nfp::no_params )
  288. #else
  289. template<typename Modifiers>
  290. option( cstring name, Modifiers const& m )
  291. #endif
  292. : basic_param( name, true, false, nfp::opt_append( nfp::opt_append( m, optional_value = true), default_value = false) )
  293. , m_arg_factory( nfp::opt_append( nfp::opt_append( m, optional_value = true), default_value = false) )
  294. {
  295. }
  296. void add_cla_id( cstring prefix, cstring tag, cstring value_separator, bool negatable = false )
  297. {
  298. add_cla_id_impl( prefix, tag, value_separator, negatable, false );
  299. }
  300. private:
  301. basic_param_ptr clone() const BOOST_OVERRIDE
  302. {
  303. return basic_param_ptr( new option( *this ) );
  304. }
  305. void produce_argument( cstring token, bool negative_form, arguments_store& store ) const BOOST_OVERRIDE
  306. {
  307. if( token.empty() )
  308. store.set( p_name, !negative_form );
  309. else {
  310. BOOST_TEST_I_ASSRT( !negative_form,
  311. format_error( p_name ) << "Can't set value to negative form of the argument." );
  312. m_arg_factory.produce_argument( token, p_name, store );
  313. }
  314. }
  315. void produce_default( arguments_store& store ) const BOOST_OVERRIDE
  316. {
  317. m_arg_factory.produce_default( p_name, store );
  318. }
  319. void cla_name_help( std::ostream& ostr, cstring cla_tag, cstring negation_prefix_, bool use_color = true ) const BOOST_OVERRIDE
  320. {
  321. namespace utils = unit_test::utils;
  322. namespace ut_detail = unit_test::ut_detail;
  323. if( !negation_prefix_.is_empty() ) {
  324. BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::YELLOW );
  325. ostr << '[' << negation_prefix_ << ']';
  326. }
  327. ostr << cla_tag;
  328. }
  329. void value_help( std::ostream& ostr ) const BOOST_OVERRIDE
  330. {
  331. if( p_value_hint.empty() )
  332. ostr << "<boolean value>";
  333. else
  334. ostr << p_value_hint;
  335. }
  336. // Data members
  337. typedef argument_factory<bool, false, false> factory_t;
  338. factory_t m_arg_factory;
  339. };
  340. //____________________________________________________________________________//
  341. template<typename EnumType, args_amount a = runtime::OPTIONAL_PARAM>
  342. class enum_parameter : public parameter<EnumType, a, true> {
  343. typedef parameter<EnumType, a, true> base;
  344. public:
  345. /// Constructor with modifiers
  346. #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
  347. template<typename Modifiers=nfp::no_params_type>
  348. enum_parameter( cstring name, Modifiers const& m = nfp::no_params )
  349. #else
  350. template<typename Modifiers>
  351. enum_parameter( cstring name, Modifiers const& m )
  352. #endif
  353. : base( name, m )
  354. {
  355. #ifdef BOOST_TEST_CLA_NEW_API
  356. auto const& values = m[enum_values<EnumType>::value];
  357. auto it = values.begin();
  358. #else
  359. std::vector<std::pair<cstring, EnumType> > const& values = m[enum_values<EnumType>::value];
  360. typename std::vector<std::pair<cstring, EnumType> >::const_iterator it = values.begin();
  361. #endif
  362. while( it != values.end() ) {
  363. m_valid_names.push_back( it->first );
  364. ++it;
  365. }
  366. }
  367. private:
  368. basic_param_ptr clone() const BOOST_OVERRIDE
  369. {
  370. return basic_param_ptr( new enum_parameter( *this ) );
  371. }
  372. void value_help( std::ostream& ostr ) const BOOST_OVERRIDE
  373. {
  374. if( this->p_value_hint.empty() ) {
  375. ostr << "<";
  376. bool first = true;
  377. BOOST_TEST_FOREACH( cstring, name, m_valid_names ) {
  378. if( first )
  379. first = false;
  380. else
  381. ostr << '|';
  382. ostr << name;
  383. }
  384. ostr << ">";
  385. }
  386. else
  387. ostr << this->p_value_hint;
  388. }
  389. // Data members
  390. std::vector<cstring> m_valid_names;
  391. };
  392. // ************************************************************************** //
  393. // ************** runtime::parameters_store ************** //
  394. // ************************************************************************** //
  395. class parameters_store {
  396. struct lg_compare {
  397. bool operator()( cstring lh, cstring rh ) const
  398. {
  399. return std::lexicographical_compare(lh.begin(), lh.end(),
  400. rh.begin(), rh.end());
  401. }
  402. };
  403. public:
  404. typedef std::map<cstring, basic_param_ptr, lg_compare> storage_type;
  405. /// Adds parameter into the persistent store
  406. void add( basic_param const& in )
  407. {
  408. basic_param_ptr p = in.clone();
  409. BOOST_TEST_I_ASSRT( m_parameters.insert( std::make_pair( cstring(p->p_name), p ) ).second,
  410. duplicate_param() << "Parameter " << p->p_name << " is duplicate." );
  411. }
  412. /// Returns true if there is no parameters registered
  413. bool is_empty() const { return m_parameters.empty(); }
  414. /// Returns map of all the registered parameter
  415. storage_type const& all() const { return m_parameters; }
  416. /// Returns true if parameter with specified name is registered
  417. bool has( cstring name ) const
  418. {
  419. return m_parameters.find( name ) != m_parameters.end();
  420. }
  421. /// Returns map of all the registered parameter
  422. basic_param_ptr get( cstring name ) const
  423. {
  424. storage_type::const_iterator const& found = m_parameters.find( name );
  425. BOOST_TEST_I_ASSRT( found != m_parameters.end(),
  426. unknown_param() << "Parameter " << name << " is unknown." );
  427. return found->second;
  428. }
  429. private:
  430. // Data members
  431. storage_type m_parameters;
  432. };
  433. } // namespace runtime
  434. } // namespace boost
  435. #include <boost/test/detail/enable_warnings.hpp>
  436. #endif // BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP