GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/string.h
Date: 2026-09-07 02:25:23
Exec Total Coverage
Lines: 62 81 76.5%
Functions: 14 26 53.8%
Branches: 37 101 36.6%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #ifndef GEOGRAM_BASIC_STRING
41 #define GEOGRAM_BASIC_STRING
42
43 #include <geogram/basic/common.h>
44 #include <geogram/basic/numeric.h>
45
46 #include <string>
47 #include <sstream>
48 #include <stdexcept>
49 #include <iomanip>
50
51 #include <vector>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <errno.h>
55 #include <stdio.h>
56 #include <limits.h>
57
58 /**
59 * \file geogram/basic/string.h
60 * \brief Functions for string manipulation
61 */
62
63 namespace GEO {
64
65 /*
66 * \brief String manipulation utilities.
67 */
68 namespace String {
69
70 /**
71 * \brief Splits a string into parts
72 * \details Splits the string \p in into a list of substrings \p out
73 * wherever \p separator occurs.
74 * \param[in] in the input string to split
75 * \param[in] separator the separator character
76 * \param[in] out the resulting list of substrings
77 * \param[in] skip_empty_fields specifies whether empty parts should
78 * be ignored and not stored in list \p out (this is true by default).
79 * \see join_strings()
80 */
81 void GEOGRAM_API split_string(
82 const std::string& in,
83 char separator,
84 std::vector<std::string>& out,
85 bool skip_empty_fields = true
86 );
87
88 /**
89 * \brief Splits a string into parts
90 * \details Splits the string \p in into a list of substrings \p out
91 * wherever \p separator occurs.
92 * \param[in] in the input string to split
93 * \param[in] separator the separator string
94 * \param[in] out the resulting list of substrings
95 * \param[in] skip_empty_fields specifies whether empty parts should
96 * be ignored and not stored in list \p out (this is true by default).
97 * \see join_strings()
98 */
99 void GEOGRAM_API split_string(
100 const std::string& in,
101 const std::string& separator,
102 std::vector<std::string>& out,
103 bool skip_empty_fields = true
104 );
105
106 /**
107 * \brief Splits a string into two parts.
108 * \param[in] in the input string to split
109 * \param[in] separator the separator character
110 * \param[in] left the part of the input string on the left
111 * of the separator or the empty string if the separator
112 * did not appear in the input string
113 * \param[in] right the right of the input string on the left
114 * of the separator or the empty string if the separator
115 * did not appear in the input string
116 * \retval true if the separator was found in the input string
117 * \retval false otherwise
118 */
119 bool GEOGRAM_API split_string(
120 const std::string& in,
121 char separator,
122 std::string& left,
123 std::string& right
124 );
125
126
127 /**
128 * \brief Splits a string into two parts.
129 * \param[in] in the input string to split
130 * \param[in] separator the separator string
131 * \param[in] left the part of the input string on the left
132 * of the separator or the empty string if the separator
133 * did not appear in the input string
134 * \param[in] right the right of the input string on the left
135 * of the separator or the empty string if the separator
136 * did not appear in the input string
137 * \retval true if the separator was found in the input string
138 * \retval false otherwise
139 */
140 bool GEOGRAM_API split_string(
141 const std::string& in,
142 const std::string& separator,
143 std::string& left,
144 std::string& right
145 );
146
147 /**
148 * \brief Join multiple strings
149 * \details Joins all the strings in list \p in into a single string
150 * with each element separated by the given \p separator character.
151 * \param[in] in the list of strings to join
152 * \param[in] separator the separator character
153 * \return the joined string
154 * \see split_string()
155 */
156 std::string GEOGRAM_API join_strings(
157 const std::vector<std::string>& in,
158 char separator
159 );
160
161 /**
162 * \brief Join multiple strings
163 * \details Joins all the strings in list \p in into a single string
164 * with each element separated by the given \p separator string.
165 * \param[in] in the list of strings to join
166 * \param[in] separator the separator string (can be an empty string)
167 * \return the joined string
168 * \see split_string()
169 */
170 std::string GEOGRAM_API join_strings(
171 const std::vector<std::string>& in,
172 const std::string& separator
173 );
174
175 /**
176 * \brief Converts a string to lowercase
177 * \details The conversion is done in place in the string \p s.
178 * \param[in,out] s The string to convert
179 * \see to_uppercase()
180 */
181 std::string GEOGRAM_API to_lowercase(const std::string& s);
182
183 /**
184 * \brief Converts a string to uppercase
185 * \details The conversion is done in place in the string \p s.
186 * \param[in,out] s The string to convert
187 * \see to_lowercase()
188 */
189 std::string GEOGRAM_API to_uppercase(const std::string& s);
190
191 /**
192 * \brief Creates a one char string
193 * \param[in] c the character to convert to a string
194 * \return a string that contains characater \p c
195 */
196 inline std::string char_to_string(char c) {
197 char s[2];
198 s[0] = c;
199 s[1] = '\0';
200 return std::string(s);
201 }
202
203 /**
204 * \brief Adds quotes to a string
205 * \details Adds character \p quote at the beginning and the end of
206 * string \p s and returns the resulting string.
207 * \param[in] s the string to quote
208 * \param[in] quotes the quoting char (default is '"')
209 * \return the quoted string
210 */
211 std::string GEOGRAM_API quote(
212 const std::string& s, char quotes = '\"'
213 );
214
215 /**
216 * \brief Checks if a string starts with a substring
217 * \param[in] haystack the input string
218 * \param[in] needle the substring to check
219 * \return \c true if \p haystack starts with \p needle, \c false
220 * otherwise.
221 */
222 bool GEOGRAM_API string_starts_with(
223 const std::string& haystack, const std::string& needle
224 );
225
226 /**
227 * \brief Checks if a string ends with a substring
228 * \param[in] haystack the input string
229 * \param[in] needle the substring to check
230 * \return \c true if \p haystack ends with \p needle, \c false
231 * otherwise.
232 */
233 bool GEOGRAM_API string_ends_with(
234 const std::string& haystack, const std::string& needle
235 );
236
237 /**
238 * \brief Removes a prefix from a string
239 * \param[in] s the string
240 * \param[in] prefix the prefix to be removed
241 * \return a new string with the prefix removed or \p s if \p s
242 * does not starts with \p prefix
243 */
244 GEO_NODISCARD inline std::string remove_prefix(
245 const std::string& s, const std::string& prefix
246 ) {
247 if(string_starts_with(s, prefix)) {
248 return s.substr(prefix.length());
249 }
250 return s;
251 }
252
253 /**
254 * \brief Removes a suffix from a string
255 * \param[in] s the string
256 * \param[in] suffix the suffix
257 * \return a new string with the suffix removed or \p s if \p s
258 * does not ends with \p suffix
259 */
260 GEO_NODISCARD inline std::string remove_suffix(
261 const std::string& s, const std::string& suffix
262 ) {
263 if(string_ends_with(s, suffix)) {
264 return s.substr(0, s.length() - suffix.length());
265 }
266 return s;
267 }
268
269 /**
270 * \brief Removes the leading and trailing spaces from a string
271 * \param[in] s a const reference to a string
272 * \return the same string as \p s with leading and trailing
273 * spaces removed
274 */
275 GEO_NODISCARD inline std::string trim_spaces(const std::string& s) {
276 size_t first = s.find_first_not_of(' ');
277 if (first == std::string::npos) {
278 return s;
279 }
280 size_t last = s.find_last_not_of(' ');
281 return s.substr(first, (last - first + 1));
282 }
283
284 /**
285 * \brief Creates a string from a format string and additional
286 * arguments. Works like sprintf()
287 * \param[in] format the format string
288 */
289 std::string GEOGRAM_API format(const char* format, ...)
290 #ifndef GOMGEN
291 #ifdef GEO_COMPILER_GCC_FAMILY
292 // Tells the compiler that format is a printf-like format
293 // string, so that it can check that the arguments match
294 // the format string and bark at you if it is not the case.
295 __attribute__ ((__format__(printf, 1, 2)))
296 #endif
297 #endif
298 ;
299
300 /**
301 * \brief Converts a time in seconds into a human-readable string
302 * \param[in] HMS_only if set, always returns a hh:mm:ss string,
303 * else returns the time in seconds and a (hh:mm:ss) if time is
304 * greater or equal to one minute.
305 */
306 std::string GEOGRAM_API format_time(double seconds, bool HMS_only=false);
307
308 /**
309 * \brief Converts a typed value to a string
310 * \param[in] value the typed value to convert
311 * \return a string that contain the stringified form of the value
312 */
313 template <class T>
314 10776 inline std::string to_string(const T& value) {
315 10776 std::ostringstream out;
316 // Makes sure that double-precision number are displayed
317 // with a sufficient number of digits. This is important
318 // to avoid losing precision when using ASCII files.
319 out << std::setprecision(17);
320
1/2
✓ Branch 1 taken 5769 times.
✗ Branch 2 not taken.
10776 out << value;
321 10776 return out.str();
322 10776 }
323
324
325 /**
326 * \brief Specialization for unsigned char for making sure it is
327 * displayed as number instead of char. One of the reasons
328 * is that Attribute<bool> is represented as Attribute<unsigned char>
329 * internally.
330 */
331 template <>
332 inline std::string to_string(const unsigned char& value) {
333 std::ostringstream out;
334 out << int(value);
335 return out.str();
336 }
337
338 template <>
339 inline std::string to_string(const signed char& value) {
340 std::ostringstream out;
341 out << int(value);
342 return out.str();
343 }
344
345
346 /**
347 * \brief Converts a typed value to a string for display.
348 * \details Does not keep all significant digits for floating point
349 * numbers.
350 * \param[in] value the typed value to convert
351 * \return a string that contain the stringified form of the value
352 */
353 template <class T>
354 inline std::string to_display_string(const T& value) {
355 return to_string(value);
356 }
357
358
359 /**
360 * \brief Converts a typed value to a string for display.
361 * \details Does not keep all significant digits for floating point
362 * numbers.
363 * \param[in] value the typed value to convert
364 * \return a string that contain the stringified form of the value
365 */
366 template <>
367 657 inline std::string to_display_string(const double& value) {
368 657 std::ostringstream out;
369
1/2
✓ Branch 1 taken 657 times.
✗ Branch 2 not taken.
657 out << value;
370 657 return out.str();
371 657 }
372
373 /**
374 * \brief Converts a typed value to a string for display.
375 * \details Does not keep all significant digits for floating point
376 * numbers.
377 * \param[in] value the typed value to convert
378 * \return a string that contain the stringified form of the value
379 */
380 template <>
381 inline std::string to_display_string(const float& value) {
382 std::ostringstream out;
383 out << value;
384 return out.str();
385 }
386
387 /**
388 * \brief Converts a boolean value to a string
389 * \param[in] value the boolean value to convert
390 * \return string \c "true" if the boolean value is true
391 * or \c "false" if the boolean value is false
392 */
393 template <>
394 inline std::string to_string(const bool& value) {
395
5/6
✓ Branch 0 taken 141 times.
✓ Branch 1 taken 593 times.
✓ Branch 4 taken 377 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 105 times.
✓ Branch 9 taken 109 times.
1404 return value ? "true" : "false";
396 }
397
398 /**
399 * \brief Conversion exception
400 * \details This exception is thrown by the conversion functions
401 * to_bool(), to_int() and to_double() when a string cannot be
402 * converted to the desired type.
403 */
404 class GEOGRAM_API ConversionError : public std::logic_error {
405 public:
406 /**
407 * \brief Constructs a conversion exception
408 * \param[in] s the input string that could not be converted
409 * \param[in] type the expected destination type
410 */
411 ConversionError(const std::string& s, const std::string& type);
412
413 /**
414 * \brief Gets the string identifying the exception
415 */
416 const char* what() const GEO_NOEXCEPT override;
417 };
418
419 /**
420 * \brief Converts a C string to a typed value
421 * \details This is a generic version that uses a std::istringstream
422 * to extract the value from the string. This function is specialized
423 * for integral types to reach the maximum efficiency.
424 * \param[in] s the source string
425 * \param[out] value the typed value
426 * \retval true if the conversion was successful
427 * \retval false otherwise
428 */
429 template <class T>
430 inline bool from_string(const char* s, T& value) {
431 std::istringstream in(s);
432 return (in >> value) && (in.eof() || ((in >> std::ws) && in.eof()));
433 }
434
435 /**
436 * \brief Converts a std::string to a typed value
437 * \details This is a generic version that uses a std::istringstream
438 * to extract the value from the string. This function is specialized
439 * for integral types to reach the maximum efficiency.
440 * \param[in] s the source string
441 * \param[out] value the typed value
442 * \retval true if the conversion was successful
443 * \retval false otherwise
444 */
445 template <class T>
446 inline bool from_string(const std::string& s, T& value) {
447 4119 return from_string(s.c_str(), value);
448 }
449
450 /**
451 * \brief Converts a string to a double value
452 * \param[in] s the source string
453 * \param[out] value the double value
454 * \retval true if the conversion was successful
455 * \retval false otherwise
456 */
457 template <>
458 1609295 inline bool from_string(const char* s, double& value) {
459 1609295 errno = 0;
460 char* end;
461 1609295 value = strtod(s, &end);
462
4/6
✓ Branch 0 taken 1609292 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1609292 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1609292 times.
1609295 return end != s && *end == '\0' && errno == 0;
463 }
464
465 /**
466 * \brief Converts a string to a signed integer value
467 * \param[in] s the source string
468 * \param[out] value the integer value
469 * \retval true if the conversion was successful
470 * \retval false otherwise
471 */
472 template <typename T>
473 413 inline bool string_to_signed_integer(const char* s, T& value) {
474 413 errno = 0;
475 char* end;
476 #ifdef GEO_OS_WINDOWS
477 Numeric::int64 v = _strtoi64(s, &end, 10);
478 #else
479 413 Numeric::int64 v = strtoll(s, &end, 10);
480 #endif
481 413 if(
482
4/6
✓ Branch 0 taken 412 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 412 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 412 times.
✗ Branch 5 not taken.
413 end != s && *end == '\0' && errno == 0 &&
483
1/2
✓ Branch 0 taken 412 times.
✗ Branch 1 not taken.
412 v >= std::numeric_limits<T>::min() &&
484 v <= std::numeric_limits<T>::max()
485 ) {
486 412 value = static_cast<T>(v);
487 412 return true;
488 }
489
490 return false;
491 }
492
493 /**
494 * \brief Converts a string to a Numeric::int8 value
495 * \see string_to_signed_integer()
496 */
497 template <>
498 inline bool from_string(const char* s, Numeric::int8& value) {
499 return string_to_signed_integer(s, value);
500 }
501
502 /**
503 * \brief Converts a string to a Numeric::int16 value
504 * \see string_to_signed_integer()
505 */
506 template <>
507 inline bool from_string(const char* s, Numeric::int16& value) {
508 return string_to_signed_integer(s, value);
509 }
510
511 /**
512 * \brief Converts a string to a Numeric::int32 value
513 * \see string_to_signed_integer()
514 */
515 template <>
516 inline bool from_string(const char* s, Numeric::int32& value) {
517 413 return string_to_signed_integer(s, value);
518 }
519
520 /**
521 * \brief Converts a string to a Numeric::int64 value
522 */
523 template <>
524 619590 inline bool from_string(const char* s, Numeric::int64& value) {
525 619590 errno = 0;
526 char* end;
527 #ifdef GEO_OS_WINDOWS
528 value = _strtoi64(s, &end, 10);
529 #else
530 619590 value = strtoll(s, &end, 10);
531 #endif
532
4/6
✓ Branch 0 taken 619589 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 619589 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 619589 times.
619590 return end != s && *end == '\0' && errno == 0;
533 }
534
535 /**
536 * \brief Converts a string to a unsigned integer value
537 * \param[in] s the source string
538 * \param[out] value the integer value
539 * \retval true if the conversion was successful
540 * \retval false otherwise
541 */
542 template <typename T>
543 435 inline bool string_to_unsigned_integer(const char* s, T& value) {
544 435 errno = 0;
545 char* end;
546 #ifdef GEO_OS_WINDOWS
547 Numeric::uint64 v = _strtoui64(s, &end, 10);
548 #else
549 435 Numeric::uint64 v = strtoull(s, &end, 10);
550 #endif
551 435 if(
552
4/8
✓ Branch 0 taken 435 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 435 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 435 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 435 times.
✗ Branch 7 not taken.
435 end != s && *end == '\0' && errno == 0 &&
553 v <= std::numeric_limits<T>::max()
554 ) {
555 435 value = static_cast<T>(v);
556 435 return true;
557 }
558
559 return false;
560 }
561
562 /**
563 * \brief Converts a string to a Numeric::uint8 value
564 * \see string_to_unsigned_integer()
565 */
566 template <>
567 inline bool from_string(const char* s, Numeric::uint8& value) {
568 return string_to_unsigned_integer(s, value);
569 }
570
571 /**
572 * \brief Converts a string to a Numeric::uint16 value
573 * \see string_to_unsigned_integer()
574 */
575 template <>
576 inline bool from_string(const char* s, Numeric::uint16& value) {
577 return string_to_unsigned_integer(s, value);
578 }
579
580 /**
581 * \brief Converts a string to a Numeric::uint32 value
582 * \see string_to_unsigned_integer()
583 */
584 template <>
585 inline bool from_string(const char* s, Numeric::uint32& value) {
586 435 return string_to_unsigned_integer(s, value);
587 }
588
589 /**
590 * \brief Converts a string to a Numeric::uint64 value
591 */
592 template <>
593 532896 inline bool from_string(const char* s, Numeric::uint64& value) {
594 532896 errno = 0;
595 char* end;
596 #ifdef GEO_OS_WINDOWS
597 value = _strtoui64(s, &end, 10);
598 #else
599 532896 value = strtoull(s, &end, 10);
600 #endif
601
4/6
✓ Branch 0 taken 532896 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 532894 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 532894 times.
532896 return end != s && *end == '\0' && errno == 0;
602 }
603
604 /**
605 * \brief Converts a string to a boolean value
606 * \details
607 * Legal values for the true boolean value are "true","True" and "1".
608 * Legal values for the false boolean value are "false","False" and "0".
609 * \param[in] s the source string
610 * \param[out] value the boolean value
611 * \retval true if the conversion was successful
612 * \retval false otherwise
613 */
614 template <>
615 3708 inline bool from_string(const char* s, bool& value) {
616
2/2
✓ Branch 0 taken 2457 times.
✓ Branch 1 taken 1251 times.
3708 if(strcmp(s, "true") == 0 ||
617
1/2
✓ Branch 0 taken 2457 times.
✗ Branch 1 not taken.
2457 strcmp(s, "True") == 0 ||
618
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2457 times.
2457 strcmp(s, "1") == 0
619 ) {
620 1251 value = true;
621 1251 return true;
622 }
623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2457 times.
2457 if(strcmp(s, "false") == 0 ||
624 strcmp(s, "False") == 0 ||
625 strcmp(s, "0") == 0
626 ) {
627 2457 value = false;
628 2457 return true;
629 }
630 return false;
631 }
632
633 /**
634 * \brief Converts a string to an int
635 * \details If the entire string cannot be
636 * converted to an int, the function
637 * throws an exception ConversionError.
638 * \param[in] s the source string
639 * \return the extracted integer value
640 * \see ConversionError
641 */
642 308 inline int to_int(const std::string& s) {
643 int value;
644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 308 times.
308 if(!from_string(s, value)) {
645 throw ConversionError(s, "integer");
646 }
647 308 return value;
648 }
649
650 /**
651 * \brief Converts a string to an unsigned int
652 * \details If the entire string cannot be
653 * converted to an unsigned int, the function
654 * throws an exception ConversionError.
655 * \param[in] s the source string
656 * \return the extracted integer value
657 * \see ConversionError
658 */
659 435 inline unsigned int to_uint(const std::string& s) {
660 unsigned int value;
661
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 435 times.
435 if(!from_string(s, value)) {
662 throw ConversionError(s, "integer");
663 }
664 435 return value;
665 }
666
667 /**
668 * \brief Converts a string to a double
669 * \details If the entire string cannot be
670 * converted to a double, the function
671 * throws an exception ConversionError.
672 * \param[in] s the source string
673 * \return the extracted double value
674 * \see ConversionError
675 */
676 274 inline double to_double(const std::string& s) {
677 double value;
678
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 274 times.
274 if(!from_string(s, value)) {
679 throw ConversionError(s, "double");
680 }
681 274 return value;
682 }
683
684 /**
685 * \brief Converts a string to a boolean
686 * \details If the entire string cannot be
687 * converted to a boolean, the function
688 * throws an exception ConversionError.
689 * \param[in] s the source string
690 * \return the extracted boolean value
691 * \see ConversionError
692 */
693 3392 inline bool to_bool(const std::string& s) {
694 bool value;
695
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3392 times.
3392 if(!from_string(s, value)) {
696 throw ConversionError(s, "boolean");
697 }
698 3392 return value;
699 }
700
701 /**
702 * \brief Converts a wide char string into an UTF8 string.
703 * \param[in] in the input null-terminated wide-char string.
704 * \return the UTF8-encoded string in a std::string.
705 */
706 std::string GEOGRAM_API wchar_to_UTF8(const wchar_t* in);
707 }
708 }
709
710 #endif
711