| [1052] | 1 | /* | 
|---|
 | 2 |  *   ORXONOX - the hottest 3D action shooter ever to exist | 
|---|
| [1505] | 3 |  *                    > www.orxonox.net < | 
|---|
| [1052] | 4 |  * | 
|---|
 | 5 |  * | 
|---|
 | 6 |  *   License notice: | 
|---|
 | 7 |  * | 
|---|
 | 8 |  *   This program is free software; you can redistribute it and/or | 
|---|
 | 9 |  *   modify it under the terms of the GNU General Public License | 
|---|
 | 10 |  *   as published by the Free Software Foundation; either version 2 | 
|---|
 | 11 |  *   of the License, or (at your option) any later version. | 
|---|
 | 12 |  * | 
|---|
 | 13 |  *   This program is distributed in the hope that it will be useful, | 
|---|
 | 14 |  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
 | 15 |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
 | 16 |  *   GNU General Public License for more details. | 
|---|
 | 17 |  * | 
|---|
 | 18 |  *   You should have received a copy of the GNU General Public License | 
|---|
 | 19 |  *   along with this program; if not, write to the Free Software | 
|---|
 | 20 |  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | 
|---|
 | 21 |  * | 
|---|
 | 22 |  *   Author: | 
|---|
| [2087] | 23 |  *      Reto Grieder | 
|---|
| [1791] | 24 |  *      Fabian 'x3n' Landau | 
|---|
| [1052] | 25 |  *      Benjamin Grauer | 
|---|
| [1505] | 26 |  *   Co-authors: | 
|---|
| [1052] | 27 |  *      ... | 
|---|
 | 28 |  */ | 
|---|
 | 29 |  | 
|---|
 | 30 | /*! | 
|---|
| [2087] | 31 |     @file | 
|---|
| [1052] | 32 |     @brief Definition and Implementation of the Convert class. | 
|---|
 | 33 | */ | 
|---|
 | 34 |  | 
|---|
| [2087] | 35 | #ifndef _Converter_H__ | 
|---|
 | 36 | #define _Converter_H__ | 
|---|
| [1052] | 37 |  | 
|---|
| [1062] | 38 | #include "UtilPrereqs.h" | 
|---|
 | 39 |  | 
|---|
| [1052] | 40 | #include <string> | 
|---|
 | 41 | #include <sstream> | 
|---|
| [1837] | 42 | #include <typeinfo> | 
|---|
| [1052] | 43 |  | 
|---|
| [1747] | 44 | #include "Debug.h" | 
|---|
| [3232] | 45 | #include "TemplateUtils.h" | 
|---|
| [1064] | 46 |  | 
|---|
| [2087] | 47 | //////////////////////////////////// | 
|---|
 | 48 | //// ACTUAL CONVERSION SEQUENCE //// | 
|---|
 | 49 | //////////////////////////////////// | 
|---|
 | 50 | /* | 
|---|
 | 51 |     There is a distinct priority when choosing the right conversion function: | 
|---|
 | 52 |     Overwrite: | 
|---|
 | 53 |     1. (Partial) template specialisation of ConverterExplicit::convert() | 
|---|
 | 54 |     Fallbacks: | 
|---|
 | 55 |     2. Any possible implicit conversion. This includes 'FooBar' --> 'int' if FooBar defines operator float(). | 
|---|
 | 56 |     3. Global or member operators for stringstream when converting from or to std::string (or FROM const char*) | 
|---|
 | 57 |     4. (Partial) template specialisation of ConverterFallback::convert() | 
|---|
 | 58 |     5. Function that simply displays "Could not convert value" with type information obtained from typeid(). | 
|---|
| [1505] | 59 |  | 
|---|
| [2087] | 60 |     Notes: | 
|---|
 | 61 |     There has to be an exact type match when using template specialisations. | 
|---|
 | 62 |     Template specialisations can be defined after including this file. Any implicit cast function or iostream | 
|---|
 | 63 |     operator has to be declared BEFORE this file gets parsed. | 
|---|
| [1505] | 64 |  | 
|---|
| [2087] | 65 |     Defining your own functions: | 
|---|
 | 66 |     There are obviously 4 ways to specifiy a user defined conversion. What should I use? | 
|---|
| [1505] | 67 |  | 
|---|
| [2087] | 68 |     Usually, ConverterFallback fits quite well. You won't have to deal with the conversion from | 
|---|
 | 69 |     'MyClass' to 'MyClass' by using another explicit template specialisation to avoid ambiguities. | 
|---|
| [1505] | 70 |  | 
|---|
| [2087] | 71 |     However if you want to overwrite an implicit conversion or an iostream operator, you really need to | 
|---|
 | 72 |     make use of ConverterExplicit. | 
|---|
 | 73 | */ | 
|---|
 | 74 |  | 
|---|
| [2171] | 75 | namespace orxonox | 
|---|
| [1505] | 76 | { | 
|---|
| [3196] | 77 |     namespace detail | 
|---|
| [2171] | 78 |     { | 
|---|
 | 79 |         //! Little template that maps integers to entire types (Alexandrescu 2001) | 
|---|
 | 80 |         template <int I> | 
|---|
 | 81 |         struct Int2Type { }; | 
|---|
 | 82 |     } | 
|---|
| [1505] | 83 |  | 
|---|
 | 84 |  | 
|---|
| [2171] | 85 |     /////////////////// | 
|---|
 | 86 |     // No Conversion // | 
|---|
 | 87 |     /////////////////// | 
|---|
| [1505] | 88 |  | 
|---|
| [2171] | 89 |     // Default template. No conversion available at all. | 
|---|
 | 90 |     template <class FromType, class ToType> | 
|---|
 | 91 |     struct ConverterFallback | 
|---|
| [1505] | 92 |     { | 
|---|
| [3196] | 93 |         FORCEINLINE static bool convert(ToType* output, const FromType& input) | 
|---|
| [2171] | 94 |         { | 
|---|
 | 95 |             COUT(2) << "Could not convert value of type " << typeid(FromType).name() | 
|---|
 | 96 |                     << " to type " << typeid(ToType).name() << std::endl; | 
|---|
 | 97 |             return false; | 
|---|
 | 98 |         } | 
|---|
 | 99 |     }; | 
|---|
| [2087] | 100 |  | 
|---|
| [2171] | 101 |     // If all else fails, try a dynamic_cast for pointer types. | 
|---|
 | 102 |     template <class FromType, class ToType> | 
|---|
 | 103 |     struct ConverterFallback<FromType*, ToType*> | 
|---|
| [1505] | 104 |     { | 
|---|
| [3196] | 105 |         FORCEINLINE static bool convert(ToType** output, FromType* const input) | 
|---|
| [2087] | 106 |         { | 
|---|
| [2171] | 107 |             ToType* temp = dynamic_cast<ToType*>(input); | 
|---|
 | 108 |             if (temp) | 
|---|
 | 109 |             { | 
|---|
 | 110 |                 *output = temp; | 
|---|
 | 111 |                 return true; | 
|---|
 | 112 |             } | 
|---|
 | 113 |             else | 
|---|
 | 114 |                 return false; | 
|---|
| [2087] | 115 |         } | 
|---|
| [2171] | 116 |     }; | 
|---|
 | 117 | } | 
|---|
| [1505] | 118 |  | 
|---|
 | 119 |  | 
|---|
| [2087] | 120 | /////////////////////// | 
|---|
 | 121 | // ConverterFallback // | 
|---|
 | 122 | /////////////////////// | 
|---|
 | 123 |  | 
|---|
 | 124 | // Default template for stringstream | 
|---|
 | 125 | template <class FromType, class ToType> | 
|---|
 | 126 | struct ConverterStringStream | 
|---|
| [1505] | 127 | { | 
|---|
| [3196] | 128 |     FORCEINLINE static bool convert(ToType* output, const FromType& input) | 
|---|
| [1505] | 129 |     { | 
|---|
| [2171] | 130 |         return orxonox::ConverterFallback<FromType, ToType>::convert(output, input); | 
|---|
| [1505] | 131 |     } | 
|---|
 | 132 | }; | 
|---|
 | 133 |  | 
|---|
 | 134 |  | 
|---|
 | 135 | ///////////// | 
|---|
| [2087] | 136 | // OStream // | 
|---|
| [1505] | 137 | ///////////// | 
|---|
 | 138 |  | 
|---|
| [2087] | 139 | namespace fallbackTemplates | 
|---|
| [1505] | 140 | { | 
|---|
| [2087] | 141 |     template <class FromType> | 
|---|
| [3196] | 142 |     FORCEINLINE bool operator <<(std::ostream& outstream,  const FromType& input) | 
|---|
| [2087] | 143 |     { | 
|---|
 | 144 |         std::string temp; | 
|---|
| [2171] | 145 |         if (orxonox::ConverterFallback<FromType, std::string>::convert(&temp, input)) | 
|---|
| [2087] | 146 |         { | 
|---|
 | 147 |             std::operator <<(outstream, temp); | 
|---|
 | 148 |             return true; | 
|---|
 | 149 |         } | 
|---|
 | 150 |         else | 
|---|
 | 151 |             return false; | 
|---|
 | 152 |     } | 
|---|
 | 153 | } | 
|---|
| [1505] | 154 |  | 
|---|
| [2087] | 155 | // template that evaluates whether we can convert to std::string via ostringstream | 
|---|
| [1505] | 156 | template <class FromType> | 
|---|
| [2087] | 157 | struct ConverterStringStream<FromType, std::string> | 
|---|
| [1505] | 158 | { | 
|---|
| [3196] | 159 |     FORCEINLINE static bool convert(std::string* output, const FromType& input) | 
|---|
| [1505] | 160 |     { | 
|---|
| [2087] | 161 |         using namespace fallbackTemplates; | 
|---|
 | 162 |         // this operator call only chooses fallbackTemplates::operator<< if there's no other fitting function | 
|---|
| [1505] | 163 |         std::ostringstream oss; | 
|---|
 | 164 |         if (oss << input) | 
|---|
 | 165 |         { | 
|---|
 | 166 |             (*output) = oss.str(); | 
|---|
 | 167 |             return true; | 
|---|
 | 168 |         } | 
|---|
 | 169 |         else | 
|---|
 | 170 |             return false; | 
|---|
 | 171 |     } | 
|---|
 | 172 | }; | 
|---|
 | 173 |  | 
|---|
| [2087] | 174 |  | 
|---|
 | 175 | ///////////// | 
|---|
 | 176 | // IStream // | 
|---|
 | 177 | ///////////// | 
|---|
 | 178 |  | 
|---|
 | 179 | namespace fallbackTemplates | 
|---|
| [1625] | 180 | { | 
|---|
| [2087] | 181 |     template <class ToType> | 
|---|
| [3196] | 182 |     FORCEINLINE bool operator >>(std::istream& instream, ToType& output) | 
|---|
| [2087] | 183 |     { | 
|---|
| [2171] | 184 |         return orxonox::ConverterFallback<std::string, ToType> | 
|---|
| [2087] | 185 |             ::convert(&output, static_cast<std::istringstream&>(instream).str()); | 
|---|
 | 186 |     } | 
|---|
| [1625] | 187 | } | 
|---|
 | 188 |  | 
|---|
| [2087] | 189 | // template that evaluates whether we can convert from std::string via ostringstream | 
|---|
| [1505] | 190 | template <class ToType> | 
|---|
| [2087] | 191 | struct ConverterStringStream<std::string, ToType> | 
|---|
| [1505] | 192 | { | 
|---|
| [3196] | 193 |     FORCEINLINE static bool convert(ToType* output, const std::string& input) | 
|---|
| [1505] | 194 |     { | 
|---|
| [2087] | 195 |         using namespace fallbackTemplates; | 
|---|
| [1505] | 196 |         std::istringstream iss(input); | 
|---|
| [2087] | 197 |         // this operator call only chooses fallbackTemplates::operator>> if there's no other fitting function | 
|---|
| [1505] | 198 |         if (iss >> (*output)) | 
|---|
| [2087] | 199 |         { | 
|---|
| [1505] | 200 |             return true; | 
|---|
| [2087] | 201 |         } | 
|---|
| [1505] | 202 |         else | 
|---|
 | 203 |             return false; | 
|---|
 | 204 |     } | 
|---|
 | 205 | }; | 
|---|
 | 206 |  | 
|---|
| [2171] | 207 | namespace orxonox | 
|---|
| [1625] | 208 | { | 
|---|
| [1505] | 209 |  | 
|---|
| [2171] | 210 |     /////////////////// | 
|---|
 | 211 |     // Implicit Cast // | 
|---|
 | 212 |     /////////////////// | 
|---|
| [1625] | 213 |  | 
|---|
| [2171] | 214 |     // implicit cast not possible, try stringstream conversion next | 
|---|
 | 215 |     template <class FromType, class ToType> | 
|---|
| [3196] | 216 |     FORCEINLINE bool convertImplicitely(ToType* output, const FromType& input, detail::Int2Type<false>) | 
|---|
| [1505] | 217 |     { | 
|---|
| [2171] | 218 |         return ConverterStringStream<FromType, ToType>::convert(output, input); | 
|---|
| [1505] | 219 |     } | 
|---|
 | 220 |  | 
|---|
| [2171] | 221 |     // We can cast implicitely | 
|---|
 | 222 |     template <class FromType, class ToType> | 
|---|
| [3196] | 223 |     FORCEINLINE bool convertImplicitely(ToType* output, const FromType& input, detail::Int2Type<true>) | 
|---|
| [2171] | 224 |     { | 
|---|
 | 225 |         (*output) = static_cast<ToType>(input); | 
|---|
| [2087] | 226 |         return true; | 
|---|
| [1505] | 227 |     } | 
|---|
 | 228 |  | 
|---|
 | 229 |  | 
|---|
| [2171] | 230 |     //////////////////////////////// | 
|---|
 | 231 |     // ConverterExplicit Fallback // | 
|---|
 | 232 |     //////////////////////////////// | 
|---|
| [1505] | 233 |  | 
|---|
| [2171] | 234 |     // Default template if no specialisation is available | 
|---|
 | 235 |     template <class FromType, class ToType> | 
|---|
 | 236 |     struct ConverterExplicit | 
|---|
 | 237 |     { | 
|---|
| [3234] | 238 |         enum { probe = ImplicitConversion<FromType, ToType>::exists }; | 
|---|
| [3196] | 239 |         FORCEINLINE static bool convert(ToType* output, const FromType& input) | 
|---|
| [2171] | 240 |         { | 
|---|
 | 241 |             // Try implict cast and probe first. If a simple cast is not possible, it will not compile | 
|---|
 | 242 |             // We therefore have to out source it into another template function | 
|---|
| [3196] | 243 |             return convertImplicitely(output, input, detail::Int2Type<probe>()); | 
|---|
| [2171] | 244 |         } | 
|---|
 | 245 |     }; | 
|---|
| [2087] | 246 |  | 
|---|
 | 247 |  | 
|---|
| [2171] | 248 |     ////////////////////// | 
|---|
 | 249 |     // Public Functions // | 
|---|
 | 250 |     ////////////////////// | 
|---|
| [2087] | 251 |  | 
|---|
| [2171] | 252 |     /** | 
|---|
 | 253 |     @brief | 
|---|
 | 254 |         Converts any value to any other as long as there exists a conversion. | 
|---|
 | 255 |         Otherwise, the conversion will generate a runtime warning and return false. | 
|---|
 | 256 |         For information about the different conversion methods (user defined too), see the section | 
|---|
 | 257 |         'Actual conversion sequence' in this file above. | 
|---|
 | 258 |     */ | 
|---|
 | 259 |     template <class FromType, class ToType> | 
|---|
| [3196] | 260 |     FORCEINLINE bool convertValue(ToType* output, const FromType& input) | 
|---|
| [2171] | 261 |     { | 
|---|
 | 262 |         return ConverterExplicit<FromType, ToType>::convert(output, input); | 
|---|
 | 263 |     } | 
|---|
| [2087] | 264 |  | 
|---|
| [2171] | 265 |     // Calls convertValue and returns true if the conversion was successful. | 
|---|
 | 266 |     // Otherwise the fallback is used. | 
|---|
 | 267 |     /** | 
|---|
 | 268 |     @brief | 
|---|
 | 269 |         Converts any value to any other as long as there exists a conversion. | 
|---|
 | 270 |         Otherwise, the conversion will generate a runtime warning and return false. | 
|---|
 | 271 |         For information about the different conversion methods (user defined too), see the section | 
|---|
 | 272 |         'Actual conversion sequence' in this file above. | 
|---|
 | 273 |         If the conversion doesn't succeed, 'fallback' is written to '*output'. | 
|---|
 | 274 |     @param fallback | 
|---|
 | 275 |         A default value that gets written to '*output' if there is no conversion. | 
|---|
 | 276 |     */ | 
|---|
 | 277 |     template<class FromType, class ToType> | 
|---|
| [3196] | 278 |     FORCEINLINE bool convertValue(ToType* output, const FromType& input, const ToType& fallback) | 
|---|
| [1625] | 279 |     { | 
|---|
| [2171] | 280 |         if (convertValue(output, input)) | 
|---|
 | 281 |             return true; | 
|---|
 | 282 |         else | 
|---|
 | 283 |         { | 
|---|
 | 284 |             (*output) = fallback; | 
|---|
 | 285 |             return false; | 
|---|
 | 286 |         } | 
|---|
| [1625] | 287 |     } | 
|---|
 | 288 |  | 
|---|
| [2171] | 289 |     // Directly returns the converted value, even if the conversion was not successful. | 
|---|
 | 290 |     template<class FromType, class ToType> | 
|---|
| [3196] | 291 |     FORCEINLINE ToType getConvertedValue(const FromType& input) | 
|---|
| [1505] | 292 |     { | 
|---|
| [2171] | 293 |         ToType output; | 
|---|
 | 294 |         convertValue(&output, input); | 
|---|
 | 295 |         return output; | 
|---|
| [1505] | 296 |     } | 
|---|
| [2171] | 297 |  | 
|---|
 | 298 |     // Directly returns the converted value, but uses the fallback on failure. | 
|---|
 | 299 |     template<class FromType, class ToType> | 
|---|
| [3196] | 300 |     FORCEINLINE ToType getConvertedValue(const FromType& input, const ToType& fallback) | 
|---|
| [1505] | 301 |     { | 
|---|
| [2171] | 302 |         ToType output; | 
|---|
 | 303 |         convertValue(&output, input, fallback); | 
|---|
 | 304 |         return output; | 
|---|
| [1505] | 305 |     } | 
|---|
| [2171] | 306 |  | 
|---|
 | 307 |     // Like getConvertedValue, but the template argument order is in reverse. | 
|---|
 | 308 |     // That means you can call it exactly like static_cast<ToType>(fromTypeValue). | 
|---|
 | 309 |     template<class ToType, class FromType> | 
|---|
| [3196] | 310 |     FORCEINLINE ToType multi_cast(const FromType& input) | 
|---|
| [1505] | 311 |     { | 
|---|
| [2171] | 312 |         ToType output; | 
|---|
 | 313 |         convertValue(&output, input); | 
|---|
 | 314 |         return output; | 
|---|
| [1505] | 315 |     } | 
|---|
 | 316 |  | 
|---|
| [2171] | 317 |     //////////////////////////////// | 
|---|
 | 318 |     // Special string conversions // | 
|---|
 | 319 |     //////////////////////////////// | 
|---|
 | 320 |  | 
|---|
 | 321 |     // Delegate conversion from const char* to std::string | 
|---|
 | 322 |     template <class ToType> | 
|---|
 | 323 |     struct ConverterExplicit<const char*, ToType> | 
|---|
| [1625] | 324 |     { | 
|---|
| [3196] | 325 |         FORCEINLINE static bool convert(ToType* output, const char* input) | 
|---|
| [1625] | 326 |         { | 
|---|
| [2171] | 327 |             return convertValue<std::string, ToType>(output, input); | 
|---|
| [1625] | 328 |         } | 
|---|
| [2171] | 329 |     }; | 
|---|
 | 330 |  | 
|---|
 | 331 |     // These conversions would exhibit ambiguous << or >> operators when using stringstream | 
|---|
 | 332 |     template <> | 
|---|
 | 333 |     struct ConverterExplicit<char, std::string> | 
|---|
 | 334 |     { | 
|---|
| [3196] | 335 |         FORCEINLINE static bool convert(std::string* output, const char input) | 
|---|
| [1625] | 336 |         { | 
|---|
| [6394] | 337 |             *output = input; | 
|---|
| [2171] | 338 |             return true; | 
|---|
| [1625] | 339 |         } | 
|---|
| [2171] | 340 |     }; | 
|---|
 | 341 |     template <> | 
|---|
 | 342 |     struct ConverterExplicit<unsigned char, std::string> | 
|---|
 | 343 |     { | 
|---|
| [3196] | 344 |         FORCEINLINE static bool convert(std::string* output, const unsigned char input) | 
|---|
| [2171] | 345 |         { | 
|---|
| [6394] | 346 |             *output = input; | 
|---|
| [2171] | 347 |             return true; | 
|---|
 | 348 |         } | 
|---|
 | 349 |     }; | 
|---|
 | 350 |     template <> | 
|---|
 | 351 |     struct ConverterExplicit<std::string, char> | 
|---|
 | 352 |     { | 
|---|
| [6394] | 353 |         FORCEINLINE static bool convert(char* output, const std::string& input) | 
|---|
| [2171] | 354 |         { | 
|---|
| [6394] | 355 |             if (!input.empty()) | 
|---|
| [2171] | 356 |                 *output = input[0]; | 
|---|
 | 357 |             else | 
|---|
 | 358 |                 *output = '\0'; | 
|---|
 | 359 |             return true; | 
|---|
 | 360 |         } | 
|---|
 | 361 |     }; | 
|---|
 | 362 |     template <> | 
|---|
 | 363 |     struct ConverterExplicit<std::string, unsigned char> | 
|---|
 | 364 |     { | 
|---|
| [6394] | 365 |         FORCEINLINE static bool convert(unsigned char* output, const std::string& input) | 
|---|
| [2171] | 366 |         { | 
|---|
| [6394] | 367 |             if (!input.empty()) | 
|---|
| [2171] | 368 |                 *output = input[0]; | 
|---|
 | 369 |             else | 
|---|
 | 370 |                 *output = '\0'; | 
|---|
 | 371 |             return true; | 
|---|
 | 372 |         } | 
|---|
 | 373 |     }; | 
|---|
| [1625] | 374 |  | 
|---|
| [2171] | 375 |  | 
|---|
 | 376 |     // bool to std::string | 
|---|
 | 377 |     template <> | 
|---|
 | 378 |     struct ConverterExplicit<bool, std::string> | 
|---|
 | 379 |     { | 
|---|
| [3196] | 380 |         FORCEINLINE static bool convert(std::string* output, const bool& input) | 
|---|
| [2171] | 381 |         { | 
|---|
 | 382 |             if (input) | 
|---|
 | 383 |               *output = "true"; | 
|---|
 | 384 |             else | 
|---|
 | 385 |               *output = "false"; | 
|---|
| [2662] | 386 |             return true; | 
|---|
| [2171] | 387 |         } | 
|---|
 | 388 |     }; | 
|---|
| [1625] | 389 |  | 
|---|
| [6394] | 390 |     // Declarations to avoid StringUtils.h include | 
|---|
 | 391 |     _UtilExport std::string removeTrailingWhitespaces(const std::string& str); | 
|---|
 | 392 |     _UtilExport std::string getLowercase(const std::string& str); | 
|---|
 | 393 |  | 
|---|
| [2171] | 394 |     // std::string to bool | 
|---|
 | 395 |     template <> | 
|---|
 | 396 |     struct ConverterExplicit<std::string, bool> | 
|---|
 | 397 |     { | 
|---|
 | 398 |         static bool convert(bool* output, const std::string& input) | 
|---|
 | 399 |         { | 
|---|
| [6394] | 400 |             const std::string& stripped = getLowercase(removeTrailingWhitespaces(input)); | 
|---|
| [2171] | 401 |             if (stripped == "true" || stripped == "on" || stripped == "yes") | 
|---|
 | 402 |             { | 
|---|
| [6394] | 403 |                 *output = true; | 
|---|
 | 404 |                 return true; | 
|---|
| [2171] | 405 |             } | 
|---|
 | 406 |             else if (stripped == "false" || stripped == "off" || stripped == "no") | 
|---|
 | 407 |             { | 
|---|
| [6394] | 408 |                 *output = false; | 
|---|
 | 409 |                 return true; | 
|---|
| [2171] | 410 |             } | 
|---|
 | 411 |  | 
|---|
 | 412 |             std::istringstream iss(input); | 
|---|
 | 413 |             if (iss >> (*output)) | 
|---|
 | 414 |                 return true; | 
|---|
 | 415 |             else | 
|---|
 | 416 |                 return false; | 
|---|
 | 417 |         } | 
|---|
 | 418 |     }; | 
|---|
 | 419 | } | 
|---|
 | 420 |  | 
|---|
| [1052] | 421 | #endif /* _Convert_H__ */ | 
|---|