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.

475 lines
20 KiB

  1. /*
  2. Copyright 2005-2013 Intel Corporation. All Rights Reserved.
  3. This file is part of Threading Building Blocks.
  4. Threading Building Blocks is free software; you can redistribute it
  5. and/or modify it under the terms of the GNU General Public License
  6. version 2 as published by the Free Software Foundation.
  7. Threading Building Blocks is distributed in the hope that it will be
  8. useful, but WITHOUT ANY WARRANTY; without even the implied warranty
  9. of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with Threading Building Blocks; if not, write to the Free Software
  13. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  14. As a special exception, you may use this file as part of a free software
  15. library without restriction. Specifically, if other files instantiate
  16. templates or use macros or inline functions from this file, or you compile
  17. this file and link it with other files to produce an executable, this
  18. file does not by itself cause the resulting executable to be covered by
  19. the GNU General Public License. This exception does not however
  20. invalidate any other reasons why the executable file might be covered by
  21. the GNU General Public License.
  22. */
  23. #ifndef UTILITY_H_
  24. #define UTILITY_H_
  25. #include <string>
  26. #include <cstring>
  27. #include <vector>
  28. #include <map>
  29. #include <set>
  30. #include <algorithm>
  31. #include <sstream>
  32. #include <numeric>
  33. #include <stdexcept>
  34. //TODO: for C++11 mode replace usage of auto_ptr with unique_ptr
  35. #include <memory>
  36. #include <cassert>
  37. namespace utility{
  38. namespace internal{
  39. //TODO: add tcs
  40. template<class dest_type>
  41. dest_type& string_to(std::string const& s, dest_type& result){
  42. std::stringstream stream(s);
  43. stream>>result;
  44. if ((!stream)||(stream.fail())){
  45. throw std::invalid_argument("error converting string \""+std::string(s)+"\"");
  46. }
  47. return result;
  48. }
  49. template<class dest_type>
  50. dest_type string_to(std::string const& s){
  51. dest_type result;
  52. return string_to(s,result);
  53. }
  54. template<typename>
  55. struct is_bool { bool static value(){return false;}};
  56. template<>
  57. struct is_bool<bool> { bool static value(){return true;}};
  58. class type_base {
  59. type_base& operator=(const type_base&);
  60. public:
  61. const std::string name;
  62. const std::string description;
  63. type_base (std::string name, std::string description) : name(name), description(description) {}
  64. virtual void parse_and_store (const std::string & s)=0;
  65. virtual std::string value() const =0;
  66. virtual std::auto_ptr<type_base> clone()const =0;
  67. virtual ~type_base(){}
  68. };
  69. template <typename type>
  70. class type_impl : public type_base {
  71. private:
  72. type_impl& operator=(const type_impl&);
  73. typedef bool(*validating_function_type)(const type&);
  74. private:
  75. type & target;
  76. validating_function_type validating_function;
  77. public:
  78. type_impl(std::string name, std::string description, type & target, validating_function_type validating_function = NULL)
  79. : type_base (name,description), target(target),validating_function(validating_function)
  80. {};
  81. void parse_and_store (const std::string & s){
  82. try{
  83. const bool is_bool = internal::is_bool<type>::value();
  84. if (is_bool &&(s.empty())){
  85. //to avoid directly assigning true
  86. //(as it will impose additional layer of indirection)
  87. //so, simply pass it as string
  88. internal::string_to("1",target);
  89. }else {
  90. internal::string_to(s,target);
  91. }
  92. }catch(std::invalid_argument& ){
  93. std::stringstream str;
  94. str
  95. <<"\""<<s<<"\""<<" is incorrect input for argument "
  96. <<"\""<<name<<"\""
  97. ;
  98. throw std::invalid_argument(str.str());
  99. }
  100. if (validating_function){
  101. if (!((validating_function)(target))){
  102. std::stringstream str;
  103. str
  104. <<"\""<<target<<"\" is invalid value for argument "
  105. <<"\""<<name<<"\""
  106. ;
  107. throw std::invalid_argument(str.str());
  108. }
  109. }
  110. }
  111. virtual std::string value()const{
  112. std::stringstream str;
  113. str<<target;
  114. return str.str();
  115. }
  116. virtual std::auto_ptr<type_base> clone()const {
  117. return std::auto_ptr<type_base>(new type_impl(*this));
  118. }
  119. };
  120. class argument{
  121. private:
  122. std::auto_ptr<type_base> p_type;
  123. bool matched_;
  124. public:
  125. argument(argument const& other): p_type(other.p_type.get() ? other.p_type->clone():std::auto_ptr<type_base>()),matched_(other.matched_){}
  126. argument& operator=(argument a){
  127. this->swap(a);
  128. return *this;
  129. }
  130. void swap(argument& other){
  131. std::auto_ptr<type_base> tmp; tmp=p_type; p_type=other.p_type; other.p_type=tmp;
  132. std::swap(matched_,other.matched_);
  133. }
  134. template<class type>
  135. argument(std::string name, std::string description, type& dest, bool(*validating_function)(const type&)= NULL)
  136. :p_type(new type_impl<type>(name,description,dest,validating_function))
  137. ,matched_(false)
  138. {}
  139. std::string value()const{
  140. return p_type->value();
  141. }
  142. std::string name()const{
  143. return p_type->name;
  144. }
  145. std::string description() const{
  146. return p_type->description;
  147. }
  148. void parse_and_store(const std::string & s){
  149. p_type->parse_and_store(s);
  150. matched_=true;
  151. }
  152. bool is_matched() const{return matched_;}
  153. };
  154. }
  155. class cli_argument_pack{
  156. typedef std::map<std::string,internal::argument> args_map_type;
  157. typedef std::vector<std::string> args_display_order_type;
  158. typedef std::vector<std::string> positional_arg_names_type;
  159. private:
  160. args_map_type args_map;
  161. args_display_order_type args_display_order;
  162. positional_arg_names_type positional_arg_names;
  163. std::set<std::string> bool_args_names;
  164. private:
  165. void add_arg(internal::argument const& a){
  166. std::pair<args_map_type::iterator, bool> result = args_map.insert(std::make_pair(a.name(),a));
  167. if (!result.second){
  168. throw std::invalid_argument("argument with name: \""+a.name()+"\" already registered");
  169. }
  170. args_display_order.push_back(a.name());
  171. }
  172. public:
  173. template<typename type>
  174. cli_argument_pack& arg(type& dest,std::string const& name, std::string const& description, bool(*validate)(const type &)= NULL){
  175. internal::argument a(name,description,dest,validate);
  176. add_arg(a);
  177. if (internal::is_bool<type>::value()){
  178. bool_args_names.insert(name);
  179. }
  180. return *this;
  181. }
  182. //Positional means that argument name can be omitted in actual CL
  183. //only key to match values for parameters with
  184. template<typename type>
  185. cli_argument_pack& positional_arg(type& dest,std::string const& name, std::string const& description, bool(*validate)(const type &)= NULL){
  186. internal::argument a(name,description,dest,validate);
  187. add_arg(a);
  188. if (internal::is_bool<type>::value()){
  189. bool_args_names.insert(name);
  190. }
  191. positional_arg_names.push_back(name);
  192. return *this;
  193. }
  194. void parse(int argc, char const* argv[]){
  195. {
  196. std::size_t current_positional_index=0;
  197. for (int j=1;j<argc;j++){
  198. internal::argument* pa = NULL;
  199. std::string argument_value;
  200. const char * const begin=argv[j];
  201. const char * const end=begin+std::strlen(argv[j]);
  202. const char * const assign_sign = std::find(begin,end,'=');
  203. struct throw_unknow_parameter{ static void _(std::string const& location){
  204. throw std::invalid_argument(std::string("unknown parameter starting at:\"")+location+"\"");
  205. }};
  206. //first try to interpret it like parameter=value string
  207. if (assign_sign!=end){
  208. std::string name_found = std::string(begin,assign_sign);
  209. args_map_type::iterator it = args_map.find(name_found );
  210. if(it!=args_map.end()){
  211. pa= &((*it).second);
  212. argument_value = std::string(assign_sign+1,end);
  213. }else {
  214. throw_unknow_parameter::_(argv[j]);
  215. }
  216. }
  217. //then see is it a named flag
  218. else{
  219. args_map_type::iterator it = args_map.find(argv[j] );
  220. if(it!=args_map.end()){
  221. pa= &((*it).second);
  222. argument_value = "";
  223. }
  224. //then try it as positional argument without name specified
  225. else if (current_positional_index < positional_arg_names.size()){
  226. std::stringstream str(argv[j]);
  227. args_map_type::iterator it = args_map.find(positional_arg_names.at(current_positional_index));
  228. //TODO: probably use of smarter assert would help here
  229. assert(it!=args_map.end()/*&&"positional_arg_names and args_map are out of sync"*/);
  230. if (it==args_map.end()){
  231. throw std::logic_error("positional_arg_names and args_map are out of sync");
  232. }
  233. pa= &((*it).second);
  234. argument_value = argv[j];
  235. current_positional_index++;
  236. }else {
  237. //TODO: add tc to check
  238. throw_unknow_parameter::_(argv[j]);
  239. }
  240. }
  241. assert(pa);
  242. if (pa->is_matched()){
  243. throw std::invalid_argument(std::string("several values specified for: \"")+pa->name()+"\" argument");
  244. }
  245. pa->parse_and_store(argument_value);
  246. }
  247. }
  248. }
  249. std::string usage_string(const std::string& binary_name)const{
  250. std::string command_line_params;
  251. std::string summary_description;
  252. for (args_display_order_type::const_iterator it = args_display_order.begin();it!=args_display_order.end();++it){
  253. const bool is_bool = (0!=bool_args_names.count((*it)));
  254. args_map_type::const_iterator argument_it = args_map.find(*it);
  255. //TODO: probably use of smarter assert would help here
  256. assert(argument_it!=args_map.end()/*&&"args_display_order and args_map are out of sync"*/);
  257. if (argument_it==args_map.end()){
  258. throw std::logic_error("args_display_order and args_map are out of sync");
  259. }
  260. const internal::argument & a = (*argument_it).second;
  261. command_line_params +=" [" + a.name() + (is_bool ?"":"=value")+ "]";
  262. summary_description +=" " + a.name() + " - " + a.description() +" ("+a.value() +")" + "\n";
  263. }
  264. std::string positional_arg_cl;
  265. for (positional_arg_names_type::const_iterator it = positional_arg_names.begin();it!=positional_arg_names.end();++it){
  266. positional_arg_cl +=" ["+(*it);
  267. }
  268. for (std::size_t i=0;i<positional_arg_names.size();++i){
  269. positional_arg_cl+="]";
  270. }
  271. command_line_params+=positional_arg_cl;
  272. std::stringstream str;
  273. using std::endl;
  274. str << " Program usage is:" << endl
  275. << " " << binary_name << command_line_params
  276. << endl << endl
  277. << " where:" << endl
  278. << summary_description
  279. ;
  280. return str.str();
  281. }
  282. };
  283. }
  284. namespace utility{
  285. namespace internal {
  286. int step_function_plus(int previous,double step){
  287. return static_cast<int>(previous+step);
  288. }
  289. int step_function_multiply(int previous,double multiply){
  290. return static_cast<int>(previous*multiply);
  291. }
  292. typedef int (* step_function_ptr_type)(int,double);
  293. struct step_function_descriptor {
  294. char mnemonic;
  295. step_function_ptr_type function;
  296. public:
  297. step_function_descriptor(char a_mnemonic, step_function_ptr_type a_function) : mnemonic(a_mnemonic), function(a_function) {}
  298. private:
  299. void operator=(step_function_descriptor const&);
  300. };
  301. step_function_descriptor step_function_descriptors[] = {
  302. step_function_descriptor('*',step_function_multiply),
  303. step_function_descriptor('+',step_function_plus)
  304. };
  305. template<typename T, size_t N>
  306. inline size_t array_length(const T(&)[N])
  307. {
  308. return N;
  309. }
  310. struct thread_range_step {
  311. step_function_ptr_type step_function;
  312. double step_function_argument;
  313. thread_range_step ( step_function_ptr_type step_function_, double step_function_argument_)
  314. :step_function(step_function_),step_function_argument(step_function_argument_)
  315. {
  316. if (!step_function_)
  317. throw std::invalid_argument("step_function for thread range step should not be NULL");
  318. }
  319. int operator()(int previous)const {
  320. return step_function(previous,step_function_argument);
  321. }
  322. friend std::istream& operator>>(std::istream& input_stream, thread_range_step& step){
  323. char function_char;
  324. double function_argument;
  325. input_stream>>function_char >> function_argument;
  326. size_t i = 0;
  327. for ( ;(i < array_length(step_function_descriptors)) && step_function_descriptors[i].mnemonic != function_char; ++i );
  328. if (i >= array_length(step_function_descriptors)){
  329. throw std::invalid_argument("step_function for thread range step should be known");
  330. }
  331. step.step_function = step_function_descriptors[i].function;
  332. step.step_function_argument = function_argument;
  333. return input_stream;
  334. }
  335. };
  336. }
  337. struct thread_number_range{
  338. int (*auto_number_of_threads)();
  339. int first;
  340. int last;
  341. internal::thread_range_step step;
  342. thread_number_range( int (*auto_number_of_threads_)(),int low_=1, int high_=-1
  343. , internal::thread_range_step step_ = internal::thread_range_step(internal::step_function_plus,1)
  344. )
  345. : auto_number_of_threads(auto_number_of_threads_), first(low_), last((high_>-1) ? high_ : auto_number_of_threads_())
  346. ,step(step_)
  347. {
  348. if (first>last){
  349. throw std::invalid_argument("");
  350. }
  351. }
  352. friend std::istream& operator>>(std::istream& i, thread_number_range& range){
  353. try{
  354. std::string s;
  355. i>>s;
  356. struct string_to_number_of_threads{
  357. int auto_value;
  358. string_to_number_of_threads(int auto_value_):auto_value(auto_value_){}
  359. int operator()(const std::string & value)const{
  360. int result=0;
  361. if (value=="auto"){
  362. result = auto_value;
  363. }
  364. else{
  365. internal::string_to(value,result);
  366. }
  367. return result;
  368. }
  369. };
  370. string_to_number_of_threads string_to_number_of_threads(range.auto_number_of_threads());
  371. int low =0;
  372. int high=0;
  373. std::size_t semicolon = s.find(':');
  374. if (semicolon == std::string::npos ){
  375. high= (low = string_to_number_of_threads(s));
  376. }else {
  377. //it is a range
  378. std::size_t second_semicolon = s.find(':',semicolon+1);
  379. low = string_to_number_of_threads(std::string(s, 0, semicolon)); //not copying the ':' char
  380. high = string_to_number_of_threads(std::string(s, semicolon+1, second_semicolon - (semicolon+1))); //not copying the ':' chars
  381. if (second_semicolon != std::string::npos){
  382. internal::string_to(std::string(s,second_semicolon + 1),range.step);
  383. }
  384. }
  385. range = thread_number_range(range.auto_number_of_threads,low,high,range.step);
  386. }catch(std::invalid_argument&){
  387. i.setstate(std::ios::failbit);
  388. }
  389. return i;
  390. }
  391. friend std::ostream& operator<<(std::ostream& o, thread_number_range const& range){
  392. using namespace internal;
  393. size_t i = 0;
  394. for ( ;(i < array_length(step_function_descriptors)) && step_function_descriptors[i].function != range.step.step_function; ++i );
  395. if (i >= array_length(step_function_descriptors)){
  396. throw std::invalid_argument("step_function for thread range step should be known");
  397. }
  398. o<<range.first<<":"<<range.last<<":"<<step_function_descriptors[i].mnemonic<<range.step.step_function_argument;
  399. return o;
  400. }
  401. };
  402. //TODO: update the thread range description in the .html files
  403. static const char* thread_number_range_desc="number of threads to use; a range of the form low[:high[:(+|*)step]]"
  404. ", where low and optional high are non-negative integers or 'auto' for the TBB default"
  405. ", and optional step expression specifies how next thread number is chosen."
  406. " E.g. expression '1:16:*2' means threads from 1 to 16 , where each next thread number"
  407. " is twice bigger (*2) than previous one."
  408. ;
  409. }
  410. #include <iostream>
  411. namespace utility{
  412. inline void report_elapsed_time(double seconds){
  413. std::cout << "elapsed time : "<<seconds<<" seconds \n";
  414. }
  415. }
  416. #include <cstdlib>
  417. namespace utility{
  418. inline void parse_cli_arguments(int argc, const char* argv[], utility::cli_argument_pack cli_pack){
  419. bool show_help = false;
  420. cli_pack.arg(show_help,"-h","show this message");
  421. bool invalid_input=false;
  422. try {
  423. cli_pack.parse(argc,argv);
  424. }catch(std::exception& e){
  425. std::cerr
  426. <<"error occurred while parsing command line."<<std::endl
  427. <<"error text:\""<<e.what()<<"\""<<std::endl
  428. <<std::flush;
  429. invalid_input =true;
  430. }
  431. if (show_help || invalid_input){
  432. std::cout<<cli_pack.usage_string(argv[0])<<std::flush;
  433. std::exit(0);
  434. }
  435. }
  436. inline void parse_cli_arguments(int argc, char* argv[], utility::cli_argument_pack cli_pack){
  437. parse_cli_arguments(argc, const_cast<const char**>(argv), cli_pack);
  438. }
  439. }
  440. #endif /* UTILITY_H_ */