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