/* Copyright 2005-2013 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 //TODO: for C++11 mode replace usage of auto_ptr with unique_ptr #include #include namespace utility{ namespace internal{ //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 { bool static value(){return false;}}; template<> struct is_bool { bool static 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 name, std::string description) : name(name), description(description) {} virtual void parse_and_store (const std::string & s)=0; virtual std::string value() const =0; virtual std::auto_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 name, std::string description, type & target, validating_function_type validating_function = NULL) : type_base (name,description), target(target),validating_function(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& ){ std::stringstream str; str <<"\""< clone()const { return std::auto_ptr(new type_impl(*this)); } }; class argument{ private: std::auto_ptr p_type; bool matched_; public: argument(argument const& other): p_type(other.p_type.get() ? other.p_type->clone():std::auto_ptr()),matched_(other.matched_){} argument& operator=(argument a){ this->swap(a); return *this; } void swap(argument& other){ std::auto_ptr tmp; tmp=p_type; p_type=other.p_type; other.p_type=tmp; std::swap(matched_,other.matched_); } template argument(std::string name, std::string description, type& dest, bool(*validating_function)(const type&)= NULL) :p_type(new type_impl(name,description,dest,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_;} }; } 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(int argc, char const* argv[]){ { std::size_t current_positional_index=0; for (int 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(previous+step); } int step_function_multiply(int previous,double multiply){ return static_cast(previous*multiply); } 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) }; 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 { return step_function(previous,step_function_argument); } 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; for ( ;(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("step_function for thread range step should be known"); } step.step_function = step_function_descriptors[i].function; step.step_function_argument = function_argument; return input_stream; } }; } struct thread_number_range{ int (*auto_number_of_threads)(); int first; int 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_plus,1) ) : auto_number_of_threads(auto_number_of_threads_), first(low_), last((high_>-1) ? high_ : auto_number_of_threads_()) ,step(step_) { if (first>last){ throw std::invalid_argument(""); } } 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{ int result=0; if (value=="auto"){ result = auto_value; } else{ internal::string_to(value,result); } return result; } }; string_to_number_of_threads string_to_number_of_threads(range.auto_number_of_threads()); int low =0; int high=0; std::size_t semicolon = s.find(':'); if (semicolon == std::string::npos ){ high= (low = string_to_number_of_threads(s)); }else { //it is a range std::size_t second_semicolon = s.find(':',semicolon+1); low = string_to_number_of_threads(std::string(s, 0, semicolon)); //not copying the ':' char high = string_to_number_of_threads(std::string(s, semicolon+1, second_semicolon - (semicolon+1))); //not copying the ':' chars if (second_semicolon != std::string::npos){ internal::string_to(std::string(s,second_semicolon + 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); } 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("step_function for thread range step should be known"); } o< namespace utility{ inline void report_elapsed_time(double seconds){ std::cout << "elapsed time : "< namespace utility{ 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."<(argv), cli_pack); } } #endif /* UTILITY_H_ */