parameter.hpp 17 KB

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