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