Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

gcc didn't like me..

  • Property svn:eol-style set to native
File size: 12.2 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
60// disable warnings about possible loss of data
61#if ORXONOX_COMPILER == ORXONOX_COMPILER_MSVC
62#  pragma warning(push)
63#  pragma warning(disable:4244)
64#endif
65
66namespace conversionTests
67{
68    // A struct that is guaranteed to be larger than any return type of our conversion functions.
69    // So we simply add all the sizes of the return types plus a little bit more.
70    struct VeryBigStruct
71    {
72        char intSize[sizeof(int)];
73        char addingMore[4096]; // just to be sure ;)
74    };
75
76    template <class FromType, class ToType>
77    class ImplicitConversion
78    {
79    private:
80        ImplicitConversion(); ImplicitConversion(const ImplicitConversion&); ~ImplicitConversion();
81        // Gets chosen only if there is an implicit conversion from FromType to ToType.
82        static int test(ToType);
83        // Accepts any argument. Why do we not use a template? The reason is that with templates,
84        // the function above is only taken iff it is an exact type match. But since we want to
85        // check for implicit conversion, we have to use the ellipsis.
86        static VeryBigStruct   test(...);
87        static FromType object; // helper object to handle private c'tor and d'tor
88    public:
89        // test(object) only has 'VerySmallStruct' return type iff the compiler doesn't choose test(...)
90        enum { exists = !(sizeof(test(object)) == sizeof(VeryBigStruct)) };
91    };
92}
93
94#if ORXONOX_COMPILER == ORXONOX_COMPILER_MSVC
95#  pragma warning(pop)
96#endif
97
98
99////////////////////////////////////
100//// ACTUAL CONVERSION SEQUENCE ////
101////////////////////////////////////
102/*
103    There is a distinct priority when choosing the right conversion function:
104    Overwrites:
105    1. (Partial) template specialisation of ConverterExplicit::convert
106    2. Global functions convertValue(ToType* output, const FromType input)
107    Fallbacks:
108    3. Any possible implicit conversion. This includes FooBar --> int if FooBar defines operator float().
109    4. Global or member operators for stringstream when converting from or to std::string (or FROM const char*)
110    5. Function that simply displays "Could not convert value" with information obtained from typeid().
111
112    A note: There has to be an exact type match (or according to the rules of template spec.) except for 3.
113
114    There are obviously a lot of ways to specifiy a user defined conversion. What should I use?
115    When using any non-template function based conversion (implicit conversion, convertValue, << or >>)
116    then you should consider that this function has to be defined prior to including this file.
117    If you do not whish do do that, you will have to spcecialsize the ConverterExplicit template.
118    There is a not so obvious advantage of the other way (non-template): You could declare each conversion function
119    in the Prereqs file of the corresponding library. Then you can use the conversion everywhere.
120    This is not possible with template specialisations even when defining them in this file (You would create
121    a circular dependency when using a class from Core for instance, because complete template specialisations
122    get compiled anyway (there is no template parameter)).
123*/
124
125namespace
126{
127    // little template that maps to ints 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.
138template <class ToType, class FromType>
139struct 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
150///////////////////////
151// ConverterFallback //
152///////////////////////
153
154// Default template for stringstream
155template <class ToType, class FromType>
156struct ConverterStringStream
157{
158    static bool convert(ToType* output, const FromType& input)
159    {
160        return ConverterFallback<ToType, FromType>::convert(output, input);
161    }
162};
163
164
165/////////////
166// OStream //
167/////////////
168
169namespace fallbackTemplates
170{
171    template <class FromType>
172    inline bool operator <<(std::ostream& outstream,  const FromType& input)
173    {
174        std::string temp;
175        if (ConverterFallback<std::string, FromType>::convert(&temp, input))
176        {
177            std::operator <<(outstream, temp);
178            return true;
179        }
180        else
181            return false;
182    }
183}
184
185// template that evaluates whether OStringStream is possible for conversions to std::string
186template <class FromType>
187struct ConverterStringStream<std::string, FromType>
188{
189    static bool convert(std::string* output, const FromType& input)
190    {
191        using namespace fallbackTemplates;
192        std::ostringstream oss;
193        if (oss << input)
194        {
195            (*output) = oss.str();
196            return true;
197        }
198        else
199            return false;
200    }
201};
202
203
204/////////////
205// IStream //
206/////////////
207
208namespace fallbackTemplates
209{
210    template <class ToType>
211    inline bool operator >>(std::istream& instream, ToType& output)
212    {
213        return ConverterFallback<ToType, std::string>
214            ::convert(&output, static_cast<std::istringstream&>(instream).str());
215    }
216}
217
218// template that evaluates whether IStringStream is possible for conversions from std::string
219template <class ToType>
220struct ConverterStringStream<ToType, std::string>
221{
222    static bool convert(ToType* output, const std::string& input)
223    {
224        using namespace fallbackTemplates;
225        std::istringstream iss(input);
226        if (iss >> (*output))
227        {
228            return true;
229        }
230        else
231            return false;
232    }
233};
234
235
236///////////////////
237// Implicit Cast //
238///////////////////
239
240// static cast no possible, try stringstream conversion next
241template <class ToType, class FromType>
242inline bool convertImplicitely(ToType* output, const FromType& input, ::Int2Type<false>)
243{
244    return ConverterStringStream<ToType, FromType>::convert(output, input);
245}
246
247// We can cast implicitely
248template <class ToType, class FromType>
249inline bool convertImplicitely(ToType* output, const FromType& input, ::Int2Type<true>)
250{
251    (*output) = static_cast<ToType>(input);
252    return true;
253}
254
255
256///////////////////////
257// Explicit Fallback //
258///////////////////////
259
260// Default template if no specialisation is available
261template <class ToType, class FromType>
262struct ConverterExplicit
263{
264    static bool convert(ToType* output, const FromType& input)
265    {
266        // try implict conversion by probing first because we use '...' instead of a template
267        const bool probe = conversionTests::ImplicitConversion<FromType, ToType>::exists;
268        return convertImplicitely(output, input, ::Int2Type<probe>());
269    }
270};
271
272
273//////////////////////
274// Public Functions //
275//////////////////////
276
277/**
278@brief
279    Converts any value to any other as long as there exists a conversion.
280    Otherwise, the conversion will generate a runtime warning.
281    For information about the different conversion methods (user defined too), see the section
282    'Actual conversion sequence' in this file above.
283@note
284    This function is only a fallback if there is no appropriate 'convertValue' function.
285*/
286template <class ToType, class FromType>
287inline bool convertValue(ToType* output, const FromType& input)
288{
289    return ConverterExplicit<ToType, FromType>::convert(output, input);
290}
291
292// For compatibility reasons. The same, but with capital ConvertValue
293template<class FromType, class ToType>
294inline bool ConvertValue(ToType* output, const FromType& input)
295{
296    return convertValue(output, input);
297}
298
299// Calls convertValue and returns true if the conversion was successful.
300// Otherwise the fallback is used.
301template<class FromType, class ToType>
302inline bool ConvertValue(ToType* output, const FromType& input, const ToType& fallback)
303{
304    if (convertValue(output, input))
305        return true;
306
307    (*output) = fallback;
308    return false;
309}
310
311// Directly returns the converted value, even if the conversion was not successful.
312template<class FromType, class ToType>
313inline ToType getConvertedValue(const FromType& input)
314{
315    ToType output;
316    ConvertValue(&output, input);
317    return output;
318}
319
320// Directly returns the converted value, but uses the fallback on failure.
321template<class FromType, class ToType>
322inline ToType getConvertedValue(const FromType& input, const ToType& fallback)
323{
324    ToType output;
325    ConvertValue(&output, input, fallback);
326    return output;
327}
328
329// Like getConvertedValue, but the template argument order is in reverse.
330// That means you can call it exactly like static_cast<ToType>(fromTypeValue).
331template<class ToType, class FromType>
332inline ToType conversion_cast(const FromType& input)
333{
334    ToType output;
335    ConvertValue(&output, input);
336    return output;
337}
338
339// Like conversion_cast above, but uses a fallback on failure.
340template<class ToType, class FromType>
341inline ToType conversion_cast(const FromType& input, const ToType& fallback)
342{
343    ToType output;
344    ConvertValue(&output, input, fallback);
345    return output;
346}
347
348
349////////////////////////////////
350// Special string conversions //
351////////////////////////////////
352
353// delegate conversion from const char* to std::string
354template <class ToType>
355struct ConverterExplicit<ToType, const char*>
356{
357    static bool convert(ToType* output, const char* input)
358    {
359        return convertValue<ToType, std::string>(output, input);
360    }
361};
362
363// These conversions would exhibit ambiguous << or >> operators when using stringstream
364template <>
365struct ConverterExplicit<std::string, char>
366{
367    static bool convert(std::string* output, const char input)
368    {
369        *output = std::string(1, input);
370        return true;
371    }
372};
373template <>
374struct ConverterExplicit<std::string, unsigned char>
375{
376    static bool convert(std::string* output, const unsigned char input)
377    {
378        *output = std::string(1, input);
379        return true;
380    }
381};
382template <>
383struct ConverterExplicit<char, std::string>
384{
385    static bool convert(char* output, const std::string input)
386    {
387        if (input != "")
388            *output = input[0];
389        else
390            *output = '\0';
391        return true;
392    }
393};
394template <>
395struct ConverterExplicit<unsigned char, std::string>
396{
397    static bool convert(unsigned char* output, const std::string input)
398    {
399        if (input != "")
400            *output = input[0];
401        else
402            *output = '\0';
403        return true;
404    }
405};
406
407#endif /* _Convert_H__ */
Note: See TracBrowser for help on using the repository browser.