Developer documentation
Version 3.0.3-105-gd3941f44
mrtrix.h
Go to the documentation of this file.
1/* Copyright (c) 2008-2022 the MRtrix3 contributors.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * Covered Software is provided under this License on an "as is"
8 * basis, without warranty of any kind, either expressed, implied, or
9 * statutory, including, without limitation, warranties that the
10 * Covered Software is free of defects, merchantable, fit for a
11 * particular purpose or non-infringing.
12 * See the Mozilla Public License v. 2.0 for more details.
13 *
14 * For more details, see http://www.mrtrix.org/.
15 */
16
17#ifndef __mrtrix_h__
18#define __mrtrix_h__
19
20#include <climits>
21#include <cstdio>
22#include <cstdarg>
23#include <cerrno>
24#include <cassert>
25#include <sstream>
26#include <algorithm>
27#include <cctype>
28#include <iostream>
29#include <array>
30#include <string>
31#include <cstring>
32#include <cstdlib>
33#include <limits>
34#include <iomanip>
35
36
37#include "types.h"
38#include "exception.h"
39
40
41namespace MR
42{
43
45
47 inline std::istream& getline (std::istream& stream, std::string& string)
48 {
49 std::getline (stream, string);
50 if (string.size() > 0)
51 if (string[string.size()-1] == 015)
52 string.resize (string.size()-1);
53 return stream;
54 }
55
56
57
58 template <typename X, typename ReturnType = int>
60 static constexpr int value () { return 0; }
61 };
62
63 template <typename X>
64 struct max_digits<X, typename std::enable_if<std::is_fundamental<X>::value, int>::type> { NOMEMALIGN
65 static constexpr int value () { return std::numeric_limits<X>::max_digits10; }
66 };
67
68 template <typename X>
69 struct max_digits<X, typename std::enable_if<std::is_fundamental<typename X::Scalar>::value, int>::type> { NOMEMALIGN
70 static constexpr int value () { return std::numeric_limits<typename X::Scalar>::max_digits10; }
71 };
72
73 template <typename X>
74 struct max_digits<X, typename std::enable_if<std::is_fundamental<typename X::value_type>::value && !std::is_fundamental<typename X::Scalar>::value, int>::type> { NOMEMALIGN
75 static constexpr int value () { return std::numeric_limits<typename X::value_type>::max_digits10; }
76 };
77
78
79
81 inline std::string& add_line (std::string& original, const std::string& new_line)
82 {
83 return original.size() ? (original += "\n" + new_line) : ( original = new_line );
84 }
85
86
88 inline std::string shorten (const std::string& text, size_t longest = 40, size_t prefix = 10)
89 {
90 if (text.size() > longest)
91 return (text.substr (0,prefix) + "..." + text.substr (text.size()-longest+prefix+3));
92 else
93 return text;
94 }
95
96
98 inline std::string lowercase (const std::string& string)
99 {
100 std::string ret;
101 ret.resize (string.size());
102 transform (string.begin(), string.end(), ret.begin(), tolower);
103 return ret;
104 }
105
107 inline std::string uppercase (const std::string& string)
108 {
109 std::string ret;
110 ret.resize (string.size());
111 transform (string.begin(), string.end(), ret.begin(), toupper);
112 return ret;
113 }
114
115
116 inline std::string printf (const char* format, ...)
117 {
118 size_t len = 0;
119 va_list list1, list2;
120 va_start (list1, format);
121 va_copy (list2, list1);
122 len = vsnprintf (nullptr, 0, format, list1) + 1;
123 va_end (list1);
124 VLA(buf, char, len);
125 vsnprintf (buf, len, format, list2);
126 va_end (list2);
127 return buf;
128 }
129
130
131 inline std::string strip (const std::string& string, const std::string& ws = {" \0\t\r\n", 5}, bool left = true, bool right = true)
132 {
133 std::string::size_type start = (left ? string.find_first_not_of (ws) : 0);
134 if (start == std::string::npos)
135 return "";
136 std::string::size_type end = (right ? string.find_last_not_of (ws) + 1 : std::string::npos);
137 return string.substr (start, end - start);
138 }
139
140
142 inline std::string unquote (const std::string& string)
143 {
144 if (string.size() <= 2)
145 return string;
146 if (!(string.front() == '\"' && string.back() == '\"'))
147 return string;
148 const std::string substring = string.substr(1, string.size()-2);
149 if (std::none_of (substring.begin(), substring.end(), [] (const char& c) { return c == '\"'; }))
150 return substring;
151 return string;
152 }
153
154
155
156 inline void replace (std::string& string, char orig, char final)
157 {
158 for (auto& c: string)
159 if (c == orig) c = final;
160 }
161
162 inline void replace (std::string& str, const std::string& from, const std::string& to)
163 {
164 if (from.empty()) return;
165 size_t start_pos = 0;
166 while ((start_pos = str.find (from, start_pos)) != std::string::npos) {
167 str.replace (start_pos, from.length(), to);
168 start_pos += to.length();
169 }
170 }
171
172
174 const std::string& string,
175 const char* delimiters = " \t\n",
176 bool ignore_empty_fields = false,
177 size_t num = std::numeric_limits<size_t>::max());
178
180 const std::string& string,
181 bool ignore_empty_fields = true,
182 size_t num = std::numeric_limits<size_t>::max())
183 {
184 return split (string, "\n", ignore_empty_fields, num);
185 }
186
187
188
189
190 /*
191 inline int round (default_type x)
192 {
193 return int (x + (x > 0.0 ? 0.5 : -0.5));
194 }
195*/
196
197
198 bool match (const std::string& pattern, const std::string& text, bool ignore_case = false);
199
200
201
203
205 inline size_t char_is_dash (const char* arg)
206 {
207 assert (arg != nullptr);
208 if (arg[0] == '-')
209 return 1;
210 if (arg[0] == '\0' || arg[1] == '\0' || arg[2] == '\0')
211 return 0;
212 const unsigned char* uarg = reinterpret_cast<const unsigned char*> (arg);
213 if (uarg[0] == 0xE2 && uarg[1] == 0x80 && ( uarg[2] >= 0x90 && uarg[2] <= 0x95 ))
214 return 3;
215 if (uarg[0] == 0xEF) {
216 if (uarg[1] == 0xB9 && ( uarg[2] == 0x98 || uarg[2] == 0xA3))
217 return 3;
218 if (uarg[1] == 0xBC && uarg[2] == 0x8D)
219 return 3;
220 }
221 return 0;
222 }
223
225 inline size_t is_dash (const std::string& arg)
226 {
227 size_t nbytes = char_is_dash (arg.c_str());
228 return nbytes != 0 && nbytes == arg.size();
229 }
230
231
233
236 inline bool consume_dash (const char*& arg)
237 {
238 size_t nbytes = char_is_dash (arg);
239 arg += nbytes;
240 return nbytes != 0;
241 }
242
243
244
245
246
247 template <class T> inline std::string str (const T& value, int precision = 0)
248 {
249 std::ostringstream stream;
250 if (precision)
251 stream.precision (precision);
252 else if (max_digits<T>::value())
253 stream.precision (max_digits<T>::value());
254 stream << value;
255 if (stream.fail())
256 throw Exception (std::string("error converting type \"") + typeid(T).name() + "\" value to string");
257 return stream.str();
258 }
259
260 template <class T> inline T to (const std::string& string)
261 {
262 const std::string stripped (strip (string));
263 std::istringstream stream (stripped);
264 T value;
265 stream >> value;
266 if (stream.fail()) {
268 const std::string lstring = lowercase (stripped);
269 if (lstring == "nan")
270 return std::numeric_limits<T>::quiet_NaN();
271 else if (lstring == "-nan")
272 return -std::numeric_limits<T>::quiet_NaN();
273 else if (lstring == "inf")
274 return std::numeric_limits<T>::infinity();
275 else if (lstring == "-inf")
276 return -std::numeric_limits<T>::infinity();
277 }
278 throw Exception ("error converting string \"" + string + "\" to type \"" + typeid(T).name() + "\"");
279 } else if (!stream.eof()) {
280 throw Exception ("incomplete use of string \"" + string + "\" in conversion to type \"" + typeid(T).name() + "\"");
281 }
282 return value;
283 }
284
285 template <> inline bool to<bool> (const std::string& string)
286 {
287 std::string value = lowercase (strip (string));
288 if (value == "true" || value == "yes")
289 return true;
290 if (value == "false" || value == "no")
291 return false;
292 return to<int> (string);
293 }
294
295 template <> inline std::string str<cfloat> (const cfloat& value, int precision)
296 {
297 std::ostringstream stream;
298 if (precision > 0)
299 stream.precision (precision);
300 stream << value.real();
301 if (value.imag())
302 stream << std::showpos << value.imag() << "i";
303 if (stream.fail())
304 throw Exception ("error converting complex float value to string");
305 return stream.str();
306 }
307
308 template <> inline cfloat to<cfloat> (const std::string& string)
309 {
310 if (!string.size())
311 throw Exception ("cannot convert empty string to complex float");
312
313 const std::string stripped = strip (string);
314 vector<cfloat> candidates;
315 for (ssize_t i = -1; i <= ssize_t(stripped.size()); ++i) {
316 std::string first, second;
317 if (i == -1) {
318 first = "0";
319 second = stripped;
320 } else if (i == ssize_t(stripped.size())) {
321 first = stripped;
322 second = "0i";
323 } else {
324 if (!(stripped[i] == '+' || stripped[i] == '-'))
325 continue;
326 first = stripped.substr(0, i);
327 second = stripped.substr(stripped[i] == '-' ? i : i+1);
328 }
329 if (!(second.back() == 'i' || second.back() == 'j'))
330 continue;
331 second.pop_back();
332 if (second.empty() || second == "-" || second == "+")
333 second.push_back ('1');
334 try {
335 candidates.push_back (cfloat (to<float> (first), to<float> (second)));
336 } catch (Exception&) { }
337 }
338
339 if (candidates.empty())
340 throw Exception ("error converting string \"" + string + "\" to complex float (no valid conversion)");
341
342 for (size_t i = 1; i != candidates.size(); ++i) {
343 if (!(candidates[i].real() == candidates[0].real() ||
344 (std::isnan (candidates[i].real()) && std::isnan (candidates[0].real()))))
345 throw Exception ("error converting string \"" + string + "\" to complex float (ambiguity in real component)");
346 if (!(candidates[i].imag() == candidates[0].imag() ||
347 (std::isnan (candidates[i].imag()) && std::isnan (candidates[0].imag()))))
348 throw Exception ("error converting string \"" + string + "\" to complex float (ambiguity in imaginary component)");
349 }
350 return candidates[0];
351 }
352
353 template <> inline std::string str<cdouble> (const cdouble& value, int precision)
354 {
355 std::ostringstream stream;
356 if (precision > 0)
357 stream.precision (precision);
358 stream << value.real();
359 if (value.imag())
360 stream << std::showpos << value.imag() << "i";
361 if (stream.fail())
362 throw Exception ("error converting complex double value to string");
363 return stream.str();
364 }
365
366 template <> inline cdouble to<cdouble> (const std::string& string)
367 {
368 if (!string.size())
369 throw Exception ("cannot convert empty string to complex double");
370
371 const std::string stripped = strip (string);
372 vector<cdouble> candidates;
373 for (ssize_t i = -1; i <= ssize_t(stripped.size()); ++i) {
374 std::string first, second;
375 if (i == -1) {
376 first = "0";
377 second = stripped;
378 } else if (i == ssize_t(stripped.size())) {
379 first = stripped;
380 second = "0i";
381 } else {
382 if (!(stripped[i] == '+' || stripped[i] == '-'))
383 continue;
384 first = stripped.substr(0, i);
385 second = stripped.substr(stripped[i] == '-' ? i : i+1);
386 }
387 if (!(second.back() == 'i' || second.back() == 'j'))
388 continue;
389 second.pop_back();
390 if (second.empty() || second == "-" || second == "+")
391 second.push_back ('1');
392 try {
393 candidates.push_back (cdouble (to<double> (first), to<double> (second)));
394 } catch (Exception&) { }
395 }
396
397 if (candidates.empty())
398 throw Exception ("error converting string \"" + string + "\" to complex double (no valid conversion)");
399
400 for (size_t i = 1; i != candidates.size(); ++i) {
401 if (!(candidates[i].real() == candidates[0].real() ||
402 (std::isnan (candidates[i].real()) && std::isnan (candidates[0].real()))))
403 throw Exception ("error converting string \"" + string + "\" to complex double (ambiguity in real component)");
404 if (!(candidates[i].imag() == candidates[0].imag() ||
405 (std::isnan (candidates[i].imag()) && std::isnan (candidates[0].imag()))))
406 throw Exception ("error converting string \"" + string + "\" to complex double (ambiguity in imaginary component)");
407 }
408 return candidates[0];
409 }
410
411
412
413
414
415
416
417 vector<default_type> parse_floats (const std::string& spec);
418
419
420
421 template <typename IntType>
422 vector<IntType> parse_ints (const std::string& spec, const IntType last = std::numeric_limits<IntType>::max())
423 {
424 typedef typename std::make_signed<IntType>::type SignedIntType;
425 if (!spec.size()) throw Exception ("integer sequence specifier is empty");
426
427 auto to_unsigned = [&] (const SignedIntType value)
428 {
430 throw Exception ("Impermissible negative value present in sequence \"" + spec + "\"");
431 return IntType(value);
432 };
433
435 std::string::size_type start = 0, end;
436 std::array<SignedIntType, 3> num;
437 size_t i = 0;
438 try {
439 do {
440 start = spec.find_first_not_of (" \t", start);
441 if (start == std::string::npos)
442 break;
443 end = spec.find_first_of (" \t,:", start);
444 std::string token (strip (spec.substr (start, end-start)));
445 if (lowercase (token) == "end") {
446 if (last == std::numeric_limits<IntType>::max())
447 throw Exception ("value of \"end\" is not known in number sequence \"" + spec + "\"");
448 num[i] = SignedIntType(last);
449 }
450 else num[i] = to<SignedIntType> (spec.substr (start, end-start));
451
452 end = spec.find_first_not_of (" \t", end);
453 char last_char = end < spec.size() ? spec[end] : '\0';
454 if (last_char == ':') {
455 ++i;
456 ++end;
457 if (i > 2) throw Exception ("invalid number range in number sequence \"" + spec + "\"");
458 } else {
459 if (i) {
460 SignedIntType inc, last;
461 if (i == 2) {
462 inc = num[1];
463 last = num[2];
464 }
465 else {
466 inc = 1;
467 last = num[1];
468 }
469 if (inc * (last - num[0]) < 0)
470 inc = -inc;
471 for (; (inc > 0 ? num[0] <= last : num[0] >= last) ; num[0] += inc)
472 V.push_back (to_unsigned(num[0]));
473 }
474 else V.push_back (to_unsigned(num[0]));
475 i = 0;
476 }
477
478 start = end;
479 if (last_char == ',')
480 ++start;
481 }
482 while (end < spec.size());
483 }
484 catch (Exception& E) {
485 throw Exception (E, "can't parse integer sequence specifier \"" + spec + "\"");
486 }
487
488 return (V);
489 }
490
491
492
493
494
495
496
497
498 inline std::string join (const vector<std::string>& V, const std::string& delimiter)
499 {
500 std::string ret;
501 if (V.empty())
502 return ret;
503 ret = V[0];
504 for (vector<std::string>::const_iterator i = V.begin() +1; i != V.end(); ++i)
505 ret += delimiter + *i;
506 return ret;
507 }
508
509 template <typename T>
510 inline std::string join (const vector<T>& V, const std::string& delimiter)
511 {
512 std::string ret;
513 if (V.empty())
514 return ret;
515 ret = str(V[0]);
516 for (typename vector<T>::const_iterator i = V.begin() +1; i != V.end(); ++i)
517 ret += delimiter + str(*i);
518 return ret;
519 }
520
521 inline std::string join (const char* const* null_terminated_array, const std::string& delimiter)
522 {
523 std::string ret;
524 if (!null_terminated_array)
525 return ret;
526 ret = null_terminated_array[0];
527 for (const char* const* p = null_terminated_array+1; *p; ++p)
528 ret += delimiter + *p;
529 return ret;
530 }
531
532
533
534}
535
536#endif
537
#define VLA(name, type, num)
Definition: types.h:118
VectorType::Scalar value(const VectorType &coefs, typename VectorType::Scalar cos_elevation, typename VectorType::Scalar cos_azimuth, typename VectorType::Scalar sin_azimuth, int lmax)
Definition: SH.h:233
#define NOMEMALIGN
Definition: memory.h:22
char delimiter(const std::string &filename)
Definition: path.h:214
Definition: base.h:24
std::complex< float > cfloat
Definition: types.h:218
void replace(std::string &string, char orig, char final)
Definition: mrtrix.h:156
std::complex< double > cdouble
Definition: types.h:217
cdouble to< cdouble >(const std::string &string)
Definition: mrtrix.h:366
bool consume_dash(const char *&arg)
match current character to a dash or any Unicode character that looks like one
Definition: mrtrix.h:236
vector< default_type > parse_floats(const std::string &spec)
std::string join(const vector< std::string > &V, const std::string &delimiter)
Definition: mrtrix.h:498
std::istream & getline(std::istream &stream, std::string &string)
read a line from the stream
Definition: mrtrix.h:47
std::string unquote(const std::string &string)
Remove quotation marks only if surrounding entire string.
Definition: mrtrix.h:142
std::string uppercase(const std::string &string)
return uppercase version of string
Definition: mrtrix.h:107
bool match(const std::string &pattern, const std::string &text, bool ignore_case=false)
std::string strip(const std::string &string, const std::string &ws={" \0\t\r\n", 5}, bool left=true, bool right=true)
Definition: mrtrix.h:131
cfloat to< cfloat >(const std::string &string)
Definition: mrtrix.h:308
bool to< bool >(const std::string &string)
Definition: mrtrix.h:285
std::string str< cdouble >(const cdouble &value, int precision)
Definition: mrtrix.h:353
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
size_t char_is_dash(const char *arg)
match a dash or any Unicode character that looks like one
Definition: mrtrix.h:205
std::string lowercase(const std::string &string)
return lowercase version of string
Definition: mrtrix.h:98
vector< IntType > parse_ints(const std::string &spec, const IntType last=std::numeric_limits< IntType >::max())
Definition: mrtrix.h:422
vector< std::string > split_lines(const std::string &string, bool ignore_empty_fields=true, size_t num=std::numeric_limits< size_t >::max())
Definition: mrtrix.h:179
std::string shorten(const std::string &text, size_t longest=40, size_t prefix=10)
convert a long string to 'beginningofstring...endofstring' for display
Definition: mrtrix.h:88
std::string printf(const char *format,...)
Definition: mrtrix.h:116
std::string str< cfloat >(const cfloat &value, int precision)
Definition: mrtrix.h:295
vector< std::string > split(const std::string &string, const char *delimiters=" \t\n", bool ignore_empty_fields=false, size_t num=std::numeric_limits< size_t >::max())
T to(const std::string &string)
Definition: mrtrix.h:260
size_t is_dash(const std::string &arg)
match whole string to a dash or any Unicode character that looks like one
Definition: mrtrix.h:225
std::string & add_line(std::string &original, const std::string &new_line)
add a line to a string, taking care of inserting a newline if needed
Definition: mrtrix.h:81
Definition: types.h:303
static constexpr int value()
Definition: mrtrix.h:60
size_t num
Definition: thread.h:216
const std::string name
Definition: thread.h:108