Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core3/src/util/Convert.h @ 1768

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

gcc test commit again

  • Property svn:eol-style set to native
File size: 19.3 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 *      Benjamin Grauer
24 *      Fabian 'x3n' Landau
25 *   Co-authors:
26 *      ...
27 */
28
29/*!
30    @file Convert.h
31    @brief Definition and Implementation of the Convert class.
32*/
33
34#ifndef _Convert_H__
35#define _Convert_H__
36
37#include "UtilPrereqs.h"
38
39#include <string>
40#include <sstream>
41
42#include "Math.h"
43#include "Debug.h"
44
45// Gcc generates warnings when implicitely casting from float to int for instance.
46// This is however exactly what convertValue does, so we need to suppress the warnings.
47// They only occur in when using the ImplicitConversion template.
48#if ORXONOX_COMPILER == ORXONOX_COMPILER_GCC
49#  pragma GCC system_header
50#endif
51
52///////////////////////////////////////////////
53// Static detection for conversion functions //
54///////////////////////////////////////////////
55
56/* The idea to use the sizeof() operator on return functions to determine function existance
57   is described in 'Moder C++ design' by Alexandrescu (2001). */
58
59namespace conversionTests
60{
61    // A struct that is guaranteed to be larger than any return type of our conversion functions.
62    // So we simply add all the sizes of the return types plus a little bit more.
63    struct VeryBigStruct
64    {
65        char intSize[sizeof(int)];
66        char issSize[sizeof(std::istringstream)];
67        char ossSize[sizeof(std::ostringstream)];
68        char boolSize[sizeof(bool)];
69        char addingMore[4096]; // just to be sure ;)
70    };
71}
72
73namespace separate_namespace
74{
75    // We want to keep the templates for the convert functions out of global namespace so that there
76    // are no ambiguities. These templates are never used anyway, they only serve to detect whether
77    // there is a global funciton for a specific conversion or not.
78    // Now why not putting this in namespace 'conversionTests'?
79    // I can not tell for sure, but I think there is a bug in msvc 8. It chooses this general template
80    // (if it was in 'conversionTests') over a better fitting function in global namespace.
81    // The solution is to use another namespace and then apply the 'using' directive in 'conversionTests'.
82    // Unfortunately there is a (somewhat documented) bug in msvc 8 that exposes namespace members
83    // globally when applying the 'using' directive in a namespace. And it so happens that the bug is
84    // triggered in this file. This unwanted global exposure hopefully doesn't cause ambiguities.
85    template <class AnyToType, class AnyFromType>
86    conversionTests::VeryBigStruct explicitConversion(AnyToType* output, const AnyFromType input)
87    {
88        // We have to make sure noboy uses it , so a good compiler error would be nice.
89        *output = (AnyToType)input; // Do not use this function!
90        // gcc does some syntax checking anyway. So return a correct value that is not a temporary.
91            return *(new conversionTests::VeryBigStruct());
92    }
93
94    // These operators simply accept anything but have lower priority than specialisations
95    // or exact-match non-template functions.
96    // They can be identified by the larger return value.
97    // We put these in a seperate namespace to avoid conflicts with ambiguities.
98    template <class Any>
99    conversionTests::VeryBigStruct operator<<(std::ostream& outstream, const Any anything);
100    template <class Any>
101    conversionTests::VeryBigStruct operator>>(std::istream& instream,  const Any anything);
102}
103
104namespace conversionTests
105{
106    // Part of the msvc hack. See above in the namespace for more explanations.
107    using namespace separate_namespace;
108
109    template <class FromType, class ToType>
110    class ImplicitConversion
111    {
112    private:
113        ImplicitConversion(); ImplicitConversion(const ImplicitConversion&); ~ImplicitConversion();
114        // Gets chosen only if there is an implicit conversion from FromType to ToType.
115        static int test(ToType);
116        // Accepts any argument. Why do we not use a template? The reason is that with templates,
117        // the function above is only taken iff it is an exact type match. But since we want to
118        // check for implicit conversion, we have to use the ellipsis.
119        static VeryBigStruct   test(...);
120        static FromType object; // helper object to handle private c'tor and d'tor
121    public:
122        // test(object) has only 'VerySmallStruct' return type iff the compiler doesn't choose test(...)
123        enum { exists = !(sizeof(test(object)) == sizeof(VeryBigStruct)) };
124    };
125
126    template <class FromType, class ToType>
127    class ExplicitConversion
128    {
129    private:
130        ExplicitConversion(); ExplicitConversion(const ExplicitConversion&); ~ExplicitConversion();
131        static FromType objectFromType; // helper object to handle private c'tor and d'tor
132        static ToType   objectToType;   // helper object to handle private c'tor and d'tor
133    public:
134        enum { exists = !(sizeof(explicitConversion(&objectToType, objectFromType)) == sizeof(VeryBigStruct)) };
135    };
136
137    template <class Type>
138    class IStringStreamOperator
139    {
140        IStringStreamOperator(); IStringStreamOperator(const IStringStreamOperator&); ~IStringStreamOperator();
141        static std::istream istream_; // helper object to perform the '>>' operation
142        static Type object;                 // helper object to handle private c'tor and d'tor
143    public:
144        enum { exists = !(sizeof(istream_ >> object) == sizeof(VeryBigStruct)) };
145    };
146
147    template <class Type>
148    class OStringStreamOperator
149    {
150        OStringStreamOperator(); OStringStreamOperator(const OStringStreamOperator&); ~OStringStreamOperator();
151        static std::ostream ostream_; // helper object to perform the '<<' operation
152        static Type object;                 // helper object to handle private c'tor and d'tor
153    public:
154        enum { exists = !(sizeof(ostream_ << object) == sizeof(VeryBigStruct)) };
155    };
156}
157
158/* Shortcuts because we usually don't have a namespace in util/ but need one here for the conversion tests*/
159
160/**
161@brief
162    Checks for an implicit conversion FromType --> TyType.
163    This also works for user defined conversion operators.
164    Usage: ImplicitConversion<FromType, ToType>::exists
165*/
166//template <class FromType, class ToType>
167//struct ImplicitConversion
168//{ enum { exists = conversionTests::asdf::ImplicitConversion<FromType, ToType>::exists }; };
169
170/**
171@brief
172    Checks for an explicit conversion FromType --> TyType via 'explicConversion()' function.
173    There has to e an exact type match for a success!
174    Usage: ExplicitConversion<FromType, ToType>::exists
175*/
176//template <class FromType, class ToType>
177//struct ExplicitConversion
178//{ enum { exists = conversionTests::asdf::ExplicitConversion<FromType, ToType>::exists }; };
179
180/**
181@brief
182    Checks for an explicit conversion std::string --> TyType via >> operator.
183    There has to e an exact type match for a success!
184    Usage: IStringStreamConversion<FromType, ToType>::exists
185*/
186//template <class Type>
187//struct IStringStreamOperator
188//{ enum { exists = conversionTests::asdf::IStringStreamOperator<Type>::exists }; };
189
190/**
191@brief
192    Checks for an explicit conversion std::string --> TyType via << operator.
193    There has to e an exact type match for a success!
194    Usage: OStringStreamConversion<FromType, ToType>::exists
195*/
196//template <class Type>
197//struct OStringStreamOperator
198//{ enum { exists = conversionTests::asdf::OStringStreamOperator<Type>::exists }; };
199
200
201
202////////////////////////////////////
203//// ACTUAL CONVERSION SEQUENCE ////
204////////////////////////////////////
205/*
206    There is a distinct priority when choosing the right conversion function:
207    Overwrites:
208    1. (Partial) template specialisation of ConverterExplicit::convert
209    2. Global functions explicitConversion(ToType* output, const FromType input)
210    Fallbacks:
211    3. Any possible implicit conversion. This includes FooBar --> int if FooBar defines operator float().
212    4. Global or member operators for stringstream when converting from or to std::string (or FROM const char*)
213    5. Function that simply displays "Could not convert value" with information obtained from typeid().
214
215    A note: There has to be an exact type match (or according to the rules of template spec.) except for 3.
216
217    There are obviously a lot of ways to specifiy a user defined conversion. What should I use?
218    When using any non-template function based conversion (implicit conversion, explicitConversion, << or >>)
219    then you should consider that this function has to be defined prior to including this file.
220    If you do not whish do do that, you will have to spcecialsize the ConverterExplicit template.
221    There is a not so obvious advantage of the other way (non-template): You could declare each conversion function
222    in the Prereqs file of the corresponding library. Then you can use the conversion everywhere.
223    This is not possible with template specialisations even when defining them in this file (You would create
224    a circular dependency when using a class from Core for instance, because complete template specialisations
225    get compiled anyway (there is no template parameter)).
226*/
227
228// Put everything in a namespace to avoid unnecessary exposure
229// Note that the textual order of the functions is in reverse.
230namespace conversion
231{
232    // Maps bools to types in order to use function overloading instead of template specialisation (Alexandrescu 2001)
233    template <bool WhetherOrNot>
234    struct ImplicitPossible { };
235    template <bool WhetherOrNot>
236    struct ExplicitPossible { };
237    template <bool WhetherOrNot>
238    struct StringStreamPossible { };
239
240
241    ///////////////////
242    // No Conversion //
243    ///////////////////
244
245    // Default template, no Conversion possible
246    template <class ToType, class FromType, int Dummy>
247    struct ConverterSS
248    {
249        static bool convert(ToType* output, FromType input)
250        {
251            COUT(2) << "Could not convert value of type " << typeid(FromType).name()
252                    << " to type " << typeid(ToType).name() << std::endl;
253            return false;
254        }
255    };
256
257
258    ///////////////////
259    // OStringStream //
260    ///////////////////
261
262    // Conversion via ostringstream
263    template <class FromType>
264    inline bool convertOStringStream(std::string* output, const FromType& input)
265    {
266        std::ostringstream oss;
267        if (oss << input)
268        {
269            (*output) = oss.str();
270            return true;
271        }
272        else
273            return false;
274    }
275
276    // template that evaluates whether OStringStream is possible for conversions to std::string
277    template <class FromType, int Dummy>
278    struct ConverterSS<std::string, FromType, Dummy>
279    {
280        // probe for '<<' stringstream operator
281        static bool convert(std::string* output, const FromType& input)
282        {
283            const bool probe = conversionTests::OStringStreamOperator<FromType>::exists;
284            return convert(output, input, StringStreamPossible<probe>());
285        }
286        // Conversion with ostringstream possible
287        static bool convert(std::string* output, const FromType& input, StringStreamPossible<true>)
288        {
289            return convertOStringStream(output, input);
290        }
291        // Conversion with ostringstream not possible
292        static bool convert(std::string* output, const FromType& input, StringStreamPossible<false>)
293        {
294            COUT(2) << "Could not convert value of type " << typeid(FromType).name()
295                    << " to std::string" << std::endl;
296            return false;
297        }
298    };
299
300
301    ///////////////////
302    // IStringStream //
303    ///////////////////
304
305    // conversion from std::string via istringstream
306    template <class ToType>
307    inline bool convertIStringStream(ToType* output, const std::string& input)
308    {
309        std::istringstream iss(input);
310        if (iss >> (*output))
311        {
312            return true;
313        }
314        else
315            return false;
316    }
317
318    // template that evaluates whether IStringStream is possible for conversions from std::string
319    template <class ToType, int Dummy>
320    struct ConverterSS<ToType, std::string, Dummy>
321    {
322        // probe for '>>' stringstream operator
323        static bool convert(ToType* output, const std::string& input)
324        {
325            const bool probe = conversionTests::IStringStreamOperator<ToType>::exists;
326            return convert(output, input, StringStreamPossible<probe>());
327        }
328        // Conversion with istringstream possible
329        static bool convert(ToType* output, const std::string& input, StringStreamPossible<true>)
330        {
331            return convertIStringStream(output, input);
332        }
333        // Conversion with istringstream not possible
334        static bool convert(ToType* output, const std::string& input, StringStreamPossible<false>)
335        {
336            COUT(2) << "Could not convert std::string value to type " << typeid(ToType).name() << std::endl;
337            return false;
338        }
339    };
340
341
342    ///////////////////
343    // Implicit Cast //
344    ///////////////////
345
346    // We can cast implicitely
347    template <class ToType, class FromType>
348    inline bool convert(ToType* output, const FromType& input, ImplicitPossible<true>)
349    {
350        (*output) = static_cast<ToType>(input);
351        return true;
352    }
353
354    // No implicit cast, leave it up to << and >> via template spcialisation
355    template <class ToType, class FromType>
356    inline bool convert(ToType* output, const FromType& input, ImplicitPossible<false>)
357    {
358        return ConverterSS<ToType, FromType, 0>::convert(output, input);
359    }
360
361
362    /////////////////////////
363    // Explicit Conversion //
364    /////////////////////////
365
366    // We can convert explicitely via function overloading
367    template <class ToType, class FromType>
368    inline bool convert(ToType* output, const FromType& input, ExplicitPossible<true>)
369    {
370        // This function can by anywhere globally!
371        return explicitConversion(output, input);
372    }
373
374    // No explict conversion via explicitConversion(), try implicit cast
375    template <class ToType, class FromType>
376    inline bool convert(ToType* output, const FromType& input, ExplicitPossible<false>)
377    {
378        const bool probe = conversionTests::ImplicitConversion<FromType, ToType>::exists;
379        return convert(output, input, ImplicitPossible<probe>());
380    }
381}
382
383// We usually don't have a namespace in util/ but it would still be desirable to lock
384// everything internal away in namespace conversion.
385// However this template can be specialised everywhere.
386
387// Template that is used when no explicit template specialisation is available
388// try explicitConversion() function next.
389template <class ToType, class FromType>
390struct ConverterExplicit
391{
392    static bool convert(ToType* output, const FromType& input)
393    {
394        // check for explicit conversion via function overloading
395        const bool probe = conversionTests::ExplicitConversion<FromType, ToType>::exists;
396        return conversion::convert(output, input, conversion::ExplicitPossible<probe>());
397    }
398};
399
400
401//////////////////////
402// Public Functions //
403//////////////////////
404
405/**
406@brief
407    Converts any value to any other as long as there exits a conversion.
408    Otherwise, the conversion will generate a runtime warning.
409    For information about the different conversion methods (user defined too), see the section
410    'Actual conversion sequence' in this file above.
411*/
412template <class ToType, class FromType>
413inline bool convertValue(ToType* output, const FromType& input)
414{
415    // check whether we can convert one type to the other explicitely via explicit template specialisations
416    return ConverterExplicit<ToType, FromType>::convert(output, input);
417}
418
419// For compatibility reasons. The same, but with capital ConvertValue
420template<class FromType, class ToType>
421inline bool ConvertValue(ToType* output, const FromType& input)
422{
423    return convertValue(output, input);
424}
425
426// Calls convertValue and returns true if the conversion was successful.
427// Otherwise the fallback is used.
428template<class FromType, class ToType>
429inline bool ConvertValue(ToType* output, const FromType& input, const ToType& fallback)
430{
431    if (convertValue(output, input))
432        return true;
433
434    (*output) = fallback;
435    return false;
436}
437
438// Directly returns the converted value, even if the conversion was not successful.
439template<class FromType, class ToType>
440inline ToType getConvertedValue(const FromType& input)
441{
442    ToType output;
443    ConvertValue(&output, input);
444    return output;
445}
446
447// Directly returns the converted value, but uses the fallback on failure.
448template<class FromType, class ToType>
449inline ToType getConvertedValue(const FromType& input, const ToType& fallback)
450{
451    ToType output;
452    ConvertValue(&output, input, fallback);
453    return output;
454}
455
456// Like getConvertedValue, but the template argument order is in reverse.
457// That means you can call it exactly like static_cast<ToType>(fromTypeValue).
458template<class ToType, class FromType>
459inline ToType conversion_cast(const FromType& input)
460{
461    ToType output;
462    ConvertValue(&output, input);
463    return output;
464}
465
466// Like conversion_cast above, but uses a fallback on failure.
467template<class ToType, class FromType>
468inline ToType conversion_cast(const FromType& input, const ToType& fallback)
469{
470    ToType output;
471    ConvertValue(&output, input, fallback);
472    return output;
473}
474
475
476///////////////////////////////////////
477// Explicit Template Specialisations //
478///////////////////////////////////////
479
480// delegate conversion from const char* to std::string
481template <class ToType>
482struct ConverterExplicit<ToType, const char*>
483{
484    static bool convert(ToType* output, const char* input)
485    {
486        return ConverterExplicit<ToType, std::string>::convert(output, input);
487    }
488};
489
490// These conversions would exhibit ambiguous << or >> operators when using stringstream
491template <> struct ConverterExplicit<std::string, char>
492{
493    static bool convert(std::string* output, const char input)
494    {
495        *output = std::string(1, input);
496        return true;
497    }
498};
499template <> struct ConverterExplicit<std::string, unsigned char>
500{
501    static bool convert(std::string* output, const unsigned char input)
502    {
503        *output = std::string(1, input);
504        return true;
505    }
506};
507template <> struct ConverterExplicit<char, std::string>
508{
509    static bool convert(char* output, const std::string input)
510    {
511        if (input != "")
512            *output = input[0];
513        else
514            *output = '\0';
515        return true;
516    }
517};
518template <> struct ConverterExplicit<unsigned char, std::string>
519{
520    static bool convert(unsigned char* output, const std::string input)
521    {
522        if (input != "")
523            *output = input[0];
524        else
525            *output = '\0';
526        return true;
527    }
528};
529
530#endif /* _Convert_H__ */
Note: See TracBrowser for help on using the repository browser.