1 /*
2  * Copyright (c) 2011-2014, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation and/or
13  * other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #pragma once
32 
33 #include <limits>
34 #include <sstream>
35 #include <string>
36 #include <stdint.h>
37 #include <cmath>
38 #include <type_traits>
39 
40 /* details namespace is here to hide implementation details to header end user. It
41  * is NOT intended to be used outside. */
42 namespace details
43 {
44 
45 /* List of allowed types for conversion */
46 template <typename T>
47 struct ConvertionAllowed : std::false_type
48 {
49 };
50 template <>
51 struct ConvertionAllowed<bool> : std::true_type
52 {
53 };
54 template <>
55 struct ConvertionAllowed<long long> : std::true_type
56 {
57 };
58 template <>
59 struct ConvertionAllowed<unsigned long long> : std::true_type
60 {
61 };
62 template <>
63 struct ConvertionAllowed<long> : std::true_type
64 {
65 };
66 template <>
67 struct ConvertionAllowed<unsigned long> : std::true_type
68 {
69 };
70 template <>
71 struct ConvertionAllowed<int> : std::true_type
72 {
73 };
74 template <>
75 struct ConvertionAllowed<unsigned int> : std::true_type
76 {
77 };
78 template <>
79 struct ConvertionAllowed<short> : std::true_type
80 {
81 };
82 template <>
83 struct ConvertionAllowed<unsigned short> : std::true_type
84 {
85 };
86 template <>
87 struct ConvertionAllowed<unsigned char> : std::true_type
88 {
89 };
90 template <>
91 struct ConvertionAllowed<signed char> : std::true_type
92 {
93 };
94 template <>
95 struct ConvertionAllowed<float> : std::true_type
96 {
97 };
98 template <>
99 struct ConvertionAllowed<double> : std::true_type
100 {
101 };
102 
103 /* Allow chars and unsigned chars to be converted via integers */
104 template <typename T, typename Via>
105 struct ConvertionAllowedVia : std::false_type
106 {
107 };
108 template <>
109 struct ConvertionAllowedVia<unsigned char, unsigned int> : std::true_type
110 {
111 };
112 template <>
113 struct ConvertionAllowedVia<signed char, int> : std::true_type
114 {
115 };
116 
117 template <typename T>
convertTo(const std::string & str,T & result)118 static inline bool convertTo(const std::string &str, T &result)
119 {
120     /* Check that conversion to that type is allowed.
121      * If this fails, this means that this template was not intended to be used
122      * with this type, thus that the result is undefined. */
123     static_assert(ConvertionAllowed<T>::value, "convertTo does not support this conversion");
124 
125     if (str.find_first_of(std::string("\r\n\t\v ")) != std::string::npos) {
126         return false;
127     }
128 
129     /* Check for a '-' in string. If type is unsigned and a - is found, the
130      * parsing fails. This is made necessary because "-1" is read as 65535 for
131      * uint16_t, for example */
132     if (str.find("-") != std::string::npos && !std::numeric_limits<T>::is_signed) {
133         return false;
134     }
135 
136     std::stringstream ss(str);
137 
138     /* Sadly, the stream conversion does not handle hexadecimal format, thus
139      * check is done manually */
140     if (str.substr(0, 2) == "0x") {
141         if (std::numeric_limits<T>::is_integer) {
142             ss >> std::hex >> result;
143         } else {
144             /* Conversion undefined for non integers */
145             return false;
146         }
147     } else {
148         ss >> result;
149     }
150 
151     return ss.eof() && !ss.fail() && !ss.bad();
152 }
153 
154 template <typename T, typename Via>
convertToVia(const std::string & str,T & result)155 static inline bool convertToVia(const std::string &str, T &result)
156 {
157     /* Check that conversion to that type is allowed.
158      * If this fails, this means that this template was not intended to be used
159      * with this type, thus that the result is undefined. */
160     static_assert(ConvertionAllowedVia<T, Via>::value,
161                   "convertToVia does not support this conversion");
162 
163     /* We want to override the behaviour of convertTo<T> with that of
164      * convertTo<Via> and then safely cast the result into a T. */
165     Via res;
166 
167     if (!convertTo<Via>(str, res)) {
168         return false;
169     }
170 
171     if ((res > std::numeric_limits<T>::max()) or (res < std::numeric_limits<T>::min())) {
172         return false;
173     }
174 
175     result = static_cast<T>(res);
176     return true;
177 }
178 } // namespace details
179 
180 /**
181  * Convert a string to a given type.
182  *
183  * This template function read the value of the type T in the given string.
184  * The function does not allow to have white spaces around the value to parse
185  * and tries to parse the whole string, which means that if some bytes were not
186  * read in the string, the function fails.
187  * Hexadecimal representation (ie numbers starting with 0x) is supported only
188  * for integral types conversions.
189  * Result may be modified, even in case of failure.
190  *
191  * @param[in]  str    the string to parse.
192  * @param[out] result reference to object where to store the result.
193  *
194  * @return true if conversion was successful, false otherwise.
195  */
196 template <typename T>
convertTo(const std::string & str,T & result)197 static inline bool convertTo(const std::string &str, T &result)
198 {
199     return details::convertTo<T>(str, result);
200 }
201 
202 /** Specialization for unsigned char of convertTo template function.
203  *
204  * This function follows the same paradigm than it's generic version.
205  *
206  * The generic version was converting char as it was a character
207  * (unsigned char is an alias to unsigned char on most compiler).
208  * Thus converting "1" would return 49 ie '1'.
209  * As convertTo is thought as an _numerical_ convertion tool
210  * (contrary to boost::lexical_cast for example),
211  * forbid considering the input as a character and consider unsigned char
212  * (aka unsigned char) as a number exclusively.
213  *
214  * @param[in]  str    the string to parse.
215  * @param[out] result reference to object where to store the result.
216  *
217  * @return true if conversion was successful, false otherwise.
218  */
219 template <>
convertTo(const std::string & str,unsigned char & result)220 inline bool convertTo<unsigned char>(const std::string &str, unsigned char &result)
221 {
222     return details::convertToVia<unsigned char, unsigned int>(str, result);
223 }
224 
225 /** Specialization for signed char of convertTo template function.
226  *
227  * @see convertTo<unsigned char>
228  */
229 template <>
convertTo(const std::string & str,signed char & result)230 inline bool convertTo<signed char>(const std::string &str, signed char &result)
231 {
232     return details::convertToVia<signed char, int>(str, result);
233 }
234 /**
235  * Specialization for float of convertTo template function.
236  *
237  * This function follows the same paradigm than it's generic version and is
238  * based on it but makes furthers checks on the returned value.
239  *
240  * The specific implementation is made necessary because the stlport conversion
241  * from string to float behaves differently than GNU STL: overflow produce
242  * +/-Infinity rather than an error.
243  *
244  * @param[in]  str    the string to parse.
245  * @param[out] result reference to object where to store the result.
246  *
247  * @return true if conversion was successful, false otherwise.
248  */
249 template <>
convertTo(const std::string & str,float & result)250 inline bool convertTo<float>(const std::string &str, float &result)
251 {
252     if (!details::convertTo(str, result)) {
253         return false;
254     }
255 
256     if (!std::isfinite(result)) {
257         return false;
258     }
259 
260     return true;
261 }
262 
263 /**
264  * Specialization for double of convertTo template function.
265  *
266  * This function follows the same paradigm than it's generic version and is
267  * based on it but makes furthers checks on the returned value.
268  *
269  * The specific implementation is made necessary because the stlport conversion
270  * from string to double behaves differently than GNU STL: overflow produce
271  * +/-Infinity rather than an error.
272  *
273  * @param[in]  str    the string to parse.
274  * @param[out] result reference to object where to store the result.
275  *
276  * @return true if conversion was successful, false otherwise.
277  */
278 template <>
convertTo(const std::string & str,double & result)279 inline bool convertTo<double>(const std::string &str, double &result)
280 {
281     if (!details::convertTo(str, result)) {
282         return false;
283     }
284 
285     if (!std::isfinite(result)) {
286         return false;
287     }
288 
289     return true;
290 }
291 
292 /**
293  * Specialization for boolean of convertTo template function.
294  *
295  * This function follows the same paradigm than it's generic version.
296  * This function accepts to parse boolean as "0/1" or "false/true" or
297  * "FALSE/TRUE".
298  * The specific implementation is made necessary because the behaviour of
299  * string streams when parsing boolean values is not sufficient to fit our
300  * requirements. Indeed, parsing "true" will correctly parse the value, but the
301  * end of stream is not reached which makes the ss.eof() fails in the generic
302  * implementation.
303  *
304  * @param[in]  str    the string to parse.
305  * @param[out] result reference to object where to store the result.
306  *
307  * @return true if conversion was successful, false otherwise.
308  */
309 template <>
convertTo(const std::string & str,bool & result)310 inline bool convertTo<bool>(const std::string &str, bool &result)
311 {
312     if (str == "0" || str == "FALSE" || str == "false") {
313         result = false;
314         return true;
315     }
316 
317     if (str == "1" || str == "TRUE" || str == "true") {
318         result = true;
319         return true;
320     }
321 
322     return false;
323 }
324