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

  1. /*
  2. Copyright 2005-2014 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. #include <memory>
  35. #include <cassert>
  36. #include <iostream>
  37. #include <cstdlib>
  38. // TBB headers should not be used, as some examples may need to be built without TBB.
  39. namespace utility{
  40. namespace internal{
  41. #if ((__GNUC__*100+__GNUC_MINOR__>=404 && __GXX_EXPERIMENTAL_CXX0X__) || _MSC_VER >= 1600) && (!__INTEL_COMPILER || __INTEL_COMPILER >= 1200 )
  42. // std::unique_ptr is available, and compiler can use it
  43. #define smart_ptr std::unique_ptr
  44. using std::swap;
  45. #else
  46. #if __INTEL_COMPILER && __GXX_EXPERIMENTAL_CXX0X__
  47. // std::unique_ptr is unavailable, so suppress std::auto_prt<> deprecation warning
  48. #pragma warning(disable: 1478)
  49. #endif
  50. #define smart_ptr std::auto_ptr
  51. // in some C++ libraries, std::swap does not work with std::auto_ptr
  52. template<typename T>
  53. void swap( std::auto_ptr<T>& ptr1, std::auto_ptr<T>& ptr2 ) {
  54. std::auto_ptr<T> tmp; tmp = ptr2; ptr2 = ptr1; ptr1 = tmp;
  55. }
  56. #endif
  57. //TODO: add tcs
  58. template<class dest_type>
  59. dest_type& string_to(std::string const& s, dest_type& result){
  60. std::stringstream stream(s);
  61. stream>>result;
  62. if ((!stream)||(stream.fail())){
  63. throw std::invalid_argument("error converting string '"+std::string(s)+"'");
  64. }
  65. return result;
  66. }
  67. template<class dest_type>
  68. dest_type string_to(std::string const& s){
  69. dest_type result;
  70. return string_to(s,result);
  71. }
  72. template<typename>
  73. struct is_bool { static bool value(){return false;}};
  74. template<>
  75. struct is_bool<bool> { static bool value(){return true;}};
  76. class type_base {
  77. type_base& operator=(const type_base&);
  78. public:
  79. const std::string name;
  80. const std::string description;
  81. type_base (std::string a_name, std::string a_description) : name(a_name), description(a_description) {}
  82. virtual void parse_and_store (const std::string & s)=0;
  83. virtual std::string value() const =0;
  84. virtual smart_ptr<type_base> clone()const =0;
  85. virtual ~type_base(){}
  86. };
  87. template <typename type>
  88. class type_impl : public type_base {
  89. private:
  90. type_impl& operator=(const type_impl&);
  91. typedef bool(*validating_function_type)(const type&);
  92. private:
  93. type & target;
  94. validating_function_type validating_function;
  95. public:
  96. type_impl(std::string a_name, std::string a_description, type & a_target, validating_function_type a_validating_function = NULL)
  97. : type_base (a_name,a_description), target(a_target),validating_function(a_validating_function)
  98. {};
  99. void parse_and_store (const std::string & s){
  100. try{
  101. const bool is_bool = internal::is_bool<type>::value();
  102. if (is_bool && s.empty()){
  103. //to avoid directly assigning true
  104. //(as it will impose additional layer of indirection)
  105. //so, simply pass it as string
  106. internal::string_to("1",target);
  107. }else {
  108. internal::string_to(s,target);
  109. }
  110. }catch(std::invalid_argument& e){
  111. std::stringstream str;
  112. str <<"'"<<s<<"' is incorrect input for argument '"<<name<<"'"
  113. <<" ("<<e.what()<<")";
  114. throw std::invalid_argument(str.str());
  115. }
  116. if (validating_function){
  117. if (!((validating_function)(target))){
  118. std::stringstream str;
  119. str <<"'"<<target<<"' is invalid value for argument '"<<name<<"'";
  120. throw std::invalid_argument(str.str());
  121. }
  122. }
  123. }
  124. virtual std::string value()const{
  125. std::stringstream str;
  126. str<<target;
  127. return str.str();
  128. }
  129. virtual smart_ptr<type_base> clone() const {
  130. return smart_ptr<type_base>(new type_impl(*this));
  131. }
  132. };
  133. class argument{
  134. private:
  135. smart_ptr<type_base> p_type;
  136. bool matched_;
  137. public:
  138. argument(argument const& other)
  139. : p_type(other.p_type.get() ? (other.p_type->clone()).release() : NULL)
  140. ,matched_(other.matched_)
  141. {}
  142. argument& operator=(argument a){
  143. this->swap(a);
  144. return *this;
  145. }
  146. void swap(argument& other){
  147. internal::swap(p_type, other.p_type);
  148. std::swap(matched_,other.matched_);
  149. }
  150. template<class type>
  151. argument(std::string a_name, std::string a_description, type& dest, bool(*a_validating_function)(const type&)= NULL)
  152. :p_type(new type_impl<type>(a_name,a_description,dest,a_validating_function))
  153. ,matched_(false)
  154. {}
  155. std::string value()const{
  156. return p_type->value();
  157. }
  158. std::string name()const{
  159. return p_type->name;
  160. }
  161. std::string description() const{
  162. return p_type->description;
  163. }
  164. void parse_and_store(const std::string & s){
  165. p_type->parse_and_store(s);
  166. matched_=true;
  167. }
  168. bool is_matched() const{return matched_;}
  169. };
  170. } // namespace internal
  171. class cli_argument_pack{
  172. typedef std::map<std::string,internal::argument> args_map_type;
  173. typedef std::vector<std::string> args_display_order_type;
  174. typedef std::vector<std::string> positional_arg_names_type;
  175. private:
  176. args_map_type args_map;
  177. args_display_order_type args_display_order;
  178. positional_arg_names_type positional_arg_names;
  179. std::set<std::string> bool_args_names;
  180. private:
  181. void add_arg(internal::argument const& a){
  182. std::pair<args_map_type::iterator, bool> result = args_map.insert(std::make_pair(a.name(),a));
  183. if (!result.second){
  184. throw std::invalid_argument("argument with name: '"+a.name()+"' already registered");
  185. }
  186. args_display_order.push_back(a.name());
  187. }
  188. public:
  189. template<typename type>
  190. cli_argument_pack& arg(type& dest,std::string const& name, std::string const& description, bool(*validate)(const type &)= NULL){
  191. internal::argument a(name,description,dest,validate);
  192. add_arg(a);
  193. if (internal::is_bool<type>::value()){
  194. bool_args_names.insert(name);
  195. }
  196. return *this;
  197. }
  198. //Positional means that argument name can be omitted in actual CL
  199. //only key to match values for parameters with
  200. template<typename type>
  201. cli_argument_pack& positional_arg(type& dest,std::string const& name, std::string const& description, bool(*validate)(const type &)= NULL){
  202. internal::argument a(name,description,dest,validate);
  203. add_arg(a);
  204. if (internal::is_bool<type>::value()){
  205. bool_args_names.insert(name);
  206. }
  207. positional_arg_names.push_back(name);
  208. return *this;
  209. }
  210. void parse(std::size_t argc, char const* argv[]){
  211. {
  212. std::size_t current_positional_index=0;
  213. for (std::size_t j=1;j<argc;j++){
  214. internal::argument* pa = NULL;
  215. std::string argument_value;
  216. const char * const begin=argv[j];
  217. const char * const end=begin+std::strlen(argv[j]);
  218. const char * const assign_sign = std::find(begin,end,'=');
  219. struct throw_unknown_parameter{ static void _(std::string const& location){
  220. throw std::invalid_argument(std::string("unknown parameter starting at:'")+location+"'");
  221. }};
  222. //first try to interpret it like parameter=value string
  223. if (assign_sign!=end){
  224. std::string name_found = std::string(begin,assign_sign);
  225. args_map_type::iterator it = args_map.find(name_found );
  226. if(it!=args_map.end()){
  227. pa= &((*it).second);
  228. argument_value = std::string(assign_sign+1,end);
  229. }else {
  230. throw_unknown_parameter::_(argv[j]);
  231. }
  232. }
  233. //then see is it a named flag
  234. else{
  235. args_map_type::iterator it = args_map.find(argv[j] );
  236. if(it!=args_map.end()){
  237. pa= &((*it).second);
  238. argument_value = "";
  239. }
  240. //then try it as positional argument without name specified
  241. else if (current_positional_index < positional_arg_names.size()){
  242. std::stringstream str(argv[j]);
  243. args_map_type::iterator found_positional_arg = args_map.find(positional_arg_names.at(current_positional_index));
  244. //TODO: probably use of smarter assert would help here
  245. assert(found_positional_arg!=args_map.end()/*&&"positional_arg_names and args_map are out of sync"*/);
  246. if (found_positional_arg==args_map.end()){
  247. throw std::logic_error("positional_arg_names and args_map are out of sync");
  248. }
  249. pa= &((*found_positional_arg).second);
  250. argument_value = argv[j];
  251. current_positional_index++;
  252. }else {
  253. //TODO: add tc to check
  254. throw_unknown_parameter::_(argv[j]);
  255. }
  256. }
  257. assert(pa);
  258. if (pa->is_matched()){
  259. throw std::invalid_argument(std::string("several values specified for: '")+pa->name()+"' argument");
  260. }
  261. pa->parse_and_store(argument_value);
  262. }
  263. }
  264. }
  265. std::string usage_string(const std::string& binary_name)const{
  266. std::string command_line_params;
  267. std::string summary_description;
  268. for (args_display_order_type::const_iterator it = args_display_order.begin();it!=args_display_order.end();++it){
  269. const bool is_bool = (0!=bool_args_names.count((*it)));
  270. args_map_type::const_iterator argument_it = args_map.find(*it);
  271. //TODO: probably use of smarter assert would help here
  272. assert(argument_it!=args_map.end()/*&&"args_display_order and args_map are out of sync"*/);
  273. if (argument_it==args_map.end()){
  274. throw std::logic_error("args_display_order and args_map are out of sync");
  275. }
  276. const internal::argument & a = (*argument_it).second;
  277. command_line_params +=" [" + a.name() + (is_bool ?"":"=value")+ "]";
  278. summary_description +=" " + a.name() + " - " + a.description() +" ("+a.value() +")" + "\n";
  279. }
  280. std::string positional_arg_cl;
  281. for (positional_arg_names_type::const_iterator it = positional_arg_names.begin();it!=positional_arg_names.end();++it){
  282. positional_arg_cl +=" ["+(*it);
  283. }
  284. for (std::size_t i=0;i<positional_arg_names.size();++i){
  285. positional_arg_cl+="]";
  286. }
  287. command_line_params+=positional_arg_cl;
  288. std::stringstream str;
  289. using std::endl;
  290. str << " Program usage is:" << endl
  291. << " " << binary_name << command_line_params
  292. << endl << endl
  293. << " where:" << endl
  294. << summary_description
  295. ;
  296. return str.str();
  297. }
  298. }; // class cli_argument_pack
  299. namespace internal {
  300. template<typename T>
  301. bool is_power_of_2( T val ) {
  302. size_t intval = size_t(val);
  303. return (intval&(intval-1)) == size_t(0);
  304. }
  305. int step_function_plus(int previous, double step){
  306. return static_cast<int>(previous+step);
  307. }
  308. int step_function_multiply(int previous, double multiply){
  309. return static_cast<int>(previous*multiply);
  310. }
  311. // "Power-of-2 ladder": nsteps is the desired number of steps between any subsequent powers of 2.
  312. // The actual step is the quotient of the nearest smaller power of 2 divided by that number (but at least 1).
  313. // E.g., '1:32:#4' means 1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32
  314. int step_function_power2_ladder(int previous, double nsteps){
  315. int steps = int(nsteps);
  316. assert( is_power_of_2(steps) ); // must be a power of 2
  317. // The actual step is 1 until the value is twice as big as nsteps
  318. if( previous < 2*steps )
  319. return previous+1;
  320. // calculate the previous power of 2
  321. int prev_power2 = previous/2; // start with half the given value
  322. int rshift = 1; // and with the shift of 1;
  323. while( int shifted = prev_power2>>rshift ) { // shift the value right; while the result is non-zero,
  324. prev_power2 |= shifted; // add the bits set in 'shifted';
  325. rshift <<= 1; // double the shift, as twice as many top bits are set;
  326. } // repeat.
  327. ++prev_power2; // all low bits set; now it's just one less than the desired power of 2
  328. assert( is_power_of_2(prev_power2) );
  329. assert( (prev_power2<=previous)&&(2*prev_power2>previous) );
  330. // The actual step value is the previous power of 2 divided by steps
  331. return previous + (prev_power2/steps);
  332. }
  333. typedef int (* step_function_ptr_type)(int,double);
  334. struct step_function_descriptor {
  335. char mnemonic;
  336. step_function_ptr_type function;
  337. public:
  338. step_function_descriptor(char a_mnemonic, step_function_ptr_type a_function) : mnemonic(a_mnemonic), function(a_function) {}
  339. private:
  340. void operator=(step_function_descriptor const&);
  341. };
  342. step_function_descriptor step_function_descriptors[] = {
  343. step_function_descriptor('*',step_function_multiply),
  344. step_function_descriptor('+',step_function_plus),
  345. step_function_descriptor('#',step_function_power2_ladder)
  346. };
  347. template<typename T, size_t N>
  348. inline size_t array_length(const T(&)[N])
  349. {
  350. return N;
  351. }
  352. struct thread_range_step {
  353. step_function_ptr_type step_function;
  354. double step_function_argument;
  355. thread_range_step ( step_function_ptr_type step_function_, double step_function_argument_)
  356. :step_function(step_function_),step_function_argument(step_function_argument_)
  357. {
  358. if (!step_function_)
  359. throw std::invalid_argument("step_function for thread range step should not be NULL");
  360. }
  361. int operator()(int previous)const {
  362. assert(0<=previous); // test 0<=first and loop discipline
  363. const int ret = step_function(previous,step_function_argument);
  364. assert(previous<ret);
  365. return ret;
  366. }
  367. friend std::istream& operator>>(std::istream& input_stream, thread_range_step& step){
  368. char function_char;
  369. double function_argument;
  370. input_stream >> function_char >> function_argument;
  371. size_t i = 0;
  372. while ((i<array_length(step_function_descriptors)) && (step_function_descriptors[i].mnemonic!=function_char)) ++i;
  373. if (i >= array_length(step_function_descriptors)){
  374. throw std::invalid_argument("unknown step function mnemonic: "+std::string(1,function_char));
  375. } else if ((function_char=='#') && !is_power_of_2(function_argument)) {
  376. throw std::invalid_argument("the argument of # should be a power of 2");
  377. }
  378. step.step_function = step_function_descriptors[i].function;
  379. step.step_function_argument = function_argument;
  380. return input_stream;
  381. }
  382. };
  383. } // namespace internal
  384. struct thread_number_range{
  385. int (*auto_number_of_threads)();
  386. int first; // 0<=first (0 can be used as a special value)
  387. int last; // first<=last
  388. internal::thread_range_step step;
  389. thread_number_range( int (*auto_number_of_threads_)(),int low_=1, int high_=-1
  390. , internal::thread_range_step step_ = internal::thread_range_step(internal::step_function_power2_ladder,4)
  391. )
  392. : auto_number_of_threads(auto_number_of_threads_), first(low_), last((high_>-1) ? high_ : auto_number_of_threads_())
  393. ,step(step_)
  394. {
  395. if (first<0) {
  396. throw std::invalid_argument("negative value not allowed");
  397. }
  398. if (first>last) {
  399. throw std::invalid_argument("decreasing sequence not allowed");
  400. }
  401. }
  402. friend std::istream& operator>>(std::istream& i, thread_number_range& range){
  403. try{
  404. std::string s;
  405. i>>s;
  406. struct string_to_number_of_threads{
  407. int auto_value;
  408. string_to_number_of_threads(int auto_value_):auto_value(auto_value_){}
  409. int operator()(const std::string & value)const{
  410. return (value=="auto")? auto_value : internal::string_to<int>(value);
  411. }
  412. };
  413. string_to_number_of_threads string_to_number_of_threads(range.auto_number_of_threads());
  414. int low, high;
  415. std::size_t colon = s.find(':');
  416. if ( colon == std::string::npos ){
  417. low = high = string_to_number_of_threads(s);
  418. } else {
  419. //it is a range
  420. std::size_t second_colon = s.find(':',colon+1);
  421. low = string_to_number_of_threads(std::string(s, 0, colon)); //not copying the colon
  422. high = string_to_number_of_threads(std::string(s, colon+1, second_colon - (colon+1))); //not copying the colons
  423. if (second_colon != std::string::npos){
  424. internal::string_to(std::string(s,second_colon + 1),range.step);
  425. }
  426. }
  427. range = thread_number_range(range.auto_number_of_threads,low,high,range.step);
  428. }catch(std::invalid_argument&){
  429. i.setstate(std::ios::failbit);
  430. throw;
  431. }
  432. return i;
  433. }
  434. friend std::ostream& operator<<(std::ostream& o, thread_number_range const& range){
  435. using namespace internal;
  436. size_t i = 0;
  437. for (; i < array_length(step_function_descriptors) && step_function_descriptors[i].function != range.step.step_function; ++i ) {}
  438. if (i >= array_length(step_function_descriptors)){
  439. throw std::invalid_argument("unknown step function for thread range");
  440. }
  441. o<<range.first<<":"<<range.last<<":"<<step_function_descriptors[i].mnemonic<<range.step.step_function_argument;
  442. return o;
  443. }
  444. }; // struct thread_number_range
  445. //TODO: fix unused warning here
  446. //TODO: update the thread range description in the .html files
  447. static const char* thread_number_range_desc="number of threads to use; a range of the form low[:high[:(+|*|#)step]],"
  448. "\n\twhere low and optional high are non-negative integers or 'auto' for the default choice,"
  449. "\n\tand optional step expression specifies how thread numbers are chosen within the range."
  450. "\n\tSee examples/common/index.html for detailed description."
  451. ;
  452. inline void report_elapsed_time(double seconds){
  453. std::cout << "elapsed time : "<<seconds<<" seconds \n";
  454. }
  455. inline void parse_cli_arguments(int argc, const char* argv[], utility::cli_argument_pack cli_pack){
  456. bool show_help = false;
  457. cli_pack.arg(show_help,"-h","show this message");
  458. bool invalid_input=false;
  459. try {
  460. cli_pack.parse(argc,argv);
  461. }catch(std::exception& e){
  462. std::cerr
  463. <<"error occurred while parsing command line."<<std::endl
  464. <<"error text: "<<e.what()<<std::endl
  465. <<std::flush;
  466. invalid_input =true;
  467. }
  468. if (show_help || invalid_input){
  469. std::cout<<cli_pack.usage_string(argv[0])<<std::flush;
  470. std::exit(0);
  471. }
  472. }
  473. inline void parse_cli_arguments(int argc, char* argv[], utility::cli_argument_pack cli_pack){
  474. parse_cli_arguments(argc, const_cast<const char**>(argv), cli_pack);
  475. }
  476. }
  477. #endif /* UTILITY_H_ */