source: branches/0.11-stable/yat/utility/utility.h @ 3155

Last change on this file since 3155 was 3155, checked in by Peter, 8 years ago

fixes #777. Make doxygen ignore internal stuff

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 12.7 KB
Line 
1#ifndef _theplu_yat_utility_utility_
2#define _theplu_yat_utility_utility_
3
4// $Id: utility.h 3155 2014-01-03 08:04:11Z peter $
5
6/*
7  Copyright (C) 2005 Jari Häkkinen, Peter Johansson, Markus Ringnér
8  Copyright (C) 2006 Jari Häkkinen
9  Copyright (C) 2007, 2008 Jari Häkkinen, Peter Johansson
10  Copyright (C) 2009, 2010, 2011, 2012, 2013 Peter Johansson
11
12  This file is part of the yat library, http://dev.thep.lu.se/yat
13
14  The yat library is free software; you can redistribute it and/or
15  modify it under the terms of the GNU General Public License as
16  published by the Free Software Foundation; either version 3 of the
17  License, or (at your option) any later version.
18
19  The yat library is distributed in the hope that it will be useful,
20  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  General Public License for more details.
23
24  You should have received a copy of the GNU General Public License
25  along with yat. If not, see <http://www.gnu.org/licenses/>.
26*/
27
28///
29/// \file yat/utility/utility.h
30///
31/// @brief Some useful functions are placed here
32///
33
34#include "deprecate.h"
35#include "Exception.h"
36#include "yat_assert.h"
37
38#include <gsl/gsl_math.h>
39
40#include <algorithm>
41#include <cctype>
42#include <cmath>
43#include <functional>
44#include <limits>
45#include <locale>
46#include <istream>
47#include <string>
48#include <stdexcept>
49#include <sstream>
50#include <utility>
51#include <vector>
52
53namespace theplu {
54namespace yat {
55namespace utility {
56
57  /**
58     Same as posix C function with same name but works on \c
59     std::string rather than \c char*.
60
61     \see http://linux.die.net/man/3/basename
62
63     \return everything after last '/'
64
65     \since New in yat 0.10
66   */
67  std::string basename(const std::string& fn);
68
69  /**
70     For each element in resulting range assign it to 0.0 if
71     corresponding element in input range is NaN else assign it to
72     1.0.
73
74     \return true if there is at least one NaN in input range
75     [first, last).
76
77     \since New in yat 0.5
78  */
79  template<typename InputIterator, typename OutputIterator>
80  bool binary_weight(InputIterator first, InputIterator last,
81                     OutputIterator result);
82
83  /**
84     Same as C function \c chdir but throws on failure (instead of
85     retrning non-zero).
86
87     \throw runtime_error if underlying chdir returns non-zero
88
89     \see http://linux.die.net/man/3/chdir
90
91     \since New in yat 0.10
92   */
93  void chdir(const std::string& dir);
94
95  /**
96     same as C function \c chmod but throws on failure (instead of
97     returning non-zero).
98
99     \see http://linux.die.net/man/3/chmod
100
101     \since New in yat 0.10
102   */
103  void chmod(const std::string& filename, mode_t mode);
104
105  /**
106     \brief convert T to a string
107
108     T is supposed to be a numerical type.
109
110     \since new in yat 0.8
111   */
112  template<typename T>
113  std::string convert(T input);
114
115  /**
116     \brief convert string to (numerical) type
117
118     \throw runtime_error if conversion fails
119   */
120  template<typename T>
121  T convert(const std::string& s);
122
123  /**
124     @brief Copy file \a source to \a target.
125
126     @throw runtime_error if read error of \a source or write error
127     for \a target is encountered.
128  */
129  void copy_file(const std::string& source, const std::string& target);
130
131  /**
132     Same as posix C function with same name but works on \c
133     std::string rather than \c char*.
134
135     \see http://linux.die.net/man/3/dirname
136
137     \return everything prior last '/'.
138
139     \since New in yat 0.10
140   */
141  std::string dirname(const std::string& fn);
142
143  /**
144     \return true if \a str matches \a pattern
145
146     \see http://linux.die.net/man/3/fnmatch
147
148     \throw runtime_error if returned value from underlying fnmatch is
149     neither zero nor \c FNM_NOMATCH.
150
151     \since New in yat 0.10
152  */
153  bool fnmatch(const std::string& pattern, const std::string& str,
154               int flags=0);
155
156  /**
157     \brief check if string is convertible to (numerical) type \c T
158
159     \since New in yat 0.5
160   */
161  template<typename T>
162  bool is(const std::string& s);
163
164  ///
165  /// @return true if string is a double
166  ///
167  /// \deprecated Provided for backward compatibility with the 0.4
168  /// API. Use is<double>(const std::string&)
169  ///
170  bool is_double(const std::string&) YAT_DEPRECATE;
171
172  /**
173     This function takes the first word (separated by whitespace) in
174     \a s, replaces all upper case with lower case, and compares it
175     with \a other.
176
177     \return true if processed \a s is equal to \a other. It returns
178     false otherwise or if \a s contains more than one word.
179  */
180  bool is_equal(std::string s, std::string other);
181
182  ///
183  /// @return true if string is a float
184  ///
185  /// \deprecated Provided for backward compatibility with the 0.4
186  /// API. Use is<float>(const std::string&)
187  ///
188  bool is_float(const std::string&) YAT_DEPRECATE;
189
190  ///
191  /// @return true if string is an int
192  ///
193  /// \deprecated Provided for backward compatibility with the 0.4
194  /// API. Use is<int>(const std::string&)
195  ///
196  bool is_int(const std::string&) YAT_DEPRECATE;
197
198  ///
199  /// @return true if string is "nan" (case-insensitive)
200  ///
201  bool is_nan(const std::string& s);
202
203  /**
204     The std::istream will be interpreted as outlined here:
205
206     Lines are separated by character \a line_sep and rows are
207     separated by character \a sep.
208     
209     The first line is read into a stringstream, which is used to
210     load the first vector (vec[0]) with elements using
211     load(stringstream, vec[0], sep).
212     
213     Therefore, column elements separation has two modes depending
214     on the value of \a sep.
215     
216     - If \a sep is the default '\\0' value then column elements are
217     separated with white space characters except the new line
218     character. Multiple sequential white space characters are treated
219     as one separator.
220     
221     - Setting \a sep to something else than the default value will
222     change the behaviour to use the \a sep character as the separator
223     between column elements. Multiple sequential \a sep characters
224     will be treated as separating elements with missing values.
225
226     If \a rectangle is true, rows must contain same number of
227     elements or function will throw.
228
229     If \a ignore_empty is true empty lines are ignored.
230
231     \see load(std::istream&, std::vector<T>&, char sep='\\0')
232
233     \note Requirement on T: utility::convert<T> must be supported
234     (from yat 0.7 T=string is also supported)
235
236     \since New in yat 0.6
237   */
238  template<typename T>
239  void load(std::istream& is, std::vector<std::vector<T> >& vec, char sep='\0', 
240            char line_sep='\n', bool ignore_empty=false, bool rectangle=true);
241
242  /**
243     \brief Fill a vector<T> with elements from istream
244
245     Element separation has two modes depending on the value of \a
246     sep.
247     
248     - If \a sep is the default '\\0' value then elements are
249     separated with white space characters. Multiple sequential white
250     space characters are treated as one separator.
251     
252     - Setting \a sep to something else than the default value will
253     change the behaviour to use the \a sep character as the
254     separator between column elements. Multiple sequential \a sep
255     characters will be treated as separating elements with missing
256     values. Missing values are set to std::numeric_limits<T>::quiet_NaN
257     
258     \note Requirement on T: utility::convert<T> must be supported
259     (from yat 0.7 T=string is also supported)
260
261     \since New in yat 0.6
262   */
263  template<typename T>
264  void load(std::istream& is, std::vector<T>& vec, char sep='\0');
265
266  /**
267     \return base-2 logarithm of x
268
269     \since New in yat 0.10
270   */
271  // c++11 provides std::log2 so perhaps we should call that one if
272  // availalable (but a bit tricky since this is a public header)
273  template<typename T>
274  T log2(T x) { return std::log(x)/M_LN2; }
275
276/// \cond IGNORE_DOXYGEN
277
278// private namespace
279namespace detail {
280
281  /**
282     \internal
283
284     \brief convert s to t
285
286     used in function is<T> and convert<T>
287
288     \return true if conversion was successful
289
290     \internal
291   */
292  template<typename T>
293  bool convert(const std::string& s, T& t);
294
295  /**
296     Functor used in load function
297   */
298  template<typename T>
299  struct VectorPusher
300  {
301    /**
302       convert element to T and push on vec's back
303
304       \internal
305     */
306    void operator()(const std::string& element, std::vector<T>& vec)
307    { 
308      if (!element.size())
309        vec.push_back(std::numeric_limits<T>::quiet_NaN());
310      else {
311        vec.push_back(theplu::yat::utility::convert<T>(element));
312      }
313    }
314  };
315
316  /**
317     specialization for string
318
319     \internal
320   */
321  template<>
322  struct VectorPusher<std::string>
323  {
324    /**
325       push element on vec's back
326     */
327    void operator()(const std::string& element, std::vector<std::string>& vec)
328    { 
329      vec.push_back(element);
330    }
331  };
332
333} // end of namespace detail
334
335/// \endcond
336
337
338  /**
339     \brief create a directory \a dir
340
341     \see http://linux.die.net/man/3/mkdir
342
343     \throw runtime_error if creation failed
344
345     \since New in yat 0.10
346   */
347  void mkdir(const std::string& dir, mode_t mode=0777);
348
349  /**
350     Similar to mkdir(const std::string&, mode_t).
351
352     No error if \a dir already exist. Make parent directories as needed.
353
354     \since New in yat 0.10
355   */
356  void mkdir_p(const std::string& dir, mode_t mode=0777);
357
358  /**
359     same as C function remove but throws errno_error at failure
360
361     \see http://linux.die.net/man/3/remove
362
363     \since New in yat 0.10
364   */
365  void remove(const std::string& fn);
366
367  /**
368     same as C function with same name but throws errno_error if error
369     is encountered
370
371     \see http://linux.die.net/man/3/rename
372
373     \since New in yat 0.10
374   */
375  void rename(const std::string& from, const std::string to);
376
377  /**
378     In \a full_str replace every sub-string \a old_str with \a
379     new_str;
380
381     \since New in yat 0.10
382   */
383  void replace(std::string& full_str, std::string old_str, std::string new_str);
384
385  // template implementations
386
387  template<typename InputIterator, typename OutputIterator>
388  bool binary_weight(InputIterator first, InputIterator last, 
389                     OutputIterator result)
390  {
391    bool nan=false;
392    while (first!=last) {
393      if (std::isnan(*first)) {
394        *result=0;
395        nan=true;
396      }
397      else
398        *result = 1.0;
399      ++first;
400      ++result;
401    }
402    return nan;
403  }
404
405
406  // template implementations
407  template<typename T>
408  std::string convert(T input)
409  {
410    std::ostringstream ss;
411    ss << input;
412    return ss.str();
413  }
414
415
416  template<typename T>
417  T convert(const std::string& s)
418  {
419    T result;
420    if (!detail::convert(s, result))
421      throw runtime_error(std::string("yat::utility::convert(\"")+s+
422                          std::string("\")"));
423    return result;
424  }
425
426
427  template<typename T>
428  bool is(const std::string& s)
429  {
430    T tmp;
431    return detail::convert(s, tmp);
432  }
433
434
435  template<typename T>
436  void load(std::istream& is, std::vector<std::vector<T> >& matrix, 
437            char sep, char line_sep, bool ignore_empty, 
438            bool rectangle)
439  {
440    size_t nof_columns=0;
441    std::string line;
442    while(getline(is, line, line_sep)){
443      if (line.empty() && ignore_empty)
444        continue;
445      matrix.push_back(std::vector<T>());
446      std::vector<T>& v=matrix.back();
447      v.reserve(nof_columns);
448      std::stringstream ss(line);
449      load(ss, v, sep);
450      // add NaN for final separator (or empty string if T=std::string)
451      detail::VectorPusher<T> pusher;
452      if(sep!='\0' && !line.empty() && line[line.size()-1]==sep) 
453        pusher("", v);
454     
455      if (rectangle && nof_columns && v.size()!=nof_columns) {
456        std::ostringstream s;
457        s << "load stream error: "
458          << "line " << matrix.size() << " has " << v.size()
459          << " columns; expected " << nof_columns << " columns.";
460        throw utility::IO_error(s.str());
461      }       
462      nof_columns = std::max(nof_columns, v.size());
463    }
464
465    // manipulate the state of the stream to be good
466    is.clear(std::ios::goodbit);
467  }
468
469  template<typename T>
470  void load(std::istream& is, std::vector<T>& vec, char sep)
471  {
472    detail::VectorPusher<T> pusher;
473    std::string element;
474    bool ok=true;
475    while(true) {
476      if(sep=='\0')
477        ok=(is>>element);
478      else
479        ok=getline(is, element, sep);
480      if(!ok)
481        break;
482      pusher(element, vec);
483    }
484  }
485
486/// \cond IGNORE_DOXYGEN
487namespace detail {
488  template<typename T>
489  bool convert(const std::string& s, T& result)
490  {
491    if (!std::numeric_limits<T>::is_signed) {
492      // first non-whitespace character
493      std::string::const_iterator iter = s.begin();
494      while (iter!=s.end() && std::isspace(*iter))
495        ++iter;
496      // unsigned int cannot start with a '-' and with some compilers
497      // operation ss >> result won't fail so catch it like this instead.
498      if (iter==s.end() || *iter=='-')
499        return false;
500    }
501    std::istringstream ss(s);
502    ss >> result;
503    if (ss.fail()) {
504      if (is_nan(s)) {
505        result = std::numeric_limits<T>::quiet_NaN();
506        return true;
507      }
508      if (is_equal(s, "inf")) {
509        result = std::numeric_limits<T>::infinity();
510        return true;
511      }
512      if (is_equal(s, "-inf")) {
513        // unsigned types are caught in prologue
514        YAT_ASSERT(std::numeric_limits<T>::is_signed);
515        result = -std::numeric_limits<T>::infinity();
516        return true;
517      }
518      return false;
519    }
520    // Check that nothing is left on stream
521    std::string b;
522    ss >> b;
523    return b.empty();
524  }
525} // of namespace detail
526
527/// \endcond
528
529}}} // of namespace utility, yat, and theplu
530
531#endif
Note: See TracBrowser for help on using the repository browser.