/* 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 #include #include #include #include #include #include #include #include #include #include #include #include // 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 void swap( std::auto_ptr& ptr1, std::auto_ptr& ptr2 ) { std::auto_ptr tmp; tmp = ptr2; ptr2 = ptr1; ptr1 = tmp; } #endif //TODO: add tcs template 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 dest_type string_to(std::string const& s){ dest_type result; return string_to(s,result); } template struct is_bool { static bool value(){return false;}}; template<> struct is_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 clone()const =0; virtual ~type_base(){} }; template 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::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 <<"'"< clone() const { return smart_ptr(new type_impl(*this)); } }; class argument{ private: smart_ptr 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 argument(std::string a_name, std::string a_description, type& dest, bool(*a_validating_function)(const type&)= NULL) :p_type(new type_impl(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 args_map_type; typedef std::vector args_display_order_type; typedef std::vector 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 bool_args_names; private: void add_arg(internal::argument const& a){ std::pair 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 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::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 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::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;jis_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 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(previous+step); } int step_function_multiply(int previous, double multiply){ return static_cast(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 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>(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)){ 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(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<(argv), cli_pack); } } #endif /* UTILITY_H_ */