Commit 0598f33a authored by Vadim Pisarevsky's avatar Vadim Pisarevsky

refactored command line parser, fixed the docs

parent b16b50d6
...@@ -58,15 +58,15 @@ The sample below demonstrates how to use CommandLineParser: ...@@ -58,15 +58,15 @@ The sample below demonstrates how to use CommandLineParser:
} }
int N = parser.get<int>("N"); int N = parser.get<int>("N");
double fps = parser.get<double>parser("fps"); double fps = parser.get<double>("fps");
std::string path = parser.get<std::string>("path"); std::string path = parser.get<std::string>("path");
use_time_stamp = parserer.has("timestamp"); use_time_stamp = parser.has("timestamp");
std::string img1 = parser.get<string>(1); std::string img1 = parser.get<string>(0);
std::string img2 = parser.get<string>(2); std::string img2 = parser.get<string>(1);
int repeat = parser.get<int>(3); int repeat = parser.get<int>(2);
if (!parser.check()) if (!parser.check())
{ {
......
...@@ -221,7 +221,7 @@ CV_EXPORTS void setNumThreads(int nthreads); ...@@ -221,7 +221,7 @@ CV_EXPORTS void setNumThreads(int nthreads);
CV_EXPORTS int getNumThreads(); CV_EXPORTS int getNumThreads();
CV_EXPORTS int getThreadNum(); CV_EXPORTS int getThreadNum();
CV_EXPORTS_W const std::string& getBuildInformation(); CV_EXPORTS_W const string& getBuildInformation();
//! Returns the number of ticks. //! Returns the number of ticks.
...@@ -4434,7 +4434,7 @@ protected: ...@@ -4434,7 +4434,7 @@ protected:
struct CV_EXPORTS Param struct CV_EXPORTS Param
{ {
enum { INT=0, BOOLEAN=1, REAL=2, STRING=3, MAT=4, MAT_VECTOR=5, ALGORITHM=6 }; enum { INT=0, BOOLEAN=1, REAL=2, STRING=3, MAT=4, MAT_VECTOR=5, ALGORITHM=6, FLOAT=7, UNSIGNED_INT=8, UINT64=9 };
Param(); Param();
Param(int _type, bool _readonly, int _offset, Param(int _type, bool _readonly, int _offset,
...@@ -4505,142 +4505,73 @@ template<> struct ParamType<Algorithm> ...@@ -4505,142 +4505,73 @@ template<> struct ParamType<Algorithm>
enum { type = Param::ALGORITHM }; enum { type = Param::ALGORITHM };
}; };
// The CommandLineParser class is designed for command line arguments parsing template<> struct ParamType<float>
class CV_EXPORTS CommandLineParserParams
{ {
public: typedef float const_param_type;
std::string help_message; typedef float member_type;
std::string def_value;
std::vector<std::string> keys; enum { type = Param::FLOAT };
int number;
}; };
template <typename T> template<> struct ParamType<unsigned>
std::string get_type_name() { return "UNKNOW"; }
bool cmp_params(const CommandLineParserParams & p1, const CommandLineParserParams & p2);
template<typename T>
T from_str(const std::string & str)
{ {
T value; typedef unsigned const_param_type;
std::stringstream ss(str); typedef unsigned member_type;
ss >> value;
enum { type = Param::UNSIGNED_INT };
if (ss.fail()) };
{
std::string err_msg =
std::string("can not convert: [")
+ str
+ std::string("] to [")
+ get_type_name<T>()
+ std::string("]");
CV_Error(CV_StsBadArg, err_msg);
}
return value; template<> struct ParamType<uint64>
} {
typedef uint64 const_param_type;
typedef uint64 member_type;
enum { type = Param::UINT64 };
};
template<> std::string from_str(const std::string & str);
template<typename T> // The CommandLineParser class is designed for command line arguments parsing
std::string to_str(T value)
{
std::ostringstream os;
os << value;
return os.str();
}
class CV_EXPORTS CommandLineParser class CV_EXPORTS CommandLineParser
{ {
public: public:
CommandLineParser(int argc, const char * const argv[], const std::string keys); CommandLineParser(int argc, const char* const argv[], const string& keys);
CommandLineParser(const CommandLineParser& parser);
std::string getPathToApplication(); CommandLineParser& operator = (const CommandLineParser& parser);
template <typename T>
T get(const std::string& name, bool space_delete = true)
{
try
{
for (size_t i = 0; i < data.size(); i++)
{
for (size_t j = 0; j < data[i].keys.size(); j++)
{
if (name.compare(data[i].keys[j]) == 0)
{
std::string v = data[i].def_value;
if (space_delete == true) v = cat_string(v);
return from_str<T>(v);
}
}
}
error = true;
error_message += "Unknown parametes " + name + "\n";
}
catch (std::exception& e)
{
error = true;
error_message += "Exception: " + std::string(e.what()) + "\n";
}
return T();
}
template <typename T>
T get(int index, bool space_delete = true)
{
try
{
for (size_t i = 0; i < data.size(); i++)
{
if (data[i].number == index - 1)
{
std::string v = data[i].def_value;
if (space_delete == true) v = cat_string(v);
return from_str<T>(v);
}
}
error = true;
error_message += "Unknown parametes #" + to_str<int>(index) + "\n";
}
catch(std::exception & e)
{
error = true;
error_message += "Exception: " + std::string(e.what()) + "\n";
}
return T();
}
bool has(const std::string& name);
bool check();
void about(std::string message);
void printMessage();
void printErrors();
protected:
bool error;
std::string error_message;
std::string about_message;
std::string path_to_app;
std::string app_name;
std::vector<CommandLineParserParams> data;
std::vector<std::string> split_range_string(std::string str, char fs, char ss); string getPathToApplication() const;
std::vector<std::string> split_string(std::string str, char symbol = ' ', bool create_empty_item = false);
std::string cat_string(std::string str);
void apply_params(std::string key, std::string value); template <typename T>
void apply_params(int i, std::string value); T get(const string& name, bool space_delete = true) const
{
T val = T();
getByName(name, space_delete, ParamType<T>::type, (void*)&val);
return val;
}
void sort_params(); template <typename T>
T get(int index, bool space_delete = true) const
{
T val = T();
getByIndex(index, space_delete, ParamType<T>::type, (void*)&val);
return val;
}
bool has(const string& name) const;
bool check() const;
void about(const string& message);
void printMessage() const;
void printErrors() const;
protected:
void getByName(const string& name, bool space_delete, int type, void* dst) const;
void getByIndex(int index, bool space_delete, int type, void* dst) const;
struct Impl;
Impl* impl;
}; };
/////////////////////////////// Parallel Primitives ////////////////////////////////// /////////////////////////////// Parallel Primitives //////////////////////////////////
......
...@@ -5,402 +5,501 @@ ...@@ -5,402 +5,501 @@
namespace cv namespace cv
{ {
bool cmp_params(const CommandLineParserParams & p1, const CommandLineParserParams & p2)
struct CommandLineParserParams
{
public:
string help_message;
string def_value;
vector<string> keys;
int number;
};
struct CommandLineParser::Impl
{
bool error;
string error_message;
string about_message;
string path_to_app;
string app_name;
vector<CommandLineParserParams> data;
vector<string> split_range_string(const string& str, char fs, char ss) const;
vector<string> split_string(const string& str, char symbol = ' ', bool create_empty_item = false) const;
string cat_string(const string& str) const;
void apply_params(const string& key, const string& value);
void apply_params(int i, string value);
void sort_params();
int refcount;
};
static string get_type_name(int type)
{
if( type == Param::INT )
return "int";
if( type == Param::UNSIGNED_INT )
return "unsigned";
if( type == Param::UINT64 )
return "unsigned long long";
if( type == Param::FLOAT )
return "float";
if( type == Param::REAL )
return "double";
if( type == Param::STRING )
return "string";
return "unknown";
}
static void from_str(const string& str, int type, void* dst)
{
std::stringstream ss(str);
if( type == Param::INT )
ss >> *(int*)dst;
else if( type == Param::UNSIGNED_INT )
ss >> *(unsigned*)dst;
else if( type == Param::UINT64 )
ss >> *(uint64*)dst;
else if( type == Param::FLOAT )
ss >> *(float*)dst;
else if( type == Param::REAL )
ss >> *(double*)dst;
else if( type == Param::STRING )
ss >> *(string*)dst;
if (ss.fail())
{ {
if (p1.number > p2.number) string err_msg = "can not convert: [" + str +
return false; + "] to [" + get_type_name(type) + "]";
CV_Error(CV_StsBadArg, err_msg);
}
}
if (p1.number == -1 && p2.number == -1) void CommandLineParser::getByName(const string& name, bool space_delete, int type, void* dst) const
{
try
{
for (size_t i = 0; i < impl->data.size(); i++)
{ {
if (p1.keys[0].compare(p2.keys[0]) > 0) for (size_t j = 0; j < impl->data[i].keys.size(); j++)
{ {
return false; if (name.compare(impl->data[i].keys[j]) == 0)
{
string v = impl->data[i].def_value;
if (space_delete)
v = impl->cat_string(v);
from_str(v, type, dst);
return;
}
} }
} }
impl->error = true;
return true; impl->error_message += "Unknown parametes " + name + "\n";
} }
catch (std::exception& e)
{
impl->error = true;
impl->error_message += "Exception: " + string(e.what()) + "\n";
}
}
CommandLineParser::CommandLineParser(int argc, const char * const argv[], const std::string keys)
void CommandLineParser::getByIndex(int index, bool space_delete, int type, void* dst) const
{
try
{ {
// path to application for (size_t i = 0; i < impl->data.size(); i++)
size_t pos_s = std::string(argv[0]).find_last_of("/\\");
if (pos_s == std::string::npos)
{ {
path_to_app = ""; if (impl->data[i].number == index)
app_name = std::string(argv[0]); {
string v = impl->data[i].def_value;
if (space_delete == true) v = impl->cat_string(v);
from_str(v, type, dst);
return;
}
} }
else impl->error = true;
impl->error_message += "Unknown parametes #" + format("%d", index) + "\n";
}
catch(std::exception & e)
{
impl->error = true;
impl->error_message += "Exception: " + string(e.what()) + "\n";
}
}
static bool cmp_params(const CommandLineParserParams & p1, const CommandLineParserParams & p2)
{
if (p1.number > p2.number)
return false;
if (p1.number == -1 && p2.number == -1)
{
if (p1.keys[0].compare(p2.keys[0]) > 0)
{ {
path_to_app = std::string(argv[0]).substr(0, pos_s); return false;
app_name = std::string(argv[0]).substr(pos_s + 1, std::string(argv[0]).length() - pos_s);
} }
}
error = false; return true;
error_message = ""; }
// parse keys CommandLineParser::CommandLineParser(int argc, const char* const argv[], const string& keys)
std::vector<std::string> k = split_range_string(keys, '{', '}'); {
impl = new Impl;
impl->refcount = 1;
// path to application
size_t pos_s = string(argv[0]).find_last_of("/\\");
if (pos_s == string::npos)
{
impl->path_to_app = "";
impl->app_name = string(argv[0]);
}
else
{
impl->path_to_app = string(argv[0]).substr(0, pos_s);
impl->app_name = string(argv[0]).substr(pos_s + 1, string(argv[0]).length() - pos_s);
}
int jj = 0; impl->error = false;
for (size_t i = 0; i < k.size(); i++) impl->error_message = "";
{
std::vector<std::string> l = split_string(k[i], '|', true);
CommandLineParserParams p;
p.keys = split_string(l[0]);
p.def_value = l[1];
p.help_message = cat_string(l[2]);
p.number = -1;
if (p.keys[0][0] == '@')
{
p.number = jj;
jj++;
}
data.push_back(p); // parse keys
} vector<string> k = impl->split_range_string(keys, '{', '}');
// parse argv int jj = 0;
jj = 0; for (size_t i = 0; i < k.size(); i++)
for (int i = 1; i < argc; i++) {
vector<string> l = impl->split_string(k[i], '|', true);
CommandLineParserParams p;
p.keys = impl->split_string(l[0]);
p.def_value = l[1];
p.help_message = impl->cat_string(l[2]);
p.number = -1;
if (p.keys[0][0] == '@')
{ {
std::string s = std::string(argv[i]); p.number = jj;
jj++;
if (s.find('=') != std::string::npos && s.find('=') < s.length())
{
std::vector<std::string> k_v = split_string(s, '=', true);
for (int h = 0; h < 2; h++)
{
if (k_v[0][0] == '-')
k_v[0] = k_v[0].substr(1, k_v[0].length() -1);
}
apply_params(k_v[0], k_v[1]);
}
else if (s.length() > 1 && s[0] == '-')
{
for (int h = 0; h < 2; h++)
{
if (s[0] == '-')
s = s.substr(1, s.length() - 1);
}
apply_params(s, "true");
}
else if (s[0] != '-')
{
apply_params(jj, s);
jj++;
}
} }
sort_params(); impl->data.push_back(p);
} }
void CommandLineParser::about(std::string message) // parse argv
jj = 0;
for (int i = 1; i < argc; i++)
{ {
about_message = message; string s = string(argv[i]);
}
void CommandLineParser::apply_params(std::string key, std::string value) if (s.find('=') != string::npos && s.find('=') < s.length())
{
for (size_t i = 0; i < data.size(); i++)
{ {
for (size_t k = 0; k < data[i].keys.size(); k++) vector<string> k_v = impl->split_string(s, '=', true);
for (int h = 0; h < 2; h++)
{ {
if (key.compare(data[i].keys[k]) == 0) if (k_v[0][0] == '-')
{ k_v[0] = k_v[0].substr(1, k_v[0].length() -1);
data[i].def_value = value;
break;
}
} }
impl->apply_params(k_v[0], k_v[1]);
} }
} else if (s.length() > 1 && s[0] == '-')
void CommandLineParser::apply_params(int i, std::string value)
{
for (size_t j = 0; j < data.size(); j++)
{ {
if (data[j].number == i) for (int h = 0; h < 2; h++)
{ {
data[j].def_value = value; if (s[0] == '-')
break; s = s.substr(1, s.length() - 1);
} }
impl->apply_params(s, "true");
} }
} else if (s[0] != '-')
void CommandLineParser::sort_params()
{
for (size_t i = 0; i < data.size(); i++)
{ {
sort(data[i].keys.begin(), data[i].keys.end()); impl->apply_params(jj, s);
jj++;
} }
}
sort (data.begin(), data.end(), cmp_params); impl->sort_params();
}
CommandLineParser::CommandLineParser(const CommandLineParser& parser)
{
impl = parser.impl;
CV_XADD(&impl->refcount, 1);
}
CommandLineParser& CommandLineParser::operator = (const CommandLineParser& parser)
{
if( this != &parser )
{
if(CV_XADD(&impl->refcount, -1) == 1)
delete impl;
impl = parser.impl;
CV_XADD(&impl->refcount, 1);
} }
return *this;
}
void CommandLineParser::about(const string& message)
{
impl->about_message = message;
}
std::string CommandLineParser::cat_string(std::string str) void CommandLineParser::Impl::apply_params(const string& key, const string& value)
{
for (size_t i = 0; i < data.size(); i++)
{ {
while (!str.empty() && str[0] == ' ') for (size_t k = 0; k < data[i].keys.size(); k++)
{ {
str = str.substr(1, str.length() - 1); if (key.compare(data[i].keys[k]) == 0)
{
data[i].def_value = value;
break;
}
} }
}
}
while (!str.empty() && str[str.length() - 1] == ' ') void CommandLineParser::Impl::apply_params(int i, string value)
{
for (size_t j = 0; j < data.size(); j++)
{
if (data[j].number == i)
{ {
str = str.substr(0, str.length() - 1); data[j].def_value = value;
break;
} }
return str;
} }
}
std::string CommandLineParser::getPathToApplication() void CommandLineParser::Impl::sort_params()
{
for (size_t i = 0; i < data.size(); i++)
{ {
return path_to_app; sort(data[i].keys.begin(), data[i].keys.end());
} }
bool CommandLineParser::has(const std::string& name) sort (data.begin(), data.end(), cmp_params);
}
string CommandLineParser::Impl::cat_string(const string& str) const
{
int left = 0, right = (int)str.length();
while( left <= right && str[left] == ' ' )
left++;
while( right > left && str[right-1] == ' ' )
right--;
return left >= right ? string("") : str.substr(left, right-left);
}
string CommandLineParser::getPathToApplication() const
{
return impl->path_to_app;
}
bool CommandLineParser::has(const string& name) const
{
for (size_t i = 0; i < impl->data.size(); i++)
{ {
for (size_t i = 0; i < data.size(); i++) for (size_t j = 0; j < impl->data[i].keys.size(); j++)
{ {
for (size_t j = 0; j < data[i].keys.size(); j++) if (name.compare(impl->data[i].keys[j]) == 0 && string("true").compare(impl->data[i].def_value) == 0)
{ {
if (name.compare(data[i].keys[j]) == 0 && std::string("true").compare(data[i].def_value) == 0) return true;
{
return true;
}
} }
} }
return false;
} }
return false;
}
bool CommandLineParser::check() bool CommandLineParser::check() const
{ {
return error == false; return impl->error == false;
} }
void CommandLineParser::printErrors() void CommandLineParser::printErrors() const
{
if (impl->error)
{ {
if (error) std::cout << std::endl << "ERRORS:" << std::endl << impl->error_message << std::endl;
{
std::cout << std::endl << "ERRORS:" << std::endl << error_message << std::endl;
}
} }
}
void CommandLineParser::printMessage() void CommandLineParser::printMessage() const
{ {
if (about_message != "") if (impl->about_message != "")
std::cout << about_message << std::endl; std::cout << impl->about_message << std::endl;
std::cout << "Usage: " << app_name << " [params] "; std::cout << "Usage: " << impl->app_name << " [params] ";
for (size_t i = 0; i < data.size(); i++) for (size_t i = 0; i < impl->data.size(); i++)
{
if (impl->data[i].number > -1)
{ {
if (data[i].number > -1) string name = impl->data[i].keys[0].substr(1, impl->data[i].keys[0].length() - 1);
{ std::cout << name << " ";
std::string name = data[i].keys[0].substr(1, data[i].keys[0].length() - 1);
std::cout << name << " ";
}
} }
}
std::cout << std::endl << std::endl; std::cout << std::endl << std::endl;
for (size_t i = 0; i < data.size(); i++) for (size_t i = 0; i < impl->data.size(); i++)
{
if (impl->data[i].number == -1)
{ {
if (data[i].number == -1) std::cout << "\t";
for (size_t j = 0; j < impl->data[i].keys.size(); j++)
{ {
std::cout << "\t"; string k = impl->data[i].keys[j];
for (size_t j = 0; j < data[i].keys.size(); j++) if (k.length() > 1)
{ {
std::string k = data[i].keys[j]; std::cout << "--";
if (k.length() > 1)
{
std::cout << "--";
}
else
{
std::cout << "-";
}
std::cout << k;
if (j != data[i].keys.size() - 1)
{
std::cout << ", ";
}
} }
std::string dv = cat_string(data[i].def_value); else
if (dv.compare("") != 0)
{ {
std::cout << " (value:" << dv << ")"; std::cout << "-";
} }
std::cout << std::endl << "\t\t" << data[i].help_message << std::endl;
}
}
std::cout << std::endl;
for (size_t i = 0; i < data.size(); i++)
{
if (data[i].number != -1)
{
std::cout << "\t";
std::string k = data[i].keys[0];
k = k.substr(1, k.length() - 1);
std::cout << k; std::cout << k;
std::string dv = cat_string(data[i].def_value); if (j != impl->data[i].keys.size() - 1)
if (dv.compare("") != 0)
{ {
std::cout << " (value:" << dv << ")"; std::cout << ", ";
} }
std::cout << std::endl << "\t\t" << data[i].help_message << std::endl;
} }
string dv = impl->cat_string(impl->data[i].def_value);
if (dv.compare("") != 0)
{
std::cout << " (value:" << dv << ")";
}
std::cout << std::endl << "\t\t" << impl->data[i].help_message << std::endl;
} }
} }
std::cout << std::endl;
std::vector<std::string> CommandLineParser::split_range_string(std::string str, char fs, char ss) for (size_t i = 0; i < impl->data.size(); i++)
{ {
std::vector<std::string> vec; if (impl->data[i].number != -1)
std::string word = "";
bool begin = false;
while (!str.empty())
{ {
if (str[0] == fs) std::cout << "\t";
{ string k = impl->data[i].keys[0];
if (begin == true) k = k.substr(1, k.length() - 1);
{
CV_Error(CV_StsParseError, std::cout << k;
std::string("error in split_range_string(")
+ str
+ std::string(", ")
+ std::string(1, fs)
+ std::string(", ")
+ std::string(1, ss)
+ std::string(")")
);
}
begin = true;
word = "";
str = str.substr(1, str.length() - 1);
}
if (str[0] == ss) string dv = impl->cat_string(impl->data[i].def_value);
if (dv.compare("") != 0)
{ {
if (begin == false) std::cout << " (value:" << dv << ")";
{
CV_Error(CV_StsParseError,
std::string("error in split_range_string(")
+ str
+ std::string(", ")
+ std::string(1, fs)
+ std::string(", ")
+ std::string(1, ss)
+ std::string(")")
);
}
begin = false;
vec.push_back(word);
} }
std::cout << std::endl << "\t\t" << impl->data[i].help_message << std::endl;
}
}
}
vector<string> CommandLineParser::Impl::split_range_string(const string& _str, char fs, char ss) const
{
string str = _str;
vector<string> vec;
string word = "";
bool begin = false;
while (!str.empty())
{
if (str[0] == fs)
{
if (begin == true) if (begin == true)
{ {
word += str[0]; CV_Error(CV_StsParseError,
string("error in split_range_string(")
+ str
+ string(", ")
+ string(1, fs)
+ string(", ")
+ string(1, ss)
+ string(")")
);
} }
begin = true;
word = "";
str = str.substr(1, str.length() - 1); str = str.substr(1, str.length() - 1);
} }
if (begin == true) if (str[0] == ss)
{ {
CV_Error(CV_StsParseError, if (begin == false)
std::string("error in split_range_string(") {
+ str CV_Error(CV_StsParseError,
+ std::string(", ") string("error in split_range_string(")
+ std::string(1, fs) + str
+ std::string(", ") + string(", ")
+ std::string(1, ss) + string(1, fs)
+ std::string(")") + string(", ")
); + string(1, ss)
+ string(")")
);
}
begin = false;
vec.push_back(word);
} }
return vec; if (begin == true)
{
word += str[0];
}
str = str.substr(1, str.length() - 1);
} }
std::vector<std::string> CommandLineParser::split_string(std::string str, char symbol, bool create_empty_item) if (begin == true)
{ {
std::vector<std::string> vec; CV_Error(CV_StsParseError,
std::string word = ""; string("error in split_range_string(")
+ str
+ string(", ")
+ string(1, fs)
+ string(", ")
+ string(1, ss)
+ string(")")
);
}
return vec;
}
while (!str.empty()) vector<string> CommandLineParser::Impl::split_string(const string& _str, char symbol, bool create_empty_item) const
{
string str = _str;
vector<string> vec;
string word = "";
while (!str.empty())
{
if (str[0] == symbol)
{ {
if (str[0] == symbol) if (!word.empty() || create_empty_item)
{
if (!word.empty() || create_empty_item)
{
vec.push_back(word);
word = "";
}
}
else
{ {
word += str[0]; vec.push_back(word);
word = "";
} }
str = str.substr(1, str.length() - 1);
} }
else
if (word != "" || create_empty_item)
{ {
vec.push_back(word); word += str[0];
} }
str = str.substr(1, str.length() - 1);
return vec;
} }
#undef clp_get if (word != "" || create_empty_item)
#define clp_get(T) template<> T CommandLineParser::get<T>(const std::string& name, bool space_delete);
clp_get(int)
clp_get(unsigned int)
clp_get(long)
clp_get(unsigned long)
clp_get(long long)
clp_get(unsigned long long)
clp_get(size_t)
clp_get(float)
clp_get(double)
clp_get(uint64)
clp_get(int64)
clp_get(std::string)
#undef clp_from_str
#define clp_from_str(T) template<> T from_str<T>(const std::string & str);
clp_from_str(int)
clp_from_str(unsigned int)
clp_from_str(long)
clp_from_str(unsigned long)
clp_from_str(long long)
clp_from_str(unsigned long long)
clp_from_str(size_t)
clp_from_str(uint64)
clp_from_str(int64)
clp_from_str(float)
clp_from_str(double)
template<>
std::string from_str(const std::string & str)
{ {
return str; vec.push_back(word);
} }
#undef clp_type_name return vec;
#define clp_type_name(type, name) template<> std::string get_type_name<type>() { return std::string(name);} }
clp_type_name(int, "int")
clp_type_name(unsigned int, "unsigned int")
clp_type_name(long, "long")
clp_type_name(long long, "long long")
clp_type_name(unsigned long long, "unsigned long long")
clp_type_name(size_t, "size_t")
clp_type_name(float, "float")
clp_type_name(double, "double")
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment