Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

svn save

  • Property svn:eol-style set to native
File size: 15.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 *      Reto Grieder (direct conversion tests)
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
45#include "Debug.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 the warnings.
49// They only occur in when using the ImplicitConversion template.
50#if ORXONOX_COMPILER == ORXONOX_COMPILER_GNUC
51#  pragma GCC system_header
52#endif
53
54///////////////////////////////////////////////
55// Static detection for conversion functions //
56///////////////////////////////////////////////
57/* The idea to use the sizeof() operator on return functions to determine function existance
58   is described in 'Moder C++ design' by Alexandrescu (2001). */
59
60namespace conversionTests
61{
62    // A struct that is guaranteed to be larger than any return type of our conversion functions.
63    // So we simply add all the sizes of the return types plus a little bit more.
64    struct VeryBigStruct
65    {
66        char intSize[sizeof(int)];
67        char issSize[sizeof(std::istringstream)];
68        char ossSize[sizeof(std::ostringstream)];
69        char boolSize[sizeof(bool)];
70        char addingMore[4096]; // just to be sure ;)
71    };
72}
73
74namespace conversion_another_namespace
75{
76    // We want to keep the templates for the convert functions out of global namespace so that there
77    // are no ambiguities. These templates are never used anyway, they only serve to detect whether
78    // there is a global funciton for a specific conversion or not.
79    // Why the seperate namespace? --> see 'using' statement at the end of conversionTests::
80    //template <class AnyToType, class AnyFromType>
81    //conversionTests::VeryBigStruct explicitConversion(AnyToType* output, const AnyFromType input);
82    //template <class Any, int Dummy>
83    //conversionTests::VeryBigStruct operator<<(std::ostream& outstream, const Any& anything);
84    //template <class Any, int Dummy>
85    //conversionTests::VeryBigStruct operator>>(std::istream& instream,  const Any& anything);
86}
87
88namespace conversionTests
89{
90// disable warnings about possible loss of data
91#if ORXONOX_COMPILER == ORXONOX_COMPILER_MSVC
92#  pragma warning(push)
93#  pragma warning(disable:4244)
94#endif
95
96    template <class FromType, class ToType>
97    class ImplicitConversion
98    {
99    private:
100        ImplicitConversion(); ImplicitConversion(const ImplicitConversion&); ~ImplicitConversion();
101        // Gets chosen only if there is an implicit conversion from FromType to ToType.
102        static int test(ToType);
103        // Accepts any argument. Why do we not use a template? The reason is that with templates,
104        // the function above is only taken iff it is an exact type match. But since we want to
105        // check for implicit conversion, we have to use the ellipsis.
106        static VeryBigStruct   test(...);
107        static FromType object; // helper object to handle private c'tor and d'tor
108    public:
109        // test(object) only has 'VerySmallStruct' return type iff the compiler doesn't choose test(...)
110        enum { exists = !(sizeof(test(object)) == sizeof(VeryBigStruct)) };
111    };
112
113#if ORXONOX_COMPILER == ORXONOX_COMPILER_MSVC
114#  pragma warning(pop)
115#endif
116
117    //template <class FromType, class ToType>
118    //class ExplicitConversion
119    //{
120    //private:
121    //    ExplicitConversion(); ExplicitConversion(const ExplicitConversion&); ~ExplicitConversion();
122    //    static FromType objectFromType; // helper object to handle private c'tor and d'tor
123    //    static ToType   objectToType;   // helper object to handle private c'tor and d'tor
124    //public:
125    //    enum { exists = !(sizeof(explicitConversion(&objectToType, objectFromType)) == sizeof(VeryBigStruct)) };
126    //};
127
128    //template <class Type>
129    //class IStreamOperator
130    //{
131    //    IStreamOperator(); IStreamOperator(const IStreamOperator&); ~IStreamOperator();
132    //    static std::istream istream_; // helper object to perform the '>>' operation
133    //    static Type object;           // helper object to handle private c'tor and d'tor
134    //public:
135    //    enum { exists = !(sizeof(istream_ >> object) == sizeof(VeryBigStruct)) };
136    //};
137
138    //template <class Type>
139    //class OStreamOperator
140    //{
141    //    OStreamOperator(); OStreamOperator(const OStreamOperator&); ~OStreamOperator();
142    //    static std::ostream ostream_; // helper object to perform the '<<' operation
143    //    static Type object;           // helper object to handle private c'tor and d'tor
144    //public:
145    //    enum { exists = !(sizeof(ostream_ << object) == sizeof(VeryBigStruct)) };
146    //};
147
148    // Somehow msvc and gcc don't like it when using function arguments that have a type
149    // in a third namespace. The global namespace functions then get overridden by the
150    // templates above. So we simply put the generic ones in a another namespace.
151    // Note: DON'T place this statement before a class template. That would trigger
152    //       a heavy bug (namespace leakage) in msvc 2005.
153    //using namespace conversion_another_namespace;
154}
155
156
157////////////////////////////////////
158//// ACTUAL CONVERSION SEQUENCE ////
159////////////////////////////////////
160/*
161    There is a distinct priority when choosing the right conversion function:
162    Overwrites:
163    1. (Partial) template specialisation of ConverterExplicit::convert
164    2. Global functions explicitConversion(ToType* output, const FromType input)
165    Fallbacks:
166    3. Any possible implicit conversion. This includes FooBar --> int if FooBar defines operator float().
167    4. Global or member operators for stringstream when converting from or to std::string (or FROM const char*)
168    5. Function that simply displays "Could not convert value" with information obtained from typeid().
169
170    A note: There has to be an exact type match (or according to the rules of template spec.) except for 3.
171
172    There are obviously a lot of ways to specifiy a user defined conversion. What should I use?
173    When using any non-template function based conversion (implicit conversion, explicitConversion, << or >>)
174    then you should consider that this function has to be defined prior to including this file.
175    If you do not whish do do that, you will have to spcecialsize the ConverterExplicit template.
176    There is a not so obvious advantage of the other way (non-template): You could declare each conversion function
177    in the Prereqs file of the corresponding library. Then you can use the conversion everywhere.
178    This is not possible with template specialisations even when defining them in this file (You would create
179    a circular dependency when using a class from Core for instance, because complete template specialisations
180    get compiled anyway (there is no template parameter)).
181*/
182
183namespace
184{
185    // little template that maps to ints to entire types (Alexandrescu 2001)
186    template <int I>
187    struct Int2Type { };
188}
189
190
191///////////////////
192// No Conversion //
193///////////////////
194
195namespace conversion
196{
197    // Default template for stringtream, no Conversion possible
198    template <class ToType, class FromType>
199    struct ConverterSS
200    {
201        static bool convert(ToType* output, FromType input)
202        {
203            COUT(2) << "Could not convert value of type " << typeid(FromType).name()
204                    << " to type " << typeid(ToType).name() << std::endl;
205            return false;
206        }
207    };
208}
209
210
211/////////////
212// OStream //
213/////////////
214
215namespace fallbackTemplates
216{
217    template <class Any>
218    bool operator <<(std::ostream& outstream,  const Any& anything)
219    {
220        COUT(2) << "Could not convert value of type " << typeid(Any).name()
221                << " to std::string" << std::endl;
222        return false;
223    }
224}
225
226namespace conversion
227{
228    //using namespace fallbackTemplates;
229    // template that evaluates whether OStringStream is possible for conversions to std::string
230    template <class FromType>
231    struct ConverterSS<std::string, FromType>
232    {
233        // probe for '<<' ostream operator
234        static bool convert(std::string* output, const FromType& input)
235        {
236            std::ostringstream oss;
237            if (oss << input)
238            {
239                (*output) = oss.str();
240                return true;
241            }
242            else
243                return false;
244        }
245    };
246
247}
248
249
250/////////////
251// IStream //
252/////////////
253
254namespace fallbackTemplates
255{
256    template <class Any>
257    bool operator >>(std::istream& instream,  const Any& anything)
258    {
259        COUT(2) << "Could not convert std::string value to type " << typeid(ToType).name() << std::endl;
260        return false;
261    }
262}
263
264namespace conversion
265{
266    // template that evaluates whether IStringStream is possible for conversions from std::string
267    template <class ToType>
268    struct ConverterSS<ToType, std::string>
269    {
270        static bool convert(ToType* output, const std::string& input)
271        {
272            std::istringstream iss(input);
273            if (iss >> (*output))
274            {
275                return true;
276            }
277            else
278                return false;
279        }
280    };
281
282    using namespace fallbackTemplates;
283}
284
285
286///////////////////
287// Implicit Cast //
288///////////////////
289
290// We can cast implicitely
291template <class ToType, class FromType>
292inline bool convertImplicitely(ToType* output, const FromType& input, ::Int2Type<true>)
293{
294    (*output) = static_cast<ToType>(input);
295    return true;
296}
297// static cast no possible, try stringstream conversion next
298template <class ToType, class FromType>
299inline bool convertImplicitely(ToType* output, const FromType& input, ::Int2Type<false>)
300{
301    return conversion::ConverterSS<ToType, FromType>::convert(output, input);
302}
303
304
305/////////////////////////
306// Explicit conversion //
307/////////////////////////
308
309namespace fallbackTemplates
310{
311    // We want to keep the templates for the convert functions out of global namespace so that there
312    // are no ambiguities. These templates are never used anyway, they only serve to detect whether
313    // there is a global funciton for a specific conversion or not.
314    // Note: Don't put these functions in a separate namespace and add a 'using' directive here.
315    // For MS sake, just don't!
316    template <class ToType, class FromType>
317    bool explicitConversion(ToType* output, const FromType input)
318    {
319        // try implict conversion by probing first because we use '...' instead of a template
320        const bool probe = conversionTests::ImplicitConversion<FromType, ToType>::exists;
321        //using namespace conversion;
322        return convertImplicitely(output, input, ::Int2Type<probe>());
323    }
324}
325
326
327/////////////////////
328// Local Overwrite //
329/////////////////////
330
331// Template that is used when no explicit template specialisation is available.
332// Try explicitConversion() function next.
333template <class ToType, class FromType>
334struct ConverterExplicit
335{
336    static bool convert(ToType* output, const FromType& input)
337    {
338        using namespace fallbackTemplates;
339        return explicitConversion(output, input);
340    }
341};
342
343
344//////////////////////
345// Public Functions //
346//////////////////////
347
348/**
349@brief
350    Converts any value to any other as long as there exits a conversion.
351    Otherwise, the conversion will generate a runtime warning.
352    For information about the different conversion methods (user defined too), see the section
353    'Actual conversion sequence' in this file above.
354*/
355template <class ToType, class FromType>
356inline bool convertValue(ToType* output, const FromType& input)
357{
358    // check whether we can convert one type to the other explicitely via explicit template specialisations
359    return ConverterExplicit<ToType, FromType>::convert(output, input);
360}
361
362// For compatibility reasons. The same, but with capital ConvertValue
363template<class FromType, class ToType>
364inline bool ConvertValue(ToType* output, const FromType& input)
365{
366    return convertValue(output, input);
367}
368
369// Calls convertValue and returns true if the conversion was successful.
370// Otherwise the fallback is used.
371template<class FromType, class ToType>
372inline bool ConvertValue(ToType* output, const FromType& input, const ToType& fallback)
373{
374    if (convertValue(output, input))
375        return true;
376
377    (*output) = fallback;
378    return false;
379}
380
381// Directly returns the converted value, even if the conversion was not successful.
382template<class FromType, class ToType>
383inline ToType getConvertedValue(const FromType& input)
384{
385    ToType output;
386    ConvertValue(&output, input);
387    return output;
388}
389
390// Directly returns the converted value, but uses the fallback on failure.
391template<class FromType, class ToType>
392inline ToType getConvertedValue(const FromType& input, const ToType& fallback)
393{
394    ToType output;
395    ConvertValue(&output, input, fallback);
396    return output;
397}
398
399// Like getConvertedValue, but the template argument order is in reverse.
400// That means you can call it exactly like static_cast<ToType>(fromTypeValue).
401template<class ToType, class FromType>
402inline ToType conversion_cast(const FromType& input)
403{
404    ToType output;
405    ConvertValue(&output, input);
406    return output;
407}
408
409// Like conversion_cast above, but uses a fallback on failure.
410template<class ToType, class FromType>
411inline ToType conversion_cast(const FromType& input, const ToType& fallback)
412{
413    ToType output;
414    ConvertValue(&output, input, fallback);
415    return output;
416}
417
418
419////////////////////////////////
420// Special string conversions //
421////////////////////////////////
422
423// delegate conversion from const char* to std::string
424template <class ToType>
425inline bool explicitConversion(ToType* output, const char* input)
426{
427    return ConverterExplicit<ToType, std::string>::convert(output, input);
428}
429
430// These conversions would exhibit ambiguous << or >> operators when using stringstream
431inline bool explicitConversion(std::string* output, const char input)
432{
433    *output = std::string(1, input);
434    return true;
435}
436inline bool explicitConversion(std::string* output, const unsigned char input)
437{
438    *output = std::string(1, input);
439    return true;
440}
441inline bool explicitConversion(char* output, const std::string input)
442{
443    if (input != "")
444        *output = input[0];
445    else
446        *output = '\0';
447    return true;
448}
449inline bool explicitConversion(unsigned char* output, const std::string input)
450{
451    if (input != "")
452        *output = input[0];
453    else
454        *output = '\0';
455    return true;
456}
457
458#endif /* _Convert_H__ */
Note: See TracBrowser for help on using the repository browser.