Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/util/Convert.h @ 3196

Last change on this file since 3196 was 3196, checked in by rgrieder, 15 years ago

Merged pch branch back to trunk.

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