source: trunk/yat/utility/utility.h @ 4102

Last change on this file since 4102 was 4102, checked in by Peter, 2 years ago

add wrapper function that calls std::string::contains, ::starts_with, and ::ends_with, if available; otherwise use home-brewed code. Add autoconf tests that check if these functions are available in CXX.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 22.5 KB
Line 
1#ifndef _theplu_yat_utility_utility_
2#define _theplu_yat_utility_utility_
3
4// $Id: utility.h 4102 2021-09-22 07:50:18Z 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, 2014, 2015, 2016, 2017, 2018, 2019, 2020 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 "config_public.h"
35
36#include "concept_check.h"
37#include "deprecate.h"
38#include "Exception.h"
39#include "iterator_traits.h"
40#include "WeightIterator.h"
41#include "yat_assert.h"
42
43#include <boost/iterator/iterator_concepts.hpp>
44#include <boost/concept_check.hpp>
45
46#include <algorithm>
47#include <cctype>
48#include <cmath>
49#include <cstdlib>
50#include <cstring>
51#include <functional>
52#include <limits>
53#include <locale>
54#include <istream>
55#include <iostream>
56#include <numeric>
57#include <string>
58#include <stdexcept>
59#include <sstream>
60#include <utility>
61#include <vector>
62
63namespace theplu {
64namespace yat {
65namespace utility {
66
67  /**
68     Same as posix C function with same name but works on \c
69     std::string rather than \c char*.
70
71     \see http://linux.die.net/man/3/basename
72
73     \return everything after last '/'
74
75     \since New in yat 0.10
76   */
77  std::string basename(const std::string& fn);
78
79  /**
80     Read data from \a is into variable \a x. Data has typically been
81     written with binary_write(std::ostream& os, T).
82
83     \c T is expected to be a native numerical type.
84
85     \return \a is
86
87     \since New in yat 0.18
88   */
89  template<typename T>
90  std::istream& binary_read(std::istream& is, T& x)
91  {
92    const unsigned char type_size=sizeof(T);
93    YAT_ASSERT(type_size <= 16);
94    char buffer[16];
95    is.read(buffer, type_size);
96    memcpy(&x, buffer, type_size);
97    return is;
98  }
99
100
101  /**
102     Write value of \a x into ostream \a os in binary format. Since
103     the data is written in the native binary format, it may not be
104     portable between different architectures.
105
106     \c T is expected to be a native numerical type.
107
108     \see binary_read(std::istream&, T&)
109
110     \since New in yat 0.18
111   */
112  template<typename T>
113  void binary_write(std::ostream& os, T x)
114  {
115    const unsigned char type_size=sizeof(T);
116    YAT_ASSERT(type_size <= 16);
117    char buffer[16];
118    memcpy(buffer, &x, type_size);
119    os.write(buffer, type_size);
120  }
121
122  /**
123     For each element in resulting range assign it to 0.0 if
124     corresponding element in input range is NaN else assign it to
125     1.0.
126
127     \return true if there is at least one NaN in input range
128     [first, last).
129
130     Type Requirements:
131     - \c InputIterator is \single_pass_iterator
132     - \c InputIterator is \readable_iterator
133     - \c std::isnan(*InputIterator) is a valid expression (e.g. \c
134       float, \c double, and \c long \c double )
135     - \c OutputIterator is \incrementable_iterator
136     - \c OutputIterator is \writable_iterator
137     - \c float is convertible to \c OutputIterator value type
138
139     \since New in yat 0.5
140  */
141  template<typename InputIterator, typename OutputIterator>
142  bool binary_weight(InputIterator first, InputIterator last,
143                     OutputIterator result);
144
145  /**
146     Same as C function \c chdir but throws on failure (instead of
147     retrning non-zero).
148
149     \throw runtime_error if underlying chdir returns non-zero
150
151     \see http://linux.die.net/man/3/chdir
152
153     \since New in yat 0.10
154   */
155  void chdir(const std::string& dir);
156
157  /**
158     same as C function \c chmod but throws on failure (instead of
159     returning non-zero).
160
161     \see http://linux.die.net/man/3/chmod
162
163     \since New in yat 0.10
164   */
165  void chmod(const std::string& filename, mode_t mode);
166
167  /**
168     \return true if string \a str contains \a s
169
170     \see https://en.cppreference.com/w/cpp/string/basic_string/comtains
171
172     \since New in yat 0.20
173   */
174  template<typename charT, class Traits, class Allocator>
175  bool contains(const std::basic_string<charT, Traits, Allocator>& str,
176                const std::basic_string<charT, Traits, Allocator>& substr);
177
178  /// since New in yat 0.20
179  template<typename charT, class Traits, class Allocator>
180  bool contains(const std::basic_string<charT, Traits, Allocator>& str,
181                charT substr);
182
183  /// since New in yat 0.20
184  template<typename charT, class Traits, class Allocator>
185  bool contains(const std::basic_string<charT, Traits, Allocator>& str,
186                const charT* substr);
187
188
189  /**
190     \brief convert T to a string
191
192     \c T is supposed to be a numerical type.
193
194     \see
195     <a href="http://www.cplusplus.com/reference/string/to_string/">std::to_string</a>
196     in C++11
197
198     \since new in yat 0.8
199   */
200  template<typename T>
201  std::string convert(T input);
202
203  /**
204     \brief convert string to (numerical) type
205
206     \throw runtime_error if conversion fails
207   */
208  template<typename T>
209  T convert(const std::string& s);
210
211  /**
212     \brief try to convert
213
214     \see is<T>
215     \see convert<T>
216
217     \return true if conversion was successful
218
219     \since new in yat 0.15
220   */
221  template<typename T>
222  bool convert_try(const std::string& s, T& t);
223
224  /**
225     @brief Copy file \a source to \a target.
226
227     @throw runtime_error if read error of \a source or write error
228     for \a target is encountered.
229  */
230  void copy_file(const std::string& source, const std::string& target);
231
232
233  /**
234     Same as posix C function with same name but works on \c
235     std::string rather than \c char*.
236
237     \see http://linux.die.net/man/3/dirname
238
239     \return everything prior last '/'.
240
241     \since New in yat 0.10
242   */
243  std::string dirname(const std::string& fn);
244
245  /**
246     \return true if string \a str ends with \a suffix
247
248     \see https://en.cppreference.com/w/cpp/string/basic_string/ends_with
249
250     \since New in yat 0.20
251   */
252  template<typename charT, class Traits, class Allocator>
253  bool ends_with(const std::basic_string<charT, Traits, Allocator>& str,
254                 const std::basic_string<charT, Traits, Allocator>& suffix);
255
256  /// since New in yat 0.20
257  template<typename charT, class Traits, class Allocator>
258  bool ends_with(const std::basic_string<charT, Traits, Allocator>& str,
259                 charT suffix);
260
261  /// since New in yat 0.20
262  template<typename charT, class Traits, class Allocator>
263  bool ends_with(const std::basic_string<charT, Traits, Allocator>& str,
264                 const charT* suffix);
265
266  /**
267     \return true if \a str matches \a pattern
268
269     \see http://linux.die.net/man/3/fnmatch
270
271     \throw runtime_error if returned value from underlying fnmatch is
272     neither zero nor \c FNM_NOMATCH.
273
274     \since New in yat 0.10
275  */
276  bool fnmatch(const std::string& pattern, const std::string& str,
277               int flags=0);
278
279  /**
280     \brief current directory
281
282     \see ​https://linux.die.net/man/3/getcwd
283
284     \since New in yat 0.17
285   */
286  std::string getcwd(void);
287
288  /**
289     Function can be used as argument to \c gsl_set_error_handler, so
290     it is called when by GSL when an error is detected. This will
291     result in a GSL_error is thrown.
292
293     \see https://www.gnu.org/software/gsl/doc/html/err.html
294
295     \since New in yat 0.18
296   */
297  void gsl_error_handler(const char* reason, const char* file,
298                         int line, int gsl_errno);
299
300  /**
301     \brief check if string is convertible to (numerical) type \c T
302
303     \since New in yat 0.5
304   */
305  template<typename T>
306  bool is(const std::string& s);
307
308  ///
309  /// @return true if string is a double
310  ///
311  /// \deprecated Provided for backward compatibility with the 0.4
312  /// API. Use is<double>(const std::string&)
313  ///
314  bool is_double(const std::string&) YAT_DEPRECATE;
315
316  /**
317     This function takes the first word (separated by whitespace) in
318     \a s, replaces all upper case with lower case, and compares it
319     with \a other.
320
321     \return true if processed \a s is equal to \a other. It returns
322     false otherwise or if \a s contains more than one word.
323  */
324  bool is_equal(std::string s, std::string other);
325
326  ///
327  /// @return true if string is a float
328  ///
329  /// \deprecated Provided for backward compatibility with the 0.4
330  /// API. Use is<float>(const std::string&)
331  ///
332  bool is_float(const std::string&) YAT_DEPRECATE;
333
334  ///
335  /// @return true if string is an int
336  ///
337  /// \deprecated Provided for backward compatibility with the 0.4
338  /// API. Use is<int>(const std::string&)
339  ///
340  bool is_int(const std::string&) YAT_DEPRECATE;
341
342  ///
343  /// @return true if string is "nan" (case-insensitive)
344  ///
345  bool is_nan(const std::string& s);
346
347  /**
348     The std::istream will be interpreted as outlined here:
349
350     Lines are separated by character \a line_sep and rows are
351     separated by character \a sep.
352     
353     The first line is read into a stringstream, which is used to
354     load the first vector (vec[0]) with elements using
355     load(stringstream, vec[0], sep).
356     
357     Therefore, column elements separation has two modes depending
358     on the value of \a sep.
359     
360     - If \a sep is the default '\\0' value then column elements are
361     separated with white space characters except the new line
362     character. Multiple sequential white space characters are treated
363     as one separator.
364     
365     - Setting \a sep to something else than the default value will
366     change the behaviour to use the \a sep character as the separator
367     between column elements. Multiple sequential \a sep characters
368     will be treated as separating elements with missing values.
369
370     If \a rectangle is true, rows must contain same number of
371     elements or function will throw.
372
373     If \a ignore_empty is true empty lines are ignored.
374
375     \see load(std::istream&, std::vector<T>&, char sep='\\0')
376
377     \note Requirement on T: utility::convert<T> must be supported
378     (from yat 0.7 T=string is also supported)
379
380     \since New in yat 0.6
381   */
382  template<typename T>
383  void load(std::istream& is, std::vector<std::vector<T> >& vec, char sep='\0', 
384            char line_sep='\n', bool ignore_empty=false, bool rectangle=true);
385
386  /**
387     \brief Fill a vector<T> with elements from istream
388
389     Element separation has two modes depending on the value of \a
390     sep.
391     
392     - If \a sep is the default '\\0' value then elements are
393     separated with white space characters. Multiple sequential white
394     space characters are treated as one separator.
395     
396     - Setting \a sep to something else than the default value will
397     change the behaviour to use the \a sep character as the
398     separator between column elements. Multiple sequential \a sep
399     characters will be treated as separating elements with missing
400     values. Missing values are set to std::numeric_limits<T>::quiet_NaN
401     
402     \note Requirement on T: utility::convert<T> must be supported
403     (from yat 0.7 T=string is also supported)
404
405     \since New in yat 0.6
406   */
407  template<typename T>
408  void load(std::istream& is, std::vector<T>& vec, char sep='\0');
409
410  /**
411     \return base-2 logarithm of x
412
413     Implemented for \c float, \c double, and \c long \c double.
414
415     \since New in yat 0.10
416   */
417  template<typename T>
418  T log2(T x) { return std::log2(x); }
419
420  /**
421     \brief create a directory \a dir
422
423     \see http://linux.die.net/man/3/mkdir
424
425     \throw runtime_error if creation failed
426
427     \since New in yat 0.10
428   */
429  void mkdir(const std::string& dir, mode_t mode=0777);
430
431  /**
432     Similar to mkdir(const std::string&, mode_t).
433
434     No error if \a dir already exist. Make parent directories as needed.
435
436     \since New in yat 0.10
437   */
438  void mkdir_p(const std::string& dir, mode_t mode=0777);
439
440  /**
441     Preprocessor macro that works like std::move, but also works when
442     rvalue is not available (in which case arg is returned).
443
444     \since new in yat 0.16
445   */
446#define YAT_MOVE(arg) std::move(arg)
447  /// likewise for std::move_if_noexcept
448#define YAT_MOVE_IF_NOEXCEPT(arg) std::move_if_noexcept(arg)
449
450  /**
451     \brief recursively print exception what
452
453     Print \c what() of \a error to \a out. If \a error is nested, the
454     exception that is nested in \a error is passed to print_what().
455
456     \note If nested exception is null, function calls terminate (this
457     behaviour might change in the future).
458
459     \since new in yat 0.16
460   */
461  void print_what(const std::exception& error, std::ostream& out=std::cerr);
462
463  /**
464     same as C function remove but throws errno_error at failure
465
466     \see http://linux.die.net/man/3/remove
467
468     \since New in yat 0.12
469   */
470  void remove(const std::string& fn);
471
472  /**
473     same as C function with same name but throws errno_error if error
474     is encountered
475
476     \see http://linux.die.net/man/3/rename
477
478     \since New in yat 0.12
479   */
480  void rename(const std::string& from, const std::string& to);
481
482  /**
483     In \a full_str replace every sub-string \a old_str with \a
484     new_str;
485
486     \since New in yat 0.10
487
488     \see <a href=https://www.boost.org/doc/libs/1_77_0/doc/html/boost/algorithm/replace_all.html>
489     boost::algorithm::replace_all</a>(string&, const string&, const string&)
490   */
491  void replace(std::string& full_str, const std::string& old_str,
492               const std::string& new_str);
493
494
495  /**
496     \return true if string \a str starts with \a prefix
497
498     \see https://en.cppreference.com/w/cpp/string/basic_string/starts_with
499
500     \since New in yat 0.20
501   */
502  template<typename charT, class Traits, class Allocator>
503  bool starts_with(const std::basic_string<charT, Traits, Allocator>& str,
504                   const std::basic_string<charT, Traits, Allocator>& prefix);
505
506  /// since New in yat 0.20
507  template<typename charT, class Traits, class Allocator>
508  bool starts_with(const std::basic_string<charT, Traits, Allocator>& str,
509                   charT prefix);
510
511  /// since New in yat 0.20
512  template<typename charT, class Traits, class Allocator>
513  bool starts_with(const std::basic_string<charT, Traits, Allocator>& str,
514                   const charT* prefix);
515
516  /**
517     Calculate sum of weights in range [first, last). The complexity
518     is linear except in the important case when \c Iterator is
519     unweighted and \random_access_iterator when complexity is constant.
520
521     Type Requirments:
522     - \c Iterator is \ref concept_data_iterator
523     - \c Iterator is \readable_iterator
524     - \c Iterator is \single_pass_iterator
525
526     \since New in yat 0.13
527   */
528  template<typename Iterator>
529  double sum_weight(Iterator first, Iterator last);
530
531  /**
532     same as C function symlink but throws errno_error at failure
533
534     Creates a symbolic link called \a path2 that contains the string
535     pointed to by \a path1
536
537     \see http://linux.die.net/man/3/symlink
538
539     \since New in yat 0.16
540   */
541  void symlink(const std::string& path1, const std::string& path2);
542
543/// \cond IGNORE_DOXYGEN
544
545// private namespace
546namespace detail {
547
548  /**
549     Functor used in load function
550   */
551  template<typename T>
552  struct VectorPusher
553  {
554    /**
555       convert element to T and push on vec's back
556
557       \internal
558     */
559    void operator()(const std::string& element, std::vector<T>& vec)
560    {
561      if (!element.size())
562        vec.push_back(std::numeric_limits<T>::quiet_NaN());
563      else {
564        vec.push_back(theplu::yat::utility::convert<T>(element));
565      }
566    }
567  };
568
569  /**
570     specialization for string
571
572     \internal
573   */
574  template<>
575  struct VectorPusher<std::string>
576  {
577    /**
578       push element on vec's back
579     */
580    void operator()(const std::string& element, std::vector<std::string>& vec)
581    {
582      vec.push_back(element);
583    }
584
585    void operator()(std::string&& element, std::vector<std::string>& vec)
586    {
587      vec.push_back(std::move(element));
588    }
589  };
590
591
592  template<typename Iterator>
593  double sum_weight(Iterator first, Iterator last, unweighted_iterator_tag tag)
594  {
595    return std::distance(first, last);
596  }
597
598
599  template<typename Iterator>
600  double sum_weight(Iterator first, Iterator last, weighted_iterator_tag tag)
601  {
602    return std::accumulate(weight_iterator(first), weight_iterator(last), 0);
603  }
604
605
606} // end of namespace detail
607
608/// \endcond
609
610  // template implementations
611
612  template<typename InputIterator, typename OutputIterator>
613  bool binary_weight(InputIterator first, InputIterator last, 
614                     OutputIterator result)
615  {
616    BOOST_CONCEPT_ASSERT((boost_concepts::SinglePassIterator<InputIterator>));
617    BOOST_CONCEPT_ASSERT((boost_concepts::ReadableIterator<InputIterator>));
618    BOOST_CONCEPT_ASSERT((boost_concepts::IncrementableIterator<OutputIterator>));
619    BOOST_CONCEPT_ASSERT((boost_concepts::WritableIterator<OutputIterator, float>));
620
621    bool nan=false;
622    while (first!=last) {
623      if (std::isnan(*first)) {
624        *result=0;
625        nan=true;
626      }
627      else
628        *result = 1.0;
629      ++first;
630      ++result;
631    }
632    return nan;
633  }
634
635
636  template<typename charT, class Traits, class Allocator>
637  bool contains(const std::basic_string<charT, Traits, Allocator>& str,
638                const std::basic_string<charT, Traits, Allocator>& substr)
639  {
640    return str.find(substr)!=std::basic_string<charT, Traits,Allocator>::npos;
641  }
642
643
644  template<typename charT, class Traits, class Allocator>
645  bool contains(const std::basic_string<charT, Traits, Allocator>& str,
646                charT substr)
647  {
648    return str.find(substr)!=std::basic_string<charT, Traits,Allocator>::npos;
649  }
650
651
652  template<typename charT, class Traits, class Allocator>
653  bool contains(const std::basic_string<charT, Traits, Allocator>& str,
654                const charT* substr)
655  {
656    return str.find(substr)!=std::basic_string<charT, Traits,Allocator>::npos;
657  }
658
659
660  template<typename T>
661  std::string convert(T input)
662  {
663    std::ostringstream ss;
664    ss << input;
665    return ss.str();
666  }
667
668
669  template<typename T>
670  T convert(const std::string& s)
671  {
672    T result;
673    if (!convert_try(s, result))
674      throw runtime_error(std::string("yat::utility::convert(\"")+s+
675                          std::string("\")"));
676    return result;
677  }
678
679
680  template<typename charT, class Traits, class Allocator>
681  bool ends_with(const std::basic_string<charT, Traits, Allocator>& str,
682                 const std::basic_string<charT, Traits, Allocator>& suffix)
683  {
684#ifdef YAT_HAVE_FUNC_STRING_ENDS_WITH
685    return str.ends_with(suffix);
686#else
687    size_t n = suffix.size();
688    return n <= str.size() && !str.compare(str.size()-n, n, suffix);
689#endif
690  }
691
692
693  template<typename charT, class Traits, class Allocator>
694  bool ends_with(const std::basic_string<charT, Traits, Allocator>& str,
695                 charT suffix)
696  {
697#ifdef YAT_HAVE_FUNC_STRING_ENDS_WITH
698    return str.ends_with(suffix);
699#else
700    Traits traits;
701    return str.size() && traits.eq(str.back(), suffix);
702#endif
703  }
704
705
706  template<typename charT, class Traits, class Allocator>
707  bool ends_with(const std::basic_string<charT, Traits, Allocator>& str,
708                 const charT* suffix)
709  {
710#ifdef YAT_HAVE_FUNC_STRING_ENDS_WITH
711    return str.ends_with(suffix);
712#else
713    Traits traits;
714    size_t n = traits.length(suffix);
715    return n <= str.size() && !str.compare(str.size()-n, n, suffix);
716#endif
717  }
718
719
720  template<typename T>
721  bool is(const std::string& s)
722  {
723    T tmp;
724    return convert_try(s, tmp);
725  }
726
727
728  template<typename T>
729  void load(std::istream& is, std::vector<std::vector<T> >& matrix,
730            char sep, char line_sep, bool ignore_empty,
731            bool rectangle)
732  {
733    size_t nof_columns=0;
734    std::string line;
735    while(getline(is, line, line_sep)){
736      if (line.empty() && ignore_empty)
737        continue;
738      std::vector<T> v;
739      v.reserve(nof_columns);
740      bool empty_last_element = !line.empty() && line.back()==sep;
741      std::stringstream ss(std::move(line));
742      load(ss, v, sep);
743      // add NaN for final separator (or empty string if T=std::string)
744      detail::VectorPusher<T> pusher;
745      if (empty_last_element && sep!='\0')
746        pusher("", v);
747
748      if (rectangle && nof_columns && v.size()!=nof_columns) {
749        std::ostringstream s;
750        s << "load stream error: "
751          << "line " << matrix.size() << " has " << v.size()
752          << " columns; expected " << nof_columns << " columns.";
753        throw utility::IO_error(s.str());
754      }
755      nof_columns = std::max(nof_columns, v.size());
756      matrix.push_back(std::move(v));
757    }
758
759    // manipulate the state of the stream to be good
760    is.clear(std::ios::goodbit);
761  }
762
763  template<typename T>
764  void load(std::istream& is, std::vector<T>& vec, char sep)
765  {
766    detail::VectorPusher<T> pusher;
767    while(true) {
768      std::string element;
769      if(sep=='\0')
770        is>>element;
771      else
772        getline(is, element, sep);
773      if(is.fail())
774        break;
775      pusher(std::move(element), vec);
776    }
777  }
778
779
780  template<typename charT, class Traits, class Allocator>
781  bool starts_with(const std::basic_string<charT, Traits, Allocator>& str,
782                   const std::basic_string<charT, Traits, Allocator>& prefix)
783  {
784#ifdef YAT_HAVE_FUNC_STRING_STARTS_WITH
785    return str.starts_with(prefix);
786#else
787    size_t n = prefix.size();
788    return n <= str.size() && !str.compare(0, n, prefix);
789#endif
790  }
791
792
793  template<typename charT, class Traits, class Allocator>
794  bool starts_with(const std::basic_string<charT, Traits, Allocator>& str,
795                   charT prefix)
796  {
797#ifdef YAT_HAVE_FUNC_STRING_STARTS_WITH
798    return str.starts_with(prefix);
799#else
800    Traits traits;
801    return str.size() && traits.eq(str[0], prefix);
802#endif
803  }
804
805
806  template<typename charT, class Traits, class Allocator>
807  bool starts_with(const std::basic_string<charT, Traits, Allocator>& str,
808                   const charT* prefix)
809  {
810#ifdef YAT_HAVE_FUNC_STRING_STARTS_WITH
811    return str.starts_with(prefix);
812#else
813    Traits traits;
814    size_t n = traits.length(prefix);
815    return n <= str.size() && !str.compare(0, n, prefix);
816#endif
817  }
818
819
820  template<typename Iterator>
821  double sum_weight(Iterator first, Iterator last)
822  {
823    BOOST_CONCEPT_ASSERT((DataIteratorConcept<Iterator>));
824    BOOST_CONCEPT_ASSERT((boost_concepts::ReadableIterator<Iterator>));
825    BOOST_CONCEPT_ASSERT((boost_concepts::SinglePassIterator<Iterator>));
826    typename weighted_iterator_traits<Iterator>::type tag;
827    return detail::sum_weight(first, last, tag);
828  }
829
830
831
832  template<typename T>
833  bool convert_try(const std::string& s, T& result)
834  {
835    if (!std::numeric_limits<T>::is_signed) {
836      // first non-whitespace character
837      std::string::const_iterator iter = s.begin();
838      while (iter!=s.end() && std::isspace(*iter))
839        ++iter;
840      // unsigned int cannot start with a '-' and with some compilers
841      // operation ss >> result won't fail so catch it like this instead.
842      if (iter==s.end() || *iter=='-')
843        return false;
844    }
845    std::istringstream ss(s);
846    ss >> result;
847    if (ss.fail()) {
848      if (is_nan(s) || is_equal(s, "-nan")) {
849        result = std::numeric_limits<T>::quiet_NaN();
850        return true;
851      }
852      if (is_equal(s, "inf")) {
853        result = std::numeric_limits<T>::infinity();
854        return true;
855      }
856      if (is_equal(s, "-inf")) {
857        // unsigned types are caught in prologue
858        YAT_ASSERT(std::numeric_limits<T>::is_signed);
859        result = -std::numeric_limits<T>::infinity();
860        return true;
861      }
862      return false;
863    }
864    // Check that nothing is left on stream
865    std::string b;
866    ss >> b;
867    return b.empty();
868  }
869
870}}} // of namespace utility, yat, and theplu
871
872#endif
Note: See TracBrowser for help on using the repository browser.