Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:52:42

0001 //  (C) Copyright Gennadiy Rozental 2001.
0002 //  Distributed under the Boost Software License, Version 1.0.
0003 //  (See accompanying file LICENSE_1_0.txt or copy at
0004 //  http://www.boost.org/LICENSE_1_0.txt)
0005 
0006 //  See http://www.boost.org/libs/test for the library home page.
0007 //
0008 //  File        : $RCSfile$
0009 //
0010 //  Version     : $Revision$
0011 //
0012 //  Description : formal parameter definition
0013 // ***************************************************************************
0014 
0015 #ifndef BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP
0016 #define BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP
0017 
0018 // Boost.Test Runtime parameters
0019 #include <boost/test/utils/runtime/fwd.hpp>
0020 #include <boost/test/utils/runtime/modifier.hpp>
0021 #include <boost/test/utils/runtime/argument.hpp>
0022 #include <boost/test/utils/runtime/argument_factory.hpp>
0023 
0024 // Boost.Test
0025 #include <boost/test/utils/class_properties.hpp>
0026 #include <boost/test/utils/foreach.hpp>
0027 #include <boost/test/utils/setcolor.hpp>
0028 
0029 // Boost
0030 #include <boost/function.hpp>
0031 #include <boost/algorithm/cxx11/all_of.hpp>
0032 
0033 // STL
0034 #include <algorithm>
0035 
0036 #include <boost/test/detail/suppress_warnings.hpp>
0037 
0038 namespace boost {
0039 namespace runtime {
0040 
0041 inline
0042 std::ostream& commandline_pretty_print(
0043     std::ostream& ostr, 
0044     std::string const& prefix, 
0045     std::string const& to_print) {
0046     
0047     const int split_at = 80;
0048 
0049     std::string::size_type current = 0;
0050 
0051     while(current < to_print.size()) {
0052 
0053         // discards spaces at the beginning
0054         std::string::size_type startpos = to_print.find_first_not_of(" \t\n", current);
0055         current += startpos - current;
0056 
0057         bool has_more_lines = (current + split_at) < to_print.size();
0058 
0059         if(has_more_lines) {
0060           std::string::size_type endpos = to_print.find_last_of(" \t\n", current + split_at);
0061           std::string sub(to_print.substr(current, endpos - current));
0062           ostr << prefix << sub;
0063           ostr << "\n";
0064           current += endpos - current;
0065         }
0066         else 
0067         {
0068           ostr << prefix << to_print.substr(current, split_at);
0069           current += split_at;
0070         }
0071     }
0072     return ostr;
0073 }
0074 
0075 // ************************************************************************** //
0076 // **************           runtime::parameter_cla_id          ************** //
0077 // ************************************************************************** //
0078 // set of attributes identifying the parameter in the command line
0079 
0080 struct parameter_cla_id {
0081     parameter_cla_id( cstring prefix, cstring tag, cstring value_separator, bool negatable )
0082     : m_prefix( prefix.begin(), prefix.size() )
0083     , m_tag( tag.begin(), tag.size() )
0084     , m_value_separator( value_separator.begin(), value_separator.size() )
0085     , m_negatable( negatable )
0086     {
0087 
0088         BOOST_TEST_I_ASSRT( algorithm::all_of( m_prefix.begin(), m_prefix.end(), valid_prefix_char ),
0089                             invalid_cla_id() << "Parameter " << m_tag
0090                                              << " has invalid characters in prefix." );
0091 
0092         BOOST_TEST_I_ASSRT( algorithm::all_of( m_tag.begin(), m_tag.end(), valid_name_char ),
0093                             invalid_cla_id() << "Parameter " << m_tag
0094                                              << " has invalid characters in name." );
0095 
0096         BOOST_TEST_I_ASSRT( algorithm::all_of( m_value_separator.begin(), m_value_separator.end(), valid_separator_char ),
0097                             invalid_cla_id() << "Parameter " << m_tag
0098                                              << " has invalid characters in value separator." );
0099     }
0100 
0101     static bool             valid_prefix_char( char c )
0102     {
0103         return c == '-' || c == '/' ;
0104     }
0105     static bool             valid_separator_char( char c )
0106     {
0107         return c == '=' || c == ':' || c == ' ' || c == '\0';
0108     }
0109     static bool             valid_name_char( char c )
0110     {
0111         return std::isalnum( c ) || c == '+' || c == '_' || c == '?';
0112     }
0113 
0114     std::string             m_prefix;
0115     std::string             m_tag;
0116     std::string             m_value_separator;
0117     bool                    m_negatable;
0118 };
0119 
0120 typedef std::vector<parameter_cla_id> param_cla_ids;
0121 
0122 // ************************************************************************** //
0123 // **************             runtime::basic_param             ************** //
0124 // ************************************************************************** //
0125 
0126 cstring const help_prefix("////");
0127 
0128 class basic_param {
0129     typedef function<void (cstring)>            callback_type;
0130     typedef unit_test::readwrite_property<bool> bool_property;
0131 
0132 protected:
0133     /// Constructor with modifiers
0134     template<typename Modifiers>
0135     basic_param( cstring name, bool is_optional, bool is_repeatable, Modifiers const& m )
0136     : p_name( name.begin(), name.end() )
0137     , p_description( nfp::opt_get( m, description, std::string() ) )
0138     , p_help( nfp::opt_get( m, runtime::help, std::string() ) )
0139     , p_env_var( nfp::opt_get( m, env_var, std::string() ) )
0140     , p_value_hint( nfp::opt_get( m, value_hint, std::string() ) )
0141     , p_optional( is_optional )
0142     , p_repeatable( is_repeatable )
0143     , p_has_optional_value( m.has( optional_value ) )
0144     , p_has_default_value( m.has( default_value ) || is_repeatable )
0145     , p_callback( nfp::opt_get( m, callback, callback_type() ) )
0146     {
0147         add_cla_id( help_prefix, name, ":" );
0148     }
0149 
0150 public:
0151     BOOST_TEST_DEFAULTED_FUNCTION(virtual ~basic_param(), {})
0152 
0153     // Pubic properties
0154     std::string const       p_name;
0155     std::string const       p_description;
0156     std::string const       p_help;
0157     std::string const       p_env_var;
0158     std::string const       p_value_hint;
0159     bool const              p_optional;
0160     bool const              p_repeatable;
0161     bool_property           p_has_optional_value;
0162     bool_property           p_has_default_value;
0163     callback_type const     p_callback;
0164 
0165     /// interface for cloning typed parameters
0166     virtual basic_param_ptr clone() const = 0;
0167 
0168     /// Access methods
0169     param_cla_ids const&    cla_ids() const { return m_cla_ids; }
0170     void                    add_cla_id( cstring prefix, cstring tag, cstring value_separator )
0171     {
0172         add_cla_id_impl( prefix, tag, value_separator, false, true );
0173     }
0174 
0175     /// interface for producing argument values for this parameter
0176     virtual void            produce_argument( cstring token, bool negative_form, arguments_store& store ) const = 0;
0177     virtual void            produce_default( arguments_store& store ) const = 0;
0178 
0179     /// interfaces for help message reporting
0180     virtual void            usage( std::ostream& ostr, cstring negation_prefix_, bool use_color = true )
0181     {
0182         namespace utils = unit_test::utils;
0183         namespace ut_detail = unit_test::ut_detail;
0184 
0185         // 
0186         ostr  << "  ";
0187         {
0188 
0189           BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::GREEN );
0190           ostr << p_name;
0191         }
0192 
0193         ostr << '\n';
0194 
0195         if( !p_description.empty() ) {
0196           commandline_pretty_print(ostr, "    ", p_description) << '\n';
0197         }
0198 
0199         BOOST_TEST_FOREACH( parameter_cla_id const&, id, cla_ids() ) {
0200             if( id.m_prefix == help_prefix )
0201                 continue;
0202 
0203             ostr << "    " << id.m_prefix;
0204 
0205             if( id.m_negatable )
0206                 cla_name_help( ostr, id.m_tag, negation_prefix_, use_color );
0207             else
0208                 cla_name_help( ostr, id.m_tag, "", use_color );
0209 
0210             BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::YELLOW );
0211             bool optional_value_ = false;
0212 
0213             if( p_has_optional_value ) {
0214                 optional_value_ = true;
0215                 ostr << '[';
0216             }
0217 
0218 
0219             if( id.m_value_separator.empty() )
0220                 ostr << ' ';
0221             else {
0222                 ostr << id.m_value_separator;
0223             }
0224 
0225             value_help( ostr );
0226 
0227             if( optional_value_ )
0228                 ostr << ']';
0229 
0230             ostr << '\n';
0231         }
0232     }
0233 
0234     virtual void            help( std::ostream& ostr, cstring negation_prefix_, bool use_color = true )
0235     {
0236         usage( ostr, negation_prefix_, use_color );
0237 
0238         if( !p_help.empty() ) {
0239             ostr << '\n';
0240             commandline_pretty_print(ostr, "  ", p_help);
0241         }
0242     }
0243 
0244 protected:
0245     void                    add_cla_id_impl( cstring prefix,
0246                                              cstring tag,
0247                                              cstring value_separator,
0248                                              bool negatable,
0249                                              bool validate_value_separator )
0250     {
0251         BOOST_TEST_I_ASSRT( !tag.is_empty(),
0252                             invalid_cla_id() << "Parameter can't have an empty name." );
0253 
0254         BOOST_TEST_I_ASSRT( !prefix.is_empty(),
0255                             invalid_cla_id() << "Parameter " << tag
0256                                              << " can't have an empty prefix." );
0257 
0258         BOOST_TEST_I_ASSRT( !value_separator.is_empty(),
0259                             invalid_cla_id() << "Parameter " << tag
0260                                              << " can't have an empty value separator." );
0261 
0262         // We trim value separator from all the spaces, so token end will indicate separator
0263         value_separator.trim();
0264         BOOST_TEST_I_ASSRT( !validate_value_separator || !value_separator.is_empty() || !p_has_optional_value,
0265                             invalid_cla_id() << "Parameter " << tag
0266                                              << " with optional value attribute can't use space as value separator." );
0267 
0268         m_cla_ids.push_back( parameter_cla_id( prefix, tag, value_separator, negatable ) );
0269     }
0270 
0271 private:
0272     /// interface for usage/help customization
0273     virtual void            cla_name_help( std::ostream& ostr, cstring cla_tag, cstring /*negation_prefix_*/, bool /*use_color*/ = true) const
0274     {
0275         ostr << cla_tag;
0276     }
0277     virtual void            value_help( std::ostream& ostr ) const
0278     {
0279         if( p_value_hint.empty() )
0280             ostr << "<value>";
0281         else
0282             ostr << p_value_hint;
0283     }
0284 
0285     // Data members
0286     param_cla_ids       m_cla_ids;
0287 };
0288 
0289 // ************************************************************************** //
0290 // **************              runtime::parameter              ************** //
0291 // ************************************************************************** //
0292 
0293 enum args_amount {
0294     OPTIONAL_PARAM,   // 0-1
0295     REQUIRED_PARAM,   // exactly 1
0296     REPEATABLE_PARAM  // 0-N
0297 };
0298 
0299 //____________________________________________________________________________//
0300 
0301 template<typename ValueType, args_amount a = runtime::OPTIONAL_PARAM, bool is_enum = false>
0302 class parameter : public basic_param {
0303 public:
0304     /// Constructor with modifiers
0305 #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
0306     template<typename Modifiers=nfp::no_params_type>
0307     parameter( cstring name, Modifiers const& m = nfp::no_params )
0308 #else
0309     template<typename Modifiers>
0310     parameter( cstring name, Modifiers const& m )
0311 #endif
0312     : basic_param( name, a != runtime::REQUIRED_PARAM, a == runtime::REPEATABLE_PARAM, m )
0313     , m_arg_factory( m )
0314     {
0315         BOOST_TEST_I_ASSRT( !m.has( default_value ) || a == runtime::OPTIONAL_PARAM,
0316                             invalid_param_spec() << "Parameter " << name
0317                                                  << " is not optional and can't have default_value." );
0318 
0319         BOOST_TEST_I_ASSRT( !m.has( optional_value ) || !this->p_repeatable,
0320                             invalid_param_spec() << "Parameter " << name
0321                                                  << " is repeatable and can't have optional_value." );
0322     }
0323 
0324 private:
0325     basic_param_ptr clone() const BOOST_OVERRIDE
0326     {
0327         return basic_param_ptr( new parameter( *this ) );
0328     }
0329     void    produce_argument( cstring token, bool , arguments_store& store ) const BOOST_OVERRIDE
0330     {
0331         m_arg_factory.produce_argument( token, this->p_name, store );
0332     }
0333     void    produce_default( arguments_store& store ) const BOOST_OVERRIDE
0334     {
0335         if( !this->p_has_default_value )
0336             return;
0337 
0338         m_arg_factory.produce_default( this->p_name, store );
0339     }
0340 
0341     // Data members
0342     typedef argument_factory<ValueType, is_enum, a == runtime::REPEATABLE_PARAM> factory_t;
0343     factory_t       m_arg_factory;
0344 };
0345 
0346 //____________________________________________________________________________//
0347 
0348 class option : public basic_param {
0349 public:
0350     /// Constructor with modifiers
0351 #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
0352     template<typename Modifiers=nfp::no_params_type>
0353     option( cstring name, Modifiers const& m = nfp::no_params )
0354 #else
0355     template<typename Modifiers>
0356     option( cstring name, Modifiers const& m )
0357 #endif
0358     : basic_param( name, true, false, nfp::opt_append( nfp::opt_append( m, optional_value = true), default_value = false) )
0359     , m_arg_factory( nfp::opt_append( nfp::opt_append( m, optional_value = true), default_value = false) )
0360     {
0361     }
0362 
0363     void            add_cla_id( cstring prefix, cstring tag, cstring value_separator, bool negatable = false )
0364     {
0365         add_cla_id_impl( prefix, tag, value_separator, negatable, false );
0366     }
0367 
0368 private:
0369     basic_param_ptr clone() const BOOST_OVERRIDE
0370     {
0371         return basic_param_ptr( new option( *this ) );
0372     }
0373 
0374     void    produce_argument( cstring token, bool negative_form, arguments_store& store ) const BOOST_OVERRIDE
0375     {
0376         if( token.empty() )
0377             store.set( p_name, !negative_form );
0378         else {
0379             BOOST_TEST_I_ASSRT( !negative_form,
0380                                 format_error( p_name ) << "Can't set value to negative form of the argument." );
0381 
0382             m_arg_factory.produce_argument( token, p_name, store );
0383         }
0384     }
0385 
0386     void    produce_default( arguments_store& store ) const BOOST_OVERRIDE
0387     {
0388         m_arg_factory.produce_default( p_name, store );
0389     }
0390     void    cla_name_help( std::ostream& ostr, cstring cla_tag, cstring negation_prefix_, bool use_color = true ) const BOOST_OVERRIDE
0391     {
0392         namespace utils = unit_test::utils;
0393         namespace ut_detail = unit_test::ut_detail;
0394 
0395         if( !negation_prefix_.is_empty() ) {
0396             BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::YELLOW );
0397             ostr << '[' << negation_prefix_ << ']';
0398         }
0399         ostr << cla_tag;
0400     }
0401     void    value_help( std::ostream& ostr ) const BOOST_OVERRIDE
0402     {
0403         if( p_value_hint.empty() )
0404             ostr << "<boolean value>";
0405         else
0406             ostr << p_value_hint;
0407     }
0408 
0409     // Data members
0410     typedef argument_factory<bool, false, false> factory_t;
0411     factory_t       m_arg_factory;
0412 };
0413 
0414 //____________________________________________________________________________//
0415 
0416 template<typename EnumType, args_amount a = runtime::OPTIONAL_PARAM>
0417 class enum_parameter : public parameter<EnumType, a, true> {
0418     typedef parameter<EnumType, a, true> base;
0419 public:
0420     /// Constructor with modifiers
0421 #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
0422     template<typename Modifiers=nfp::no_params_type>
0423     enum_parameter( cstring name, Modifiers const& m = nfp::no_params )
0424 #else
0425     template<typename Modifiers>
0426     enum_parameter( cstring name, Modifiers const& m )
0427 #endif
0428     : base( name, m )
0429     {
0430 #ifdef BOOST_TEST_CLA_NEW_API
0431         auto const& values = m[enum_values<EnumType>::value];
0432         auto it = values.begin();
0433 #else
0434         std::vector<std::pair<cstring, EnumType> > const& values = m[enum_values<EnumType>::value];
0435         typename std::vector<std::pair<cstring, EnumType> >::const_iterator it = values.begin();
0436 #endif
0437         while( it != values.end() ) {
0438             m_valid_names.push_back( it->first );
0439             ++it;
0440         }
0441     }
0442 
0443 private:
0444     basic_param_ptr clone() const BOOST_OVERRIDE
0445     {
0446         return basic_param_ptr( new enum_parameter( *this ) );
0447     }
0448 
0449     void    value_help( std::ostream& ostr ) const BOOST_OVERRIDE
0450     {
0451         if( this->p_value_hint.empty() ) {
0452             ostr << "<";
0453             bool first = true;
0454             BOOST_TEST_FOREACH( cstring, name, m_valid_names ) {
0455                 if( first )
0456                     first = false;
0457                 else
0458                     ostr << '|';
0459                 ostr << name;
0460             }
0461             ostr << ">";
0462         }
0463         else
0464             ostr << this->p_value_hint;
0465     }
0466 
0467     // Data members
0468     std::vector<cstring>    m_valid_names;
0469 };
0470 
0471 
0472 // ************************************************************************** //
0473 // **************           runtime::parameters_store          ************** //
0474 // ************************************************************************** //
0475 
0476 class parameters_store {
0477     struct lg_compare {
0478         bool operator()( cstring lh, cstring rh ) const
0479         {
0480             return std::lexicographical_compare(lh.begin(), lh.end(),
0481                                                 rh.begin(), rh.end());
0482         }
0483     };
0484 public:
0485 
0486     typedef std::map<cstring, basic_param_ptr, lg_compare> storage_type;
0487 
0488     /// Adds parameter into the persistent store
0489     void                    add( basic_param const& in )
0490     {
0491         basic_param_ptr p = in.clone();
0492 
0493         BOOST_TEST_I_ASSRT( m_parameters.insert( std::make_pair( cstring(p->p_name), p ) ).second,
0494                             duplicate_param() << "Parameter " << p->p_name << " is duplicate." );
0495     }
0496 
0497     /// Returns true if there is no parameters registered
0498     bool                    is_empty() const    { return m_parameters.empty(); }
0499     /// Returns map of all the registered parameter
0500     storage_type const&     all() const         { return m_parameters; }
0501     /// Returns true if parameter with specified name is registered
0502     bool                    has( cstring name ) const
0503     {
0504         return m_parameters.find( name ) != m_parameters.end();
0505     }
0506     /// Returns map of all the registered parameter
0507     basic_param_ptr         get( cstring name ) const
0508     {
0509         storage_type::const_iterator const& found = m_parameters.find( name );
0510         BOOST_TEST_I_ASSRT( found != m_parameters.end(),
0511                             unknown_param() << "Parameter " << name << " is unknown." );
0512 
0513         return found->second;
0514     }
0515 
0516 private:
0517     // Data members
0518     storage_type            m_parameters;
0519 };
0520 
0521 } // namespace runtime
0522 } // namespace boost
0523 
0524 #include <boost/test/detail/enable_warnings.hpp>
0525 
0526 #endif // BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP