Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/objecthierarchy/src/util/Convert.h @ 2016

Last change on this file since 2016 was 2016, checked in by rgrieder, 16 years ago

Merged the changes in Convert.h from core3 branch back.
If it doesn't work, feel free to revert. I have however more or less checked every possible conversion on gcc (tardis) and msvc with a script.

  • Property svn:eol-style set to native
File size: 13.5 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
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:
23 *      Reto Grieder
24 *      Fabian 'x3n' Landau
25 *      Benjamin Grauer
26 *   Co-authors:
27 *      ...
28 */
29
30/*!
31    @file
32    @brief Definition and Implementation of the Convert class.
33*/
34
35#ifndef _Converter_H__
36#define _Converter_H__
37
38#include "UtilPrereqs.h"
39
40#include <string>
41#include <sstream>
42#include <istream>
43#include <ostream>
44#include <typeinfo>
45
46#include "Debug.h"
47#include "String.h"
48
49// GCC generates warnings when implicitely casting from float to int for instance.
50// This is however exactly what convertValue does, so we need to suppress these warnings.
51// They only occur when using the ImplicitConversion template.
52#if ORXONOX_COMPILER == ORXONOX_COMPILER_GNUC
53#  pragma GCC system_header
54#endif
55
56
57///////////////////////////////////////////////
58// Static detection for conversion functions //
59///////////////////////////////////////////////
60
61/* The idea to use the sizeof() operator on return functions to determine function existance
62   is described in 'Modern C++ design' by Alexandrescu (2001). */
63
64// disable warnings about possible loss of data
65#if ORXONOX_COMPILER == ORXONOX_COMPILER_MSVC
66#  pragma warning(push)
67#  pragma warning(disable:4244)
68#endif
69
70template <class FromType, class ToType>
71class ImplicitConversion
72{
73private:
74    ImplicitConversion(); ImplicitConversion(const ImplicitConversion&); ~ImplicitConversion();
75    // Gets chosen only if there is an implicit conversion from FromType to ToType.
76    static char test(ToType);
77    // Accepts any argument. Why do we not use a template? The reason is that with templates,
78    // the function above is only taken iff it is an exact type match. But since we want to
79    // check for implicit conversion, we have to use the ellipsis.
80    static long long test(...);
81    static FromType object; // helper object to handle private c'tor and d'tor
82public:
83    // test(object) only has 'long long' return type iff the compiler doesn't choose test(...)
84    enum { exists = (sizeof(test(object)) == sizeof(char)) };
85};
86
87#if ORXONOX_COMPILER == ORXONOX_COMPILER_MSVC
88#  pragma warning(pop)
89#endif
90
91
92////////////////////////////////////
93//// ACTUAL CONVERSION SEQUENCE ////
94////////////////////////////////////
95/*
96    There is a distinct priority when choosing the right conversion function:
97    Overwrite:
98    1. (Partial) template specialisation of ConverterExplicit::convert()
99    Fallbacks:
100    2. Any possible implicit conversion. This includes 'FooBar' --> 'int' if FooBar defines operator float().
101    3. Global or member operators for stringstream when converting from or to std::string (or FROM const char*)
102    4. (Partial) template specialisation of ConverterFallback::convert()
103    5. Function that simply displays "Could not convert value" with type information obtained from typeid().
104
105    Notes:
106    There has to be an exact type match when using template specialisations.
107    Template specialisations can be defined after including this file. Any implicit cast function or iostream
108    operator has to be declared BEFORE this file gets parsed.
109
110    Defining your own functions:
111    There are obviously 4 ways to specifiy a user defined conversion. What should I use?
112
113    Usually, ConverterFallback fits quite well. You won't have to deal with the conversion from
114    'MyClass' to 'MyClass' by using another explicit template specialisation to avoid ambiguities.
115
116    However if you want to overwrite an implicit conversion or an iostream operator, you really need to
117    make use of ConverterExplicit.
118*/
119
120namespace
121{
122    //! Little template that maps integers to entire types (Alexandrescu 2001)
123    template <int I>
124    struct Int2Type { };
125}
126
127
128///////////////////
129// No Conversion //
130///////////////////
131
132// Default template. No conversion available at all.
133template <class FromType, class ToType>
134struct ConverterFallback
135{
136    static bool convert(ToType* output, const FromType& input)
137    {
138        COUT(2) << "Could not convert value of type " << typeid(FromType).name()
139                << " to type " << typeid(ToType).name() << std::endl;
140        return false;
141    }
142};
143
144
145///////////////////////
146// ConverterFallback //
147///////////////////////
148
149// Default template for stringstream
150template <class FromType, class ToType>
151struct ConverterStringStream
152{
153    static bool convert(ToType* output, const FromType& input)
154    {
155        return ConverterFallback<FromType, ToType>::convert(output, input);
156    }
157};
158
159
160/////////////
161// OStream //
162/////////////
163
164namespace fallbackTemplates
165{
166    template <class FromType>
167    inline bool operator <<(std::ostream& outstream,  const FromType& input)
168    {
169        std::string temp;
170        if (ConverterFallback<FromType, std::string>::convert(&temp, input))
171        {
172            std::operator <<(outstream, temp);
173            return true;
174        }
175        else
176            return false;
177    }
178}
179
180// template that evaluates whether we can convert to std::string via ostringstream
181template <class FromType>
182struct ConverterStringStream<FromType, std::string>
183{
184    static bool convert(std::string* output, const FromType& input)
185    {
186        using namespace fallbackTemplates;
187        // this operator call only chooses fallbackTemplates::operator<< if there's no other fitting function
188        std::ostringstream oss;
189        if (oss << input)
190        {
191            (*output) = oss.str();
192            return true;
193        }
194        else
195            return false;
196    }
197};
198
199
200/////////////
201// IStream //
202/////////////
203
204namespace fallbackTemplates
205{
206    template <class ToType>
207    inline bool operator >>(std::istream& instream, ToType& output)
208    {
209        return ConverterFallback<std::string, ToType>
210            ::convert(&output, static_cast<std::istringstream&>(instream).str());
211    }
212}
213
214// template that evaluates whether we can convert from std::string via ostringstream
215template <class ToType>
216struct ConverterStringStream<std::string, ToType>
217{
218    static bool convert(ToType* output, const std::string& input)
219    {
220        using namespace fallbackTemplates;
221        std::istringstream iss(input);
222        // this operator call only chooses fallbackTemplates::operator>> if there's no other fitting function
223        if (iss >> (*output))
224        {
225            return true;
226        }
227        else
228            return false;
229    }
230};
231
232
233///////////////////
234// Implicit Cast //
235///////////////////
236
237// implicit cast not possible, try stringstream conversion next
238template <class FromType, class ToType>
239inline bool convertImplicitely(ToType* output, const FromType& input, ::Int2Type<false>)
240{
241    return ConverterStringStream<FromType, ToType>::convert(output, input);
242}
243
244// We can cast implicitely
245template <class FromType, class ToType>
246inline bool convertImplicitely(ToType* output, const FromType& input, ::Int2Type<true>)
247{
248    (*output) = static_cast<ToType>(input);
249    return true;
250}
251
252
253////////////////////////////////
254// ConverterExplicit Fallback //
255////////////////////////////////
256
257// Default template if no specialisation is available
258template <class FromType, class ToType>
259struct ConverterExplicit
260{
261    static bool convert(ToType* output, const FromType& input)
262    {
263        // Try implict cast and probe first. If a simple cast is not possible, it will not compile
264        // We therefore have to out source it into another template function
265        const bool probe = ImplicitConversion<FromType, ToType>::exists;
266        return convertImplicitely(output, input, ::Int2Type<probe>());
267    }
268};
269
270
271//////////////////////
272// Public Functions //
273//////////////////////
274
275/**
276@brief
277    Converts any value to any other as long as there exists a conversion.
278    Otherwise, the conversion will generate a runtime warning and return false.
279    For information about the different conversion methods (user defined too), see the section
280    'Actual conversion sequence' in this file above.
281*/
282template <class FromType, class ToType>
283inline bool convertValue(ToType* output, const FromType& input)
284{
285    return ConverterExplicit<FromType, ToType>::convert(output, input);
286}
287
288// For compatibility reasons. The same, but with capital ConvertValue
289template<class FromType, class ToType>
290inline bool ConvertValue(ToType* output, const FromType& input)
291{
292    return convertValue(output, input);
293}
294
295// Calls convertValue and returns true if the conversion was successful.
296// Otherwise the fallback is used.
297/**
298@brief
299    Converts any value to any other as long as there exists a conversion.
300    Otherwise, the conversion will generate a runtime warning and return false.
301    For information about the different conversion methods (user defined too), see the section
302    'Actual conversion sequence' in this file above.
303    If the conversion doesn't succeed, 'fallback' is written to '*output'.
304@param fallback
305    A default value that gets written to '*output' if there is no conversion.
306*/
307template<class FromType, class ToType>
308inline bool convertValue(ToType* output, const FromType& input, const ToType& fallback)
309{
310    if (convertValue(output, input))
311        return true;
312    else
313    {
314        (*output) = fallback;
315        return false;
316    }
317}
318
319// for compatibility reason. (capital 'c' in ConvertValue)
320template<class FromType, class ToType>
321inline bool ConvertValue(ToType* output, const FromType& input, const ToType& fallback)
322{
323    return convertValue(output, input, fallback);
324}
325
326// Directly returns the converted value, even if the conversion was not successful.
327template<class FromType, class ToType>
328inline ToType getConvertedValue(const FromType& input)
329{
330    ToType output;
331    convertValue(&output, input);
332    return output;
333}
334
335// Directly returns the converted value, but uses the fallback on failure.
336template<class FromType, class ToType>
337inline ToType getConvertedValue(const FromType& input, const ToType& fallback)
338{
339    ToType output;
340    convertValue(&output, input, fallback);
341    return output;
342}
343
344// Like getConvertedValue, but the template argument order is in reverse.
345// That means you can call it exactly like static_cast<ToType>(fromTypeValue).
346template<class ToType, class FromType>
347inline ToType conversion_cast(const FromType& input)
348{
349    ToType output;
350    convertValue(&output, input);
351    return output;
352}
353
354// convert to string Shortcut
355template <class FromType>
356std::string convertToString(FromType value)
357{
358  return getConvertedValue<FromType, std::string>(value);
359}
360
361// convert from string Shortcut
362template <class ToType>
363ToType convertFromString(std::string str)
364{
365  return getConvertedValue<std::string, ToType>(str);
366}
367
368////////////////////////////////
369// Special string conversions //
370////////////////////////////////
371
372// Delegate conversion from const char* to std::string
373template <class ToType>
374struct ConverterExplicit<const char*, ToType>
375{
376    static bool convert(ToType* output, const char* input)
377    {
378        return convertValue<std::string, ToType>(output, input);
379    }
380};
381
382// These conversions would exhibit ambiguous << or >> operators when using stringstream
383template <>
384struct ConverterExplicit<char, std::string>
385{
386    static bool convert(std::string* output, const char input)
387    {
388        *output = std::string(1, input);
389        return true;
390    }
391};
392template <>
393struct ConverterExplicit<unsigned char, std::string>
394{
395    static bool convert(std::string* output, const unsigned char input)
396    {
397        *output = std::string(1, input);
398        return true;
399    }
400};
401template <>
402struct ConverterExplicit<std::string, char>
403{
404    static bool convert(char* output, const std::string input)
405    {
406        if (input != "")
407            *output = input[0];
408        else
409            *output = '\0';
410        return true;
411    }
412};
413template <>
414struct ConverterExplicit<std::string, unsigned char>
415{
416    static bool convert(unsigned char* output, const std::string input)
417    {
418        if (input != "")
419            *output = input[0];
420        else
421            *output = '\0';
422        return true;
423    }
424};
425
426
427// bool to std::string
428template <>
429struct ConverterExplicit<bool, std::string>
430{
431    static bool convert(std::string* output, const bool& input)
432    {
433        if (input)
434          *output = "true";
435        else
436          *output = "false";
437        return false;
438    }
439};
440
441// std::string to bool
442template <>
443struct ConverterExplicit<std::string, bool>
444{
445    static bool convert(bool* output, const std::string& input)
446    {
447        std::string stripped = getLowercase(removeTrailingWhitespaces(input));
448        if (stripped == "true" || stripped == "on" || stripped == "yes")
449        {
450          *output = true;
451          return true;
452        }
453        else if (stripped == "false" || stripped == "off" || stripped == "no")
454        {
455          *output = false;
456          return true;
457        }
458
459        std::istringstream iss(input);
460        if (iss >> (*output))
461            return true;
462        else
463            return false;
464    }
465};
466
467#endif /* _Convert_H__ */
Note: See TracBrowser for help on using the repository browser.