Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2171 was 2171, checked in by landauf, 15 years ago

merged revisions 2111-2170 from objecthierarchy branch back to trunk.

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