523 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			523 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | //  (C) Copyright Gennadiy Rozental 2001. | ||
|  | //  Use, modification, and distribution are subject to the | ||
|  | //  Boost Software License, Version 1.0. (See accompanying file | ||
|  | //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
|  | 
 | ||
|  | //  See http://www.boost.org/libs/test for the library home page. | ||
|  | // | ||
|  | //!@file | ||
|  | //!@brief CLA parser | ||
|  | // *************************************************************************** | ||
|  | 
 | ||
|  | #ifndef BOOST_TEST_UTILS_RUNTIME_CLA_PARSER_HPP | ||
|  | #define BOOST_TEST_UTILS_RUNTIME_CLA_PARSER_HPP | ||
|  | 
 | ||
|  | // Boost.Test Runtime parameters | ||
|  | #include <boost/test/utils/runtime/argument.hpp> | ||
|  | #include <boost/test/utils/runtime/modifier.hpp> | ||
|  | #include <boost/test/utils/runtime/parameter.hpp> | ||
|  | 
 | ||
|  | #include <boost/test/utils/runtime/cla/argv_traverser.hpp> | ||
|  | 
 | ||
|  | // Boost.Test | ||
|  | #include <boost/test/utils/foreach.hpp> | ||
|  | #include <boost/test/utils/algorithm.hpp> | ||
|  | #include <boost/test/detail/throw_exception.hpp> | ||
|  | #include <boost/test/detail/global_typedef.hpp> | ||
|  | 
 | ||
|  | #include <boost/algorithm/cxx11/all_of.hpp> // !! ?? unnecessary after cxx11 | ||
|  | 
 | ||
|  | // STL | ||
|  | // !! ?? #include <unordered_set> | ||
|  | #include <set> | ||
|  | #include <iostream> | ||
|  | 
 | ||
|  | #include <boost/test/detail/suppress_warnings.hpp> | ||
|  | 
 | ||
|  | namespace boost { | ||
|  | namespace runtime { | ||
|  | namespace cla { | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************         runtime::cla::parameter_trie         ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | namespace rt_cla_detail { | ||
|  | 
 | ||
|  | struct parameter_trie; | ||
|  | typedef shared_ptr<parameter_trie> parameter_trie_ptr; | ||
|  | typedef std::map<char,parameter_trie_ptr> trie_per_char; | ||
|  | typedef std::vector<boost::reference_wrapper<parameter_cla_id const> > param_cla_id_list; | ||
|  | 
 | ||
|  | struct parameter_trie { | ||
|  |     parameter_trie() : m_has_final_candidate( false ) {} | ||
|  | 
 | ||
|  |     /// If subtrie corresponding to the char c exists returns it otherwise creates new | ||
|  |     parameter_trie_ptr  make_subtrie( char c ) | ||
|  |     { | ||
|  |         trie_per_char::const_iterator it = m_subtrie.find( c ); | ||
|  | 
 | ||
|  |         if( it == m_subtrie.end() ) | ||
|  |             it = m_subtrie.insert( std::make_pair( c, parameter_trie_ptr( new parameter_trie ) ) ).first; | ||
|  | 
 | ||
|  |         return it->second; | ||
|  |     } | ||
|  | 
 | ||
|  |     /// Creates series of sub-tries per characters in a string | ||
|  |     parameter_trie_ptr  make_subtrie( cstring s ) | ||
|  |     { | ||
|  |         parameter_trie_ptr res; | ||
|  | 
 | ||
|  |         BOOST_TEST_FOREACH( char, c, s ) | ||
|  |             res = (res ? res->make_subtrie( c ) : make_subtrie( c )); | ||
|  | 
 | ||
|  |         return res; | ||
|  |     } | ||
|  | 
 | ||
|  |     /// Registers candidate parameter for this subtrie. If final, it needs to be unique | ||
|  |     void                add_candidate_id( parameter_cla_id const& param_id, basic_param_ptr param_candidate, bool final ) | ||
|  |     { | ||
|  |         BOOST_TEST_I_ASSRT( !m_has_final_candidate && (!final || m_id_candidates.empty()), | ||
|  |           conflicting_param() << "Parameter cla id " << param_id.m_tag << " conflicts with the " | ||
|  |                               << "parameter cla id " << m_id_candidates.back().get().m_tag ); | ||
|  | 
 | ||
|  |         m_has_final_candidate = final; | ||
|  |         m_id_candidates.push_back( ref(param_id) ); | ||
|  | 
 | ||
|  |         if( m_id_candidates.size() == 1 ) | ||
|  |             m_param_candidate = param_candidate; | ||
|  |         else | ||
|  |             m_param_candidate.reset(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /// Gets subtrie for specified char if present or nullptr otherwise | ||
|  |     parameter_trie_ptr  get_subtrie( char c ) const | ||
|  |     { | ||
|  |         trie_per_char::const_iterator it = m_subtrie.find( c ); | ||
|  | 
 | ||
|  |         return it != m_subtrie.end() ? it->second : parameter_trie_ptr(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Data members | ||
|  |     trie_per_char       m_subtrie; | ||
|  |     param_cla_id_list   m_id_candidates; | ||
|  |     basic_param_ptr     m_param_candidate; | ||
|  |     bool                m_has_final_candidate; | ||
|  | }; | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************      runtime::cla::report_foreing_token      ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | static void  | ||
|  | report_foreing_token( cstring program_name, cstring token ) | ||
|  | { | ||
|  |     std::cerr << "Boost.Test WARNING: token \"" << token << "\" does not correspond to the Boost.Test argument \n" | ||
|  |               << "                    and should be placed after all Boost.Test arguments and the -- separator.\n" | ||
|  |               << "                    For example: " << program_name << " --random -- " << token << "\n";  | ||
|  | } | ||
|  | 
 | ||
|  | } // namespace rt_cla_detail | ||
|  | 
 | ||
|  | // ************************************************************************** // | ||
|  | // **************             runtime::cla::parser             ************** // | ||
|  | // ************************************************************************** // | ||
|  | 
 | ||
|  | class parser { | ||
|  | public: | ||
|  |     /// Initializes a parser and builds internal trie representation used for | ||
|  |     /// parsing based on the supplied parameters | ||
|  | #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS | ||
|  |     template<typename Modifiers=nfp::no_params_type> | ||
|  |     parser( parameters_store const& parameters, Modifiers const& m = nfp::no_params ) | ||
|  | #else | ||
|  |     template<typename Modifiers> | ||
|  |     parser( parameters_store const& parameters, Modifiers const& m ) | ||
|  | #endif | ||
|  |     { | ||
|  |         nfp::opt_assign( m_end_of_param_indicator, m, end_of_params ); | ||
|  |         nfp::opt_assign( m_negation_prefix, m, negation_prefix ); | ||
|  | 
 | ||
|  |         BOOST_TEST_I_ASSRT( algorithm::all_of( m_end_of_param_indicator.begin(), | ||
|  |                                                m_end_of_param_indicator.end(), | ||
|  |                                                parameter_cla_id::valid_prefix_char ), | ||
|  |                             invalid_cla_id() << "End of parameters indicator can only consist of prefix characters." ); | ||
|  | 
 | ||
|  |         BOOST_TEST_I_ASSRT( algorithm::all_of( m_negation_prefix.begin(), | ||
|  |                                                m_negation_prefix.end(), | ||
|  |                                                parameter_cla_id::valid_name_char ), | ||
|  |                             invalid_cla_id() << "Negation prefix can only consist of prefix characters." ); | ||
|  | 
 | ||
|  |         build_trie( parameters ); | ||
|  |     } | ||
|  | 
 | ||
|  |     // input processing method | ||
|  |     int | ||
|  |     parse( int argc, char** argv, runtime::arguments_store& res ) | ||
|  |     { | ||
|  |         // save program name for help message | ||
|  |         m_program_name = argv[0]; | ||
|  |         cstring path_sep( "\\/" ); | ||
|  | 
 | ||
|  |         cstring::iterator it = unit_test::utils::find_last_of( m_program_name.begin(), m_program_name.end(), | ||
|  |                                                                 path_sep.begin(), path_sep.end() ); | ||
|  |         if( it != m_program_name.end() ) | ||
|  |             m_program_name.trim_left( it + 1 ); | ||
|  | 
 | ||
|  |         // Set up the traverser | ||
|  |         argv_traverser tr( argc, (char const**)argv ); | ||
|  | 
 | ||
|  |         // Loop till we reach end of input | ||
|  |         while( !tr.eoi() ) { | ||
|  |             cstring curr_token = tr.current_token(); | ||
|  | 
 | ||
|  |             cstring prefix; | ||
|  |             cstring name; | ||
|  |             cstring value_separator; | ||
|  |             bool    negative_form = false; | ||
|  | 
 | ||
|  |             // Perform format validations and split the argument into prefix, name and separator | ||
|  |             // False return value indicates end of params indicator is met | ||
|  |             if( !validate_token_format( curr_token, prefix, name, value_separator, negative_form ) ) { | ||
|  |                 // get rid of "end of params" token | ||
|  |                 tr.next_token(); | ||
|  |                 break; | ||
|  |             } | ||
|  | 
 | ||
|  |             // Locate trie corresponding to found prefix and skip it in the input | ||
|  |             trie_ptr curr_trie = m_param_trie[prefix]; | ||
|  | 
 | ||
|  |             if( !curr_trie ) { | ||
|  |                 //  format_error() << "Unrecognized parameter prefix in the argument " << tr.current_token() | ||
|  |                 rt_cla_detail::report_foreing_token( m_program_name, curr_token ); | ||
|  |                 tr.save_token(); | ||
|  |                 continue; | ||
|  |             } | ||
|  | 
 | ||
|  |             curr_token.trim_left( prefix.size() ); | ||
|  | 
 | ||
|  |             // Locate parameter based on a name and skip it in the input | ||
|  |             locate_result locate_res = locate_parameter( curr_trie, name, curr_token ); | ||
|  |             parameter_cla_id const& found_id    = locate_res.first; | ||
|  |             basic_param_ptr         found_param = locate_res.second; | ||
|  | 
 | ||
|  |             if( negative_form ) { | ||
|  |                 BOOST_TEST_I_ASSRT( found_id.m_negatable, | ||
|  |                                     format_error( found_param->p_name )  | ||
|  |                                         << "Parameter tag " << found_id.m_tag << " is not negatable." ); | ||
|  | 
 | ||
|  |                 curr_token.trim_left( m_negation_prefix.size() ); | ||
|  |             } | ||
|  | 
 | ||
|  |             curr_token.trim_left( name.size() ); | ||
|  | 
 | ||
|  |             cstring value; | ||
|  | 
 | ||
|  |             // Skip validations if parameter has optional value and we are at the end of token | ||
|  |             if( !value_separator.is_empty() || !found_param->p_has_optional_value ) { | ||
|  |                 // Validate and skip value separator in the input | ||
|  |                 BOOST_TEST_I_ASSRT( found_id.m_value_separator == value_separator, | ||
|  |                                     format_error( found_param->p_name )  | ||
|  |                                         << "Invalid separator for the parameter " | ||
|  |                                         << found_param->p_name | ||
|  |                                         << " in the argument " << tr.current_token() ); | ||
|  | 
 | ||
|  |                 curr_token.trim_left( value_separator.size() ); | ||
|  | 
 | ||
|  |                 // Deduce value source | ||
|  |                 value = curr_token; | ||
|  |                 if( value.is_empty() ) { | ||
|  |                     tr.next_token(); | ||
|  |                     value = tr.current_token(); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 BOOST_TEST_I_ASSRT( !value.is_empty(), | ||
|  |                                     format_error( found_param->p_name ) | ||
|  |                                         << "Missing an argument value for the parameter " | ||
|  |                                         << found_param->p_name | ||
|  |                                         << " in the argument " << tr.current_token() ); | ||
|  |             } | ||
|  | 
 | ||
|  |             // Validate against argument duplication | ||
|  |             BOOST_TEST_I_ASSRT( !res.has( found_param->p_name ) || found_param->p_repeatable, | ||
|  |                                 duplicate_arg( found_param->p_name ) | ||
|  |                                     << "Duplicate argument value for the parameter " | ||
|  |                                     << found_param->p_name | ||
|  |                                     << " in the argument " << tr.current_token() ); | ||
|  | 
 | ||
|  |             // Produce argument value | ||
|  |             found_param->produce_argument( value, negative_form, res ); | ||
|  | 
 | ||
|  |             tr.next_token(); | ||
|  |         } | ||
|  | 
 | ||
|  |         // generate the remainder and return it's size | ||
|  |         return tr.remainder(); | ||
|  |     } | ||
|  | 
 | ||
|  |     // help/usage/version | ||
|  |     void | ||
|  |     version( std::ostream& ostr ) | ||
|  |     { | ||
|  |        ostr << "Boost.Test module "; | ||
|  | 
 | ||
|  | #if defined(BOOST_TEST_MODULE) | ||
|  |        // we do not want to refer to the master test suite there | ||
|  |        ostr << '\'' << BOOST_TEST_STRINGIZE( BOOST_TEST_MODULE ).trim( "\"" ) << "' "; | ||
|  | #endif | ||
|  | 
 | ||
|  |        ostr << "in executable '" << m_program_name << "'\n"; | ||
|  |        ostr << "Compiled from Boost version " | ||
|  |             << BOOST_VERSION/100000      << "." | ||
|  |             << BOOST_VERSION/100 % 1000  << "." | ||
|  |             << BOOST_VERSION % 100       ; | ||
|  |        ostr << " with "; | ||
|  | #if defined(BOOST_TEST_INCLUDED) | ||
|  |        ostr << "single header inclusion of"; | ||
|  | #elif defined(BOOST_TEST_DYN_LINK) | ||
|  |        ostr << "dynamic linking to"; | ||
|  | #else | ||
|  |        ostr << "static linking to"; | ||
|  | #endif | ||
|  |        ostr << " Boost.Test\n"; | ||
|  |        ostr << "- Compiler: " << BOOST_COMPILER << '\n' | ||
|  |             << "- Platform: " << BOOST_PLATFORM << '\n' | ||
|  |             << "- STL     : " << BOOST_STDLIB; | ||
|  |        ostr << std::endl; | ||
|  |     } | ||
|  | 
 | ||
|  |     void | ||
|  |     usage( std::ostream& ostr, cstring param_name = cstring() ) | ||
|  |     { | ||
|  |         if( !param_name.is_empty() ) { | ||
|  |             basic_param_ptr param = locate_parameter( m_param_trie[help_prefix], param_name, "" ).second; | ||
|  |             param->usage( ostr, m_negation_prefix ); | ||
|  |         } | ||
|  |         else { | ||
|  |             ostr << "Usage: " << m_program_name << " [Boost.Test argument]... "; | ||
|  |             if( !m_end_of_param_indicator.empty() ) | ||
|  |                 ostr << m_end_of_param_indicator << " [custom test module argument]..."; | ||
|  |             ostr << "\n"; | ||
|  |         } | ||
|  | 
 | ||
|  |         ostr << "\nFor detailed help on Boost.Test parameters use:\n" | ||
|  |              << "  " << m_program_name << " --help\n" | ||
|  |              << "or\n" | ||
|  |              << "  " << m_program_name << " --help=<parameter name>\n"; | ||
|  |     } | ||
|  | 
 | ||
|  |     void | ||
|  |     help( std::ostream& ostr, parameters_store const& parameters, cstring param_name ) | ||
|  |     { | ||
|  |         if( !param_name.is_empty() ) { | ||
|  |             basic_param_ptr param = locate_parameter( m_param_trie[help_prefix], param_name, "" ).second; | ||
|  |             param->help( ostr, m_negation_prefix ); | ||
|  |             return; | ||
|  |         } | ||
|  | 
 | ||
|  |         ostr << "Usage: " << m_program_name << " [Boost.Test argument]... "; | ||
|  |         if( !m_end_of_param_indicator.empty() ) | ||
|  |             ostr << m_end_of_param_indicator << " [custom test module argument]..."; | ||
|  | 
 | ||
|  |         ostr << "\n\nBoost.Test arguments correspond to parameters listed below. " | ||
|  |                 "All parameters are optional. You can use specify parameter value either " | ||
|  |                 "as a command line argument or as a value of corresponding environment " | ||
|  |                 "variable. In case if argument for the same parameter is specified in both " | ||
|  |                 "places, command line is taking precedence. Command line argument format " | ||
|  |                 "supports parameter name guessing, so you can use any unambiguous " | ||
|  |                 "prefix to identify a parameter."; | ||
|  |         if( !m_end_of_param_indicator.empty() ) | ||
|  |             ostr << " All the arguments after the " << m_end_of_param_indicator << " are ignored by the Boost.Test."; | ||
|  | 
 | ||
|  |         ostr << "\n\nBoost.Test supports following parameters:\n"; | ||
|  | 
 | ||
|  |         BOOST_TEST_FOREACH( parameters_store::storage_type::value_type const&, v, parameters.all() ) { | ||
|  |             basic_param_ptr param = v.second; | ||
|  | 
 | ||
|  |             param->usage( ostr, m_negation_prefix ); | ||
|  |         } | ||
|  | 
 | ||
|  |         ostr << "\nUse --help=<parameter name> to display detailed help for specific parameter.\n"; | ||
|  |     } | ||
|  | 
 | ||
|  | private: | ||
|  |     typedef rt_cla_detail::parameter_trie_ptr   trie_ptr; | ||
|  |     typedef rt_cla_detail::trie_per_char        trie_per_char; | ||
|  |     typedef std::map<cstring,trie_ptr>          str_to_trie; | ||
|  | 
 | ||
|  |     void | ||
|  |     build_trie( parameters_store const& parameters ) | ||
|  |     { | ||
|  |         // Iterate over all parameters | ||
|  |         BOOST_TEST_FOREACH( parameters_store::storage_type::value_type const&, v, parameters.all() ) { | ||
|  |             basic_param_ptr param = v.second; | ||
|  | 
 | ||
|  |             // Register all parameter's ids in trie. | ||
|  |             BOOST_TEST_FOREACH( parameter_cla_id const&, id, param->cla_ids() ) { | ||
|  |                 // This is the trie corresponding to the prefix. | ||
|  |                 trie_ptr next_trie = m_param_trie[id.m_prefix]; | ||
|  |                 if( !next_trie ) | ||
|  |                     next_trie = m_param_trie[id.m_prefix] = trie_ptr( new rt_cla_detail::parameter_trie ); | ||
|  | 
 | ||
|  |                 // Build the trie, by following name's characters | ||
|  |                 // and register this parameter as candidate on each level | ||
|  |                 for( size_t index = 0; index < id.m_tag.size(); ++index ) { | ||
|  |                     next_trie = next_trie->make_subtrie( id.m_tag[index] ); | ||
|  | 
 | ||
|  |                     next_trie->add_candidate_id( id, param, index == (id.m_tag.size() - 1) ); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     bool | ||
|  |     validate_token_format( cstring token, cstring& prefix, cstring& name, cstring& separator, bool& negative_form ) | ||
|  |     { | ||
|  |         // Match prefix | ||
|  |         cstring::iterator it = token.begin(); | ||
|  |         while( it != token.end() && parameter_cla_id::valid_prefix_char( *it ) ) | ||
|  |             ++it; | ||
|  | 
 | ||
|  |         prefix.assign( token.begin(), it ); | ||
|  | 
 | ||
|  |         if( prefix.empty() ) | ||
|  |             return true; | ||
|  | 
 | ||
|  |         // Match name | ||
|  |         while( it != token.end() && parameter_cla_id::valid_name_char( *it ) ) | ||
|  |             ++it; | ||
|  | 
 | ||
|  |         name.assign( prefix.end(), it ); | ||
|  | 
 | ||
|  |         if( name.empty() ) { | ||
|  |             if( prefix == m_end_of_param_indicator ) | ||
|  |                 return false; | ||
|  | 
 | ||
|  |             BOOST_TEST_I_THROW( format_error() << "Invalid format for an actual argument " << token ); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Match value separator | ||
|  |         while( it != token.end() && parameter_cla_id::valid_separator_char( *it ) ) | ||
|  |             ++it; | ||
|  | 
 | ||
|  |         separator.assign( name.end(), it ); | ||
|  | 
 | ||
|  |         // Match negation prefix | ||
|  |         negative_form = !m_negation_prefix.empty() && ( name.substr( 0, m_negation_prefix.size() ) == m_negation_prefix ); | ||
|  |         if( negative_form ) | ||
|  |             name.trim_left( m_negation_prefix.size() ); | ||
|  | 
 | ||
|  |         return true; | ||
|  |     } | ||
|  | 
 | ||
|  |     // C++03: cannot have references as types | ||
|  |     typedef std::pair<parameter_cla_id, basic_param_ptr> locate_result; | ||
|  | 
 | ||
|  |     locate_result | ||
|  |     locate_parameter( trie_ptr curr_trie, cstring name, cstring token ) | ||
|  |     { | ||
|  |         std::vector<trie_ptr> typo_candidates; | ||
|  |         std::vector<trie_ptr> next_typo_candidates; | ||
|  |         trie_ptr next_trie; | ||
|  | 
 | ||
|  |         BOOST_TEST_FOREACH( char, c, name ) { | ||
|  |             if( curr_trie ) { | ||
|  |                 // locate next subtrie corresponding to the char | ||
|  |                 next_trie = curr_trie->get_subtrie( c ); | ||
|  | 
 | ||
|  |                 if( next_trie ) | ||
|  |                     curr_trie = next_trie; | ||
|  |                 else { | ||
|  |                     // Initiate search for typo candicates. We will account for 'wrong char' typo | ||
|  |                     // 'missing char' typo and 'extra char' typo | ||
|  |                     BOOST_TEST_FOREACH( trie_per_char::value_type const&, typo_cand, curr_trie->m_subtrie ) { | ||
|  |                         // 'wrong char' typo | ||
|  |                         typo_candidates.push_back( typo_cand.second ); | ||
|  | 
 | ||
|  |                         // 'missing char' typo | ||
|  |                         if( (next_trie = typo_cand.second->get_subtrie( c )) ) | ||
|  |                             typo_candidates.push_back( next_trie ); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     // 'extra char' typo | ||
|  |                     typo_candidates.push_back( curr_trie ); | ||
|  | 
 | ||
|  |                     curr_trie.reset(); | ||
|  |                 } | ||
|  |             } | ||
|  |             else { | ||
|  |                 // go over existing typo candidates and see if they are still viable | ||
|  |                 BOOST_TEST_FOREACH( trie_ptr, typo_cand, typo_candidates ) { | ||
|  |                     trie_ptr next_typo_cand = typo_cand->get_subtrie( c ); | ||
|  | 
 | ||
|  |                     if( next_typo_cand ) | ||
|  |                         next_typo_candidates.push_back( next_typo_cand ); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 next_typo_candidates.swap( typo_candidates ); | ||
|  |                 next_typo_candidates.clear(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         if( !curr_trie ) { | ||
|  |             std::vector<cstring> typo_candidate_names; | ||
|  |             std::set<parameter_cla_id const*> unique_typo_candidate; // !! ?? unordered_set | ||
|  |             typo_candidate_names.reserve( typo_candidates.size() ); | ||
|  | // !! ??            unique_typo_candidate.reserve( typo_candidates.size() ); | ||
|  | 
 | ||
|  |             BOOST_TEST_FOREACH( trie_ptr, trie_cand, typo_candidates ) { | ||
|  |                 // avoid ambiguos candidate trie | ||
|  |                 if( trie_cand->m_id_candidates.size() > 1 ) | ||
|  |                     continue; | ||
|  | 
 | ||
|  |                 BOOST_TEST_FOREACH( parameter_cla_id const&, param_cand, trie_cand->m_id_candidates ) { | ||
|  |                     if( !unique_typo_candidate.insert( ¶m_cand ).second ) | ||
|  |                         continue; | ||
|  | 
 | ||
|  |                     typo_candidate_names.push_back( param_cand.m_tag ); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  | #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES | ||
|  |             BOOST_TEST_I_THROW( unrecognized_param( std::move(typo_candidate_names) ) | ||
|  |                                 << "An unrecognized parameter in the argument " | ||
|  |                                 << token ); | ||
|  | #else | ||
|  |             BOOST_TEST_I_THROW( unrecognized_param( typo_candidate_names ) | ||
|  |                                 << "An unrecognized parameter in the argument " | ||
|  |                                 << token ); | ||
|  | #endif | ||
|  |         } | ||
|  | 
 | ||
|  |         if( curr_trie->m_id_candidates.size() > 1 ) { | ||
|  |             std::vector<cstring> amb_names; | ||
|  |             BOOST_TEST_FOREACH( parameter_cla_id const&, param_id, curr_trie->m_id_candidates ) | ||
|  |                 amb_names.push_back( param_id.m_tag ); | ||
|  | 
 | ||
|  | #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES | ||
|  |             BOOST_TEST_I_THROW( ambiguous_param( std::move( amb_names ) ) | ||
|  |                                 << "An ambiguous parameter name in the argument " << token ); | ||
|  | #else | ||
|  |             BOOST_TEST_I_THROW( ambiguous_param( amb_names ) | ||
|  |                                 << "An ambiguous parameter name in the argument " << token ); | ||
|  | #endif | ||
|  |         } | ||
|  | 
 | ||
|  |         return locate_result( curr_trie->m_id_candidates.back().get(), curr_trie->m_param_candidate ); | ||
|  |     } | ||
|  | 
 | ||
|  |     // Data members | ||
|  |     cstring     m_program_name; | ||
|  |     std::string m_end_of_param_indicator; | ||
|  |     std::string m_negation_prefix; | ||
|  |     str_to_trie m_param_trie; | ||
|  | }; | ||
|  | 
 | ||
|  | } // namespace cla | ||
|  | } // namespace runtime | ||
|  | } // namespace boost | ||
|  | 
 | ||
|  | #include <boost/test/detail/enable_warnings.hpp> | ||
|  | 
 | ||
|  | #endif // BOOST_TEST_UTILS_RUNTIME_CLA_PARSER_HPP |