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
475 lines
20 KiB
/*
|
|
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 <string>
|
|
#include <cstring>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <set>
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include <numeric>
|
|
#include <stdexcept>
|
|
//TODO: for C++11 mode replace usage of auto_ptr with unique_ptr
|
|
#include <memory>
|
|
#include <cassert>
|
|
|
|
namespace utility{
|
|
namespace internal{
|
|
//TODO: add tcs
|
|
template<class dest_type>
|
|
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<class dest_type>
|
|
dest_type string_to(std::string const& s){
|
|
dest_type result;
|
|
return string_to(s,result);
|
|
}
|
|
|
|
|
|
template<typename>
|
|
struct is_bool { bool static value(){return false;}};
|
|
template<>
|
|
struct is_bool<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<type_base> clone()const =0;
|
|
virtual ~type_base(){}
|
|
};
|
|
template <typename type>
|
|
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<type>::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
|
|
<<"\""<<s<<"\""<<" is incorrect input for argument "
|
|
<<"\""<<name<<"\""
|
|
;
|
|
throw std::invalid_argument(str.str());
|
|
}
|
|
if (validating_function){
|
|
if (!((validating_function)(target))){
|
|
std::stringstream str;
|
|
str
|
|
<<"\""<<target<<"\" is invalid value for argument "
|
|
<<"\""<<name<<"\""
|
|
;
|
|
throw std::invalid_argument(str.str());
|
|
}
|
|
}
|
|
}
|
|
virtual std::string value()const{
|
|
std::stringstream str;
|
|
str<<target;
|
|
return str.str();
|
|
}
|
|
virtual std::auto_ptr<type_base> clone()const {
|
|
return std::auto_ptr<type_base>(new type_impl(*this));
|
|
}
|
|
};
|
|
|
|
class argument{
|
|
private:
|
|
std::auto_ptr<type_base> p_type;
|
|
bool matched_;
|
|
public:
|
|
argument(argument const& other): p_type(other.p_type.get() ? other.p_type->clone():std::auto_ptr<type_base>()),matched_(other.matched_){}
|
|
argument& operator=(argument a){
|
|
this->swap(a);
|
|
return *this;
|
|
}
|
|
void swap(argument& other){
|
|
std::auto_ptr<type_base> tmp; tmp=p_type; p_type=other.p_type; other.p_type=tmp;
|
|
std::swap(matched_,other.matched_);
|
|
}
|
|
template<class type>
|
|
argument(std::string name, std::string description, type& dest, bool(*validating_function)(const type&)= NULL)
|
|
:p_type(new type_impl<type>(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<std::string,internal::argument> args_map_type;
|
|
typedef std::vector<std::string> args_display_order_type;
|
|
typedef std::vector<std::string> 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<std::string> bool_args_names;
|
|
private:
|
|
void add_arg(internal::argument const& a){
|
|
std::pair<args_map_type::iterator, bool> 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<typename type>
|
|
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<type>::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<typename type>
|
|
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<type>::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;j<argc;j++){
|
|
internal::argument* pa = NULL;
|
|
std::string argument_value;
|
|
|
|
const char * const begin=argv[j];
|
|
const char * const end=begin+std::strlen(argv[j]);
|
|
|
|
const char * const assign_sign = std::find(begin,end,'=');
|
|
|
|
struct throw_unknow_parameter{ static void _(std::string const& location){
|
|
throw std::invalid_argument(std::string("unknown parameter starting at:\"")+location+"\"");
|
|
}};
|
|
//first try to interpret it like parameter=value string
|
|
if (assign_sign!=end){
|
|
std::string name_found = std::string(begin,assign_sign);
|
|
args_map_type::iterator it = args_map.find(name_found );
|
|
|
|
if(it!=args_map.end()){
|
|
pa= &((*it).second);
|
|
argument_value = std::string(assign_sign+1,end);
|
|
}else {
|
|
throw_unknow_parameter::_(argv[j]);
|
|
}
|
|
}
|
|
//then see is it a named flag
|
|
else{
|
|
args_map_type::iterator it = args_map.find(argv[j] );
|
|
if(it!=args_map.end()){
|
|
pa= &((*it).second);
|
|
argument_value = "";
|
|
}
|
|
//then try it as positional argument without name specified
|
|
else if (current_positional_index < positional_arg_names.size()){
|
|
std::stringstream str(argv[j]);
|
|
args_map_type::iterator it = args_map.find(positional_arg_names.at(current_positional_index));
|
|
//TODO: probably use of smarter assert would help here
|
|
assert(it!=args_map.end()/*&&"positional_arg_names and args_map are out of sync"*/);
|
|
if (it==args_map.end()){
|
|
throw std::logic_error("positional_arg_names and args_map are out of sync");
|
|
}
|
|
pa= &((*it).second);
|
|
argument_value = argv[j];
|
|
|
|
current_positional_index++;
|
|
}else {
|
|
//TODO: add tc to check
|
|
throw_unknow_parameter::_(argv[j]);
|
|
}
|
|
}
|
|
assert(pa);
|
|
if (pa->is_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<positional_arg_names.size();++i){
|
|
positional_arg_cl+="]";
|
|
}
|
|
command_line_params+=positional_arg_cl;
|
|
std::stringstream str;
|
|
using std::endl;
|
|
str << " Program usage is:" << endl
|
|
<< " " << binary_name << command_line_params
|
|
<< endl << endl
|
|
<< " where:" << endl
|
|
<< summary_description
|
|
;
|
|
return str.str();
|
|
}
|
|
};
|
|
}
|
|
|
|
namespace utility{
|
|
|
|
namespace internal {
|
|
int step_function_plus(int previous,double step){
|
|
return static_cast<int>(previous+step);
|
|
}
|
|
int step_function_multiply(int previous,double multiply){
|
|
return static_cast<int>(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<typename T, size_t N>
|
|
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<<range.first<<":"<<range.last<<":"<<step_function_descriptors[i].mnemonic<<range.step.step_function_argument;
|
|
return o;
|
|
}
|
|
|
|
};
|
|
//TODO: update the thread range description in the .html files
|
|
static const char* thread_number_range_desc="number of threads to use; a range of the form low[:high[:(+|*)step]]"
|
|
", where low and optional high are non-negative integers or 'auto' for the TBB default"
|
|
", and optional step expression specifies how next thread number is chosen."
|
|
" E.g. expression '1:16:*2' means threads from 1 to 16 , where each next thread number"
|
|
" is twice bigger (*2) than previous one."
|
|
;
|
|
}
|
|
#include <iostream>
|
|
namespace utility{
|
|
inline void report_elapsed_time(double seconds){
|
|
std::cout << "elapsed time : "<<seconds<<" seconds \n";
|
|
}
|
|
}
|
|
#include <cstdlib>
|
|
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."<<std::endl
|
|
<<"error text:\""<<e.what()<<"\""<<std::endl
|
|
<<std::flush;
|
|
invalid_input =true;
|
|
}
|
|
if (show_help || invalid_input){
|
|
std::cout<<cli_pack.usage_string(argv[0])<<std::flush;
|
|
std::exit(0);
|
|
}
|
|
|
|
}
|
|
inline void parse_cli_arguments(int argc, char* argv[], utility::cli_argument_pack cli_pack){
|
|
parse_cli_arguments(argc, const_cast<const char**>(argv), cli_pack);
|
|
}
|
|
}
|
|
#endif /* UTILITY_H_ */
|