You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							516 lines
						
					
					
						
							23 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							516 lines
						
					
					
						
							23 KiB
						
					
					
				| /* | |
|     Copyright 2005-2014 Intel Corporation.  All Rights Reserved. | |
|  | |
|     This file is part of Threading Building Blocks. | |
|  | |
|     Threading Building Blocks is free software; you can redistribute it | |
|     and/or modify it under the terms of the GNU General Public License | |
|     version 2 as published by the Free Software Foundation. | |
|  | |
|     Threading Building Blocks is distributed in the hope that it will be | |
|     useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
|     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | |
|     GNU General Public License for more details. | |
|  | |
|     You should have received a copy of the GNU General Public License | |
|     along with Threading Building Blocks; if not, write to the Free Software | |
|     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | |
|  | |
|     As a special exception, you may use this file as part of a free software | |
|     library without restriction.  Specifically, if other files instantiate | |
|     templates or use macros or inline functions from this file, or you compile | |
|     this file and link it with other files to produce an executable, this | |
|     file does not by itself cause the resulting executable to be covered by | |
|     the GNU General Public License.  This exception does not however | |
|     invalidate any other reasons why the executable file might be covered by | |
|     the GNU General Public License. | |
| */ | |
| 
 | |
| #ifndef UTILITY_H_ | |
| #define UTILITY_H_ | |
|  | |
| #include <string> | |
| #include <cstring> | |
| #include <vector> | |
| #include <map> | |
| #include <set> | |
| #include <algorithm> | |
| #include <sstream> | |
| #include <numeric> | |
| #include <stdexcept> | |
| #include <memory> | |
| #include <cassert> | |
| #include <iostream> | |
| #include <cstdlib> | |
| // TBB headers should not be used, as some examples may need to be built without TBB. | |
|  | |
| namespace utility{ | |
|     namespace internal{ | |
| 
 | |
| #if ((__GNUC__*100+__GNUC_MINOR__>=404 && __GXX_EXPERIMENTAL_CXX0X__) || _MSC_VER >= 1600) && (!__INTEL_COMPILER || __INTEL_COMPILER >= 1200 ) | |
|     // std::unique_ptr is available, and compiler can use it | |
|     #define smart_ptr std::unique_ptr | |
|     using std::swap; | |
| #else | |
|     #if __INTEL_COMPILER && __GXX_EXPERIMENTAL_CXX0X__ | |
|     // std::unique_ptr is unavailable, so suppress std::auto_prt<> deprecation warning | |
|     #pragma warning(disable: 1478) | |
|     #endif | |
|     #define smart_ptr std::auto_ptr | |
|     // in some C++ libraries, std::swap does not work with std::auto_ptr | |
|     template<typename T> | |
|     void swap( std::auto_ptr<T>& ptr1, std::auto_ptr<T>& ptr2 ) { | |
|         std::auto_ptr<T> tmp; tmp = ptr2; ptr2 = ptr1; ptr1 = tmp; | |
|     } | |
| #endif | |
|  | |
|         //TODO: add tcs | |
|         template<class dest_type> | |
|         dest_type& string_to(std::string const& s, dest_type& result){ | |
|             std::stringstream stream(s); | |
|             stream>>result; | |
|             if ((!stream)||(stream.fail())){ | |
|                 throw std::invalid_argument("error converting string '"+std::string(s)+"'"); | |
|             } | |
|             return result; | |
|         } | |
| 
 | |
|         template<class dest_type> | |
|         dest_type string_to(std::string const& s){ | |
|             dest_type result; | |
|             return string_to(s,result); | |
|         } | |
| 
 | |
|         template<typename> | |
|         struct is_bool          { static bool value(){return false;}}; | |
|         template<> | |
|         struct is_bool<bool>    { static bool value(){return true;}}; | |
| 
 | |
|         class type_base { | |
|             type_base& operator=(const type_base&); | |
|             public: | |
|             const std::string name; | |
|             const std::string description; | |
| 
 | |
|             type_base (std::string a_name, std::string a_description) : name(a_name), description(a_description) {} | |
|             virtual void parse_and_store (const std::string & s)=0; | |
|             virtual std::string value() const =0; | |
|             virtual smart_ptr<type_base> clone()const =0; | |
|             virtual ~type_base(){} | |
|         }; | |
|         template <typename type> | |
|         class type_impl : public type_base { | |
|         private: | |
|             type_impl& operator=(const type_impl&); | |
|             typedef bool(*validating_function_type)(const type&); | |
|         private: | |
|             type & target; | |
|             validating_function_type validating_function; | |
|         public: | |
|             type_impl(std::string a_name, std::string a_description, type & a_target, validating_function_type a_validating_function = NULL) | |
|                 : type_base (a_name,a_description), target(a_target),validating_function(a_validating_function) | |
|             {}; | |
|             void parse_and_store (const std::string & s){ | |
|                 try{ | |
|                     const bool is_bool = internal::is_bool<type>::value(); | |
|                     if (is_bool && s.empty()){ | |
|                         //to avoid directly assigning true | |
|                         //(as it will impose additional layer of indirection) | |
|                         //so, simply pass it as string | |
|                         internal::string_to("1",target); | |
|                     }else { | |
|                         internal::string_to(s,target); | |
|                     } | |
|                 }catch(std::invalid_argument& e){ | |
|                     std::stringstream str; | |
|                     str <<"'"<<s<<"' is incorrect input for argument '"<<name<<"'" | |
|                         <<" ("<<e.what()<<")"; | |
|                     throw std::invalid_argument(str.str()); | |
|                 } | |
|                 if (validating_function){ | |
|                     if (!((validating_function)(target))){ | |
|                         std::stringstream str; | |
|                         str <<"'"<<target<<"' is invalid value for argument '"<<name<<"'"; | |
|                         throw std::invalid_argument(str.str()); | |
|                     } | |
|                 } | |
|             } | |
|             virtual std::string value()const{ | |
|                 std::stringstream str; | |
|                 str<<target; | |
|                 return str.str(); | |
|             } | |
|             virtual smart_ptr<type_base> clone() const { | |
|                 return smart_ptr<type_base>(new type_impl(*this)); | |
|             } | |
|         }; | |
| 
 | |
|         class argument{ | |
|         private: | |
|             smart_ptr<type_base> p_type; | |
|             bool matched_; | |
|         public: | |
|             argument(argument const& other) | |
|                 : p_type(other.p_type.get() ? (other.p_type->clone()).release() : NULL) | |
|                  ,matched_(other.matched_) | |
|             {} | |
|             argument& operator=(argument a){ | |
|                 this->swap(a); | |
|                 return *this; | |
|             } | |
|             void swap(argument& other){ | |
|                 internal::swap(p_type, other.p_type); | |
|                 std::swap(matched_,other.matched_); | |
|             } | |
|             template<class type> | |
|             argument(std::string a_name, std::string a_description, type& dest, bool(*a_validating_function)(const type&)= NULL) | |
|                 :p_type(new type_impl<type>(a_name,a_description,dest,a_validating_function)) | |
|                  ,matched_(false) | |
|             {} | |
|             std::string value()const{ | |
|                 return p_type->value(); | |
|             } | |
|             std::string name()const{ | |
|                 return p_type->name; | |
|             } | |
|             std::string description() const{ | |
|                 return p_type->description; | |
|             } | |
|             void parse_and_store(const std::string & s){ | |
|                 p_type->parse_and_store(s); | |
|                 matched_=true; | |
|             } | |
|             bool is_matched() const{return matched_;} | |
|         }; | |
|     } // namespace internal | |
|  | |
|     class cli_argument_pack{ | |
|         typedef std::map<std::string,internal::argument> args_map_type; | |
|         typedef std::vector<std::string> args_display_order_type; | |
|         typedef std::vector<std::string> positional_arg_names_type; | |
|     private: | |
|         args_map_type args_map; | |
|         args_display_order_type args_display_order; | |
|         positional_arg_names_type positional_arg_names; | |
|         std::set<std::string> bool_args_names; | |
|     private: | |
|         void add_arg(internal::argument const& a){ | |
|             std::pair<args_map_type::iterator, bool> result = args_map.insert(std::make_pair(a.name(),a)); | |
|             if (!result.second){ | |
|                 throw std::invalid_argument("argument with name: '"+a.name()+"' already registered"); | |
|             } | |
|             args_display_order.push_back(a.name()); | |
|         } | |
|     public: | |
|         template<typename type> | |
|         cli_argument_pack& arg(type& dest,std::string const& name, std::string const& description, bool(*validate)(const type &)= NULL){ | |
|             internal::argument a(name,description,dest,validate); | |
|             add_arg(a); | |
|             if (internal::is_bool<type>::value()){ | |
|                 bool_args_names.insert(name); | |
|             } | |
|             return *this; | |
|         } | |
| 
 | |
|         //Positional means that argument name can be omitted in actual CL | |
|         //only key to match values for parameters with | |
|         template<typename type> | |
|         cli_argument_pack& positional_arg(type& dest,std::string const& name, std::string const& description, bool(*validate)(const type &)= NULL){ | |
|             internal::argument a(name,description,dest,validate); | |
|             add_arg(a); | |
|             if (internal::is_bool<type>::value()){ | |
|                 bool_args_names.insert(name); | |
|             } | |
|             positional_arg_names.push_back(name); | |
|             return *this; | |
|         } | |
| 
 | |
|         void parse(std::size_t argc, char const* argv[]){ | |
|             { | |
|                 std::size_t current_positional_index=0; | |
|                 for (std::size_t j=1;j<argc;j++){ | |
|                     internal::argument* pa = NULL; | |
|                     std::string argument_value; | |
| 
 | |
|                     const char * const begin=argv[j]; | |
|                     const char * const end=begin+std::strlen(argv[j]); | |
| 
 | |
|                     const char * const assign_sign = std::find(begin,end,'='); | |
| 
 | |
|                     struct throw_unknown_parameter{ static void _(std::string const& location){ | |
|                         throw std::invalid_argument(std::string("unknown parameter starting at:'")+location+"'"); | |
|                     }}; | |
|                     //first try to interpret it like parameter=value string | |
|                     if (assign_sign!=end){ | |
|                         std::string name_found = std::string(begin,assign_sign); | |
|                         args_map_type::iterator it = args_map.find(name_found ); | |
| 
 | |
|                         if(it!=args_map.end()){ | |
|                             pa= &((*it).second); | |
|                             argument_value = std::string(assign_sign+1,end); | |
|                         }else { | |
|                             throw_unknown_parameter::_(argv[j]); | |
|                         } | |
|                     } | |
|                     //then see is it a named flag | |
|                     else{ | |
|                         args_map_type::iterator it = args_map.find(argv[j] ); | |
|                         if(it!=args_map.end()){ | |
|                             pa= &((*it).second); | |
|                             argument_value = ""; | |
|                         } | |
|                         //then try it as positional argument without name specified | |
|                         else if (current_positional_index < positional_arg_names.size()){ | |
|                             std::stringstream str(argv[j]); | |
|                             args_map_type::iterator found_positional_arg = args_map.find(positional_arg_names.at(current_positional_index)); | |
|                             //TODO: probably use of smarter assert would help here | |
|                             assert(found_positional_arg!=args_map.end()/*&&"positional_arg_names and args_map are out of sync"*/); | |
|                             if (found_positional_arg==args_map.end()){ | |
|                                 throw std::logic_error("positional_arg_names and args_map are out of sync"); | |
|                             } | |
|                             pa= &((*found_positional_arg).second); | |
|                             argument_value = argv[j]; | |
| 
 | |
|                             current_positional_index++; | |
|                         }else { | |
|                             //TODO: add tc to check | |
|                             throw_unknown_parameter::_(argv[j]); | |
|                         } | |
|                     } | |
|                     assert(pa); | |
|                     if (pa->is_matched()){ | |
|                         throw std::invalid_argument(std::string("several values specified for: '")+pa->name()+"' argument"); | |
|                     } | |
|                     pa->parse_and_store(argument_value); | |
|                 } | |
|             } | |
|         } | |
|         std::string usage_string(const std::string& binary_name)const{ | |
|             std::string command_line_params; | |
|             std::string summary_description; | |
| 
 | |
|             for (args_display_order_type::const_iterator it = args_display_order.begin();it!=args_display_order.end();++it){ | |
|                 const bool is_bool = (0!=bool_args_names.count((*it))); | |
|                 args_map_type::const_iterator argument_it = args_map.find(*it); | |
|                 //TODO: probably use of smarter assert would help here | |
|                 assert(argument_it!=args_map.end()/*&&"args_display_order and args_map are out of sync"*/); | |
|                 if (argument_it==args_map.end()){ | |
|                     throw std::logic_error("args_display_order and args_map are out of sync"); | |
|                 } | |
|                 const internal::argument & a = (*argument_it).second; | |
|                 command_line_params +=" [" + a.name() + (is_bool ?"":"=value")+ "]"; | |
|                 summary_description +=" " + a.name() + " - " + a.description() +" ("+a.value() +")" + "\n"; | |
|             } | |
| 
 | |
|             std::string positional_arg_cl; | |
|             for (positional_arg_names_type::const_iterator it = positional_arg_names.begin();it!=positional_arg_names.end();++it){ | |
|                 positional_arg_cl +=" ["+(*it); | |
|             } | |
|             for (std::size_t i=0;i<positional_arg_names.size();++i){ | |
|                 positional_arg_cl+="]"; | |
|             } | |
|             command_line_params+=positional_arg_cl; | |
|             std::stringstream str; | |
|             using std::endl; | |
|             str << " Program usage is:" << endl | |
|                  << " " << binary_name << command_line_params | |
|                  << endl << endl | |
|                  << " where:" << endl | |
|                  << summary_description | |
|             ; | |
|             return str.str(); | |
|         } | |
|     }; // class cli_argument_pack | |
|  | |
|     namespace internal { | |
|         template<typename T> | |
|         bool is_power_of_2( T val ) { | |
|             size_t intval = size_t(val); | |
|             return (intval&(intval-1)) == size_t(0); | |
|         } | |
|         int step_function_plus(int previous, double step){ | |
|             return static_cast<int>(previous+step); | |
|         } | |
|         int step_function_multiply(int previous, double multiply){ | |
|             return static_cast<int>(previous*multiply); | |
|         } | |
|         // "Power-of-2 ladder": nsteps is the desired number of steps between any subsequent powers of 2. | |
|         // The actual step is the quotient of the nearest smaller power of 2 divided by that number (but at least 1). | |
|         // E.g., '1:32:#4' means 1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32 | |
|         int step_function_power2_ladder(int previous, double nsteps){ | |
|             int steps = int(nsteps); | |
|             assert( is_power_of_2(steps) );  // must be a power of 2 | |
|             // The actual step is 1 until the value is twice as big as nsteps | |
|             if( previous < 2*steps ) | |
|                 return previous+1; | |
|             // calculate the previous power of 2 | |
|             int prev_power2 = previous/2;                 // start with half the given value | |
|             int rshift = 1;                               // and with the shift of 1; | |
|             while( int shifted = prev_power2>>rshift ) {  // shift the value right; while the result is non-zero, | |
|                 prev_power2 |= shifted;                   //   add the bits set in 'shifted'; | |
|                 rshift <<= 1;                             //   double the shift, as twice as many top bits are set; | |
|             }                                             // repeat. | |
|             ++prev_power2; // all low bits set; now it's just one less than the desired power of 2 | |
|             assert( is_power_of_2(prev_power2) ); | |
|             assert( (prev_power2<=previous)&&(2*prev_power2>previous) ); | |
|             // The actual step value is the previous power of 2 divided by steps | |
|             return previous + (prev_power2/steps); | |
|         } | |
|         typedef int (* step_function_ptr_type)(int,double); | |
| 
 | |
|         struct step_function_descriptor  { | |
|             char mnemonic; | |
|             step_function_ptr_type function; | |
|         public: | |
|             step_function_descriptor(char a_mnemonic, step_function_ptr_type a_function) : mnemonic(a_mnemonic), function(a_function) {} | |
|         private: | |
|             void operator=(step_function_descriptor  const&); | |
|         }; | |
|         step_function_descriptor step_function_descriptors[] = { | |
|                 step_function_descriptor('*',step_function_multiply), | |
|                 step_function_descriptor('+',step_function_plus), | |
|                 step_function_descriptor('#',step_function_power2_ladder) | |
|         }; | |
| 
 | |
|         template<typename T, size_t N> | |
|         inline size_t array_length(const T(&)[N]) | |
|         { | |
|            return N; | |
|         } | |
| 
 | |
|         struct thread_range_step { | |
|             step_function_ptr_type step_function; | |
|             double step_function_argument; | |
| 
 | |
|             thread_range_step ( step_function_ptr_type step_function_, double step_function_argument_) | |
|                 :step_function(step_function_),step_function_argument(step_function_argument_) | |
|             { | |
|                 if (!step_function_) | |
|                     throw std::invalid_argument("step_function for thread range step should not be NULL"); | |
|             } | |
|             int operator()(int previous)const { | |
|                 assert(0<=previous); // test 0<=first and loop discipline | |
|                 const int ret = step_function(previous,step_function_argument); | |
|                 assert(previous<ret); | |
|                 return ret; | |
|             } | |
|             friend std::istream& operator>>(std::istream& input_stream, thread_range_step& step){ | |
|                 char function_char; | |
|                 double function_argument; | |
|                 input_stream >> function_char >> function_argument; | |
|                 size_t i = 0; | |
|                 while ((i<array_length(step_function_descriptors)) && (step_function_descriptors[i].mnemonic!=function_char)) ++i; | |
|                 if (i >= array_length(step_function_descriptors)){ | |
|                     throw std::invalid_argument("unknown step function mnemonic: "+std::string(1,function_char)); | |
|                 } else if ((function_char=='#') && !is_power_of_2(function_argument)) { | |
|                     throw std::invalid_argument("the argument of # should be a power of 2"); | |
|                 } | |
|                 step.step_function = step_function_descriptors[i].function; | |
|                 step.step_function_argument = function_argument; | |
|                 return input_stream; | |
|             } | |
|         }; | |
|     } // namespace internal | |
|  | |
|     struct thread_number_range{ | |
|         int (*auto_number_of_threads)(); | |
|         int first; // 0<=first (0 can be used as a special value) | |
|         int last;  // first<=last | |
|  | |
|         internal::thread_range_step step; | |
| 
 | |
|         thread_number_range( int (*auto_number_of_threads_)(),int low_=1, int high_=-1 | |
|                 , internal::thread_range_step step_ =  internal::thread_range_step(internal::step_function_power2_ladder,4) | |
|         ) | |
|             : auto_number_of_threads(auto_number_of_threads_), first(low_), last((high_>-1) ? high_ : auto_number_of_threads_()) | |
|               ,step(step_) | |
|         { | |
|             if (first<0) { | |
|                 throw std::invalid_argument("negative value not allowed"); | |
|             } | |
|             if (first>last) { | |
|                 throw std::invalid_argument("decreasing sequence not allowed"); | |
|             } | |
|         } | |
|         friend std::istream& operator>>(std::istream& i, thread_number_range& range){ | |
|             try{ | |
|                 std::string s; | |
|                 i>>s; | |
|                 struct string_to_number_of_threads{ | |
|                     int auto_value; | |
|                     string_to_number_of_threads(int auto_value_):auto_value(auto_value_){} | |
|                     int operator()(const std::string & value)const{ | |
|                         return (value=="auto")? auto_value : internal::string_to<int>(value); | |
|                     } | |
|                 }; | |
|                 string_to_number_of_threads string_to_number_of_threads(range.auto_number_of_threads()); | |
|                 int low, high; | |
|                 std::size_t colon = s.find(':'); | |
|                 if ( colon == std::string::npos ){ | |
|                     low = high = string_to_number_of_threads(s); | |
|                 } else { | |
|                     //it is a range | |
|                     std::size_t second_colon = s.find(':',colon+1); | |
| 
 | |
|                     low  = string_to_number_of_threads(std::string(s, 0, colon)); //not copying the colon | |
|                     high = string_to_number_of_threads(std::string(s, colon+1, second_colon - (colon+1))); //not copying the colons | |
|                     if (second_colon != std::string::npos){ | |
|                         internal::string_to(std::string(s,second_colon + 1),range.step); | |
|                     } | |
|                 } | |
|                 range = thread_number_range(range.auto_number_of_threads,low,high,range.step); | |
|             }catch(std::invalid_argument&){ | |
|                 i.setstate(std::ios::failbit); | |
|                 throw; | |
|             } | |
|             return i; | |
|         } | |
|         friend std::ostream& operator<<(std::ostream& o, thread_number_range const& range){ | |
|             using namespace internal; | |
|             size_t i = 0; | |
|             for (; i < array_length(step_function_descriptors) && step_function_descriptors[i].function != range.step.step_function; ++i ) {} | |
|             if (i >= array_length(step_function_descriptors)){ | |
|                 throw std::invalid_argument("unknown step function for thread range"); | |
|             } | |
|             o<<range.first<<":"<<range.last<<":"<<step_function_descriptors[i].mnemonic<<range.step.step_function_argument; | |
|             return o; | |
|         } | |
|     }; // struct thread_number_range | |
|     //TODO: fix unused warning here | |
|     //TODO: update the thread range description in the .html files | |
|     static const char* thread_number_range_desc="number of threads to use; a range of the form low[:high[:(+|*|#)step]]," | |
|                                                 "\n\twhere low and optional high are non-negative integers or 'auto' for the default choice," | |
|                                                 "\n\tand optional step expression specifies how thread numbers are chosen within the range." | |
|                                                 "\n\tSee examples/common/index.html for detailed description." | |
|    ; | |
| 
 | |
|     inline void report_elapsed_time(double seconds){ | |
|         std::cout << "elapsed time : "<<seconds<<" seconds \n"; | |
|     } | |
| 
 | |
|     inline void parse_cli_arguments(int argc, const char* argv[], utility::cli_argument_pack cli_pack){ | |
|         bool show_help = false; | |
|         cli_pack.arg(show_help,"-h","show this message"); | |
| 
 | |
|         bool invalid_input=false; | |
|         try { | |
|             cli_pack.parse(argc,argv); | |
|         }catch(std::exception& e){ | |
|             std::cerr | |
|                     <<"error occurred while parsing command line."<<std::endl | |
|                     <<"error text: "<<e.what()<<std::endl | |
|                     <<std::flush; | |
|             invalid_input =true; | |
|         } | |
|         if (show_help || invalid_input){ | |
|             std::cout<<cli_pack.usage_string(argv[0])<<std::flush; | |
|             std::exit(0); | |
|         } | |
| 
 | |
|     } | |
|     inline void parse_cli_arguments(int argc, char* argv[], utility::cli_argument_pack cli_pack){ | |
|          parse_cli_arguments(argc, const_cast<const char**>(argv), cli_pack); | |
|     } | |
| } | |
| 
 | |
| #endif /* UTILITY_H_ */
 |