Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Added convert test for gcc.

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