Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Convert.h done. Has yet to be tested with gcc. And the comments have to be adapted.

  • Property svn:eol-style set to native
File size: 12.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
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 for fallbackConvert, no conversion possible
138template <class ToType, class FromType>
139inline bool fallbackConversion(ToType* output, FromType input)
140{
141    COUT(2) << "Could not convert value of type " << typeid(FromType).name()
142            << " to type " << typeid(ToType).name() << std::endl;
143    return false;
144}
145
146
147/////////////////////
148// fallbackConvert //
149/////////////////////
150
151// Class template used when << or >> was not available with string conversions
152// It is required to call not yet declared fallbackConvert functions.
153template <class ToType, class FromType>
154struct ConverterFallbackTemplate
155{
156    static bool convert(ToType* output, const FromType& input)
157    {
158        return fallbackConversion(output, input);
159    }
160};
161
162// Default template for stringstream
163template <class ToType, class FromType>
164struct ConverterStringStream
165{
166    static bool convert(ToType* output, const FromType& input)
167    {
168        return fallbackConversion(output, input);
169    }
170};
171
172
173/////////////
174// OStream //
175/////////////
176
177namespace fallbackTemplates
178{
179    template <class FromType>
180    inline bool operator <<(std::ostream& outstream,  const FromType& input)
181    {
182        std::string temp;
183        if (ConverterFallbackTemplate<std::string, FromType>::convert(&temp, input))
184        {
185            std::operator <<(outstream, temp);
186            return true;
187        }
188        else
189            return false;
190    }
191}
192
193// template that evaluates whether OStringStream is possible for conversions to std::string
194template <class FromType>
195struct ConverterStringStream<std::string, FromType>
196{
197    static bool convert(std::string* output, const FromType& input)
198    {
199        using namespace fallbackTemplates;
200        std::ostringstream oss;
201        if (oss << input)
202        {
203            (*output) = oss.str();
204            return true;
205        }
206        else
207            return false;
208    }
209};
210
211
212/////////////
213// IStream //
214/////////////
215
216namespace fallbackTemplates
217{
218    template <class ToType>
219    inline bool operator >>(std::istream& instream, ToType& output)
220    {
221        return ConverterFallbackTemplate<ToType, std::string>
222            ::convert(&output, static_cast<std::istringstream&>(instream).str());
223    }
224}
225
226// template that evaluates whether IStringStream is possible for conversions from std::string
227template <class ToType>
228struct ConverterStringStream<ToType, std::string>
229{
230    static bool convert(ToType* output, const std::string& input)
231    {
232        using namespace fallbackTemplates;
233        std::istringstream iss(input);
234        if (iss >> (*output))
235        {
236            return true;
237        }
238        else
239            return false;
240    }
241};
242
243
244///////////////////
245// Implicit Cast //
246///////////////////
247
248// We can cast implicitely
249template <class ToType, class FromType>
250inline bool convertImplicitely(ToType* output, const FromType& input, ::Int2Type<true>)
251{
252    (*output) = static_cast<ToType>(input);
253    return true;
254}
255
256// static cast no possible, try stringstream conversion next
257template <class ToType, class FromType>
258inline bool convertImplicitely(ToType* output, const FromType& input, ::Int2Type<false>)
259{
260    return ConverterStringStream<ToType, FromType>::convert(output, input);
261}
262
263
264///////////////////////
265// Explicit Fallback //
266///////////////////////
267
268template <class ToType, class FromType>
269inline bool explicitConversion(ToType* output, const FromType& input)
270{
271    // try implict conversion by probing first because we use '...' instead of a template
272    const bool probe = conversionTests::ImplicitConversion<FromType, ToType>::exists;
273    return convertImplicitely(output, input, ::Int2Type<probe>());
274}
275
276
277// Indirect calls over a class template so we can call functions not yet declared.
278template <class ToType, class FromType>
279struct ConverterExplicitTemplate
280{
281    static bool convert(ToType* output, const FromType& input)
282    {
283        return explicitConversion(output, input);
284    }
285};
286
287
288//////////////////////
289// Public Functions //
290//////////////////////
291
292/**
293@brief
294    Converts any value to any other as long as there exists a conversion.
295    Otherwise, the conversion will generate a runtime warning.
296    For information about the different conversion methods (user defined too), see the section
297    'Actual conversion sequence' in this file above.
298@note
299    This function is only a fallback if there is no appropriate 'convertValue' function.
300*/
301template <class ToType, class FromType>
302inline bool convertValue(ToType* output, const FromType& input)
303{
304    return ConverterExplicitTemplate<ToType, FromType>::convert(output, input);
305}
306
307// For compatibility reasons. The same, but with capital ConvertValue
308template<class FromType, class ToType>
309inline bool ConvertValue(ToType* output, const FromType& input)
310{
311    return convertValue(output, input);
312}
313
314// Calls convertValue and returns true if the conversion was successful.
315// Otherwise the fallback is used.
316template<class FromType, class ToType>
317inline bool ConvertValue(ToType* output, const FromType& input, const ToType& fallback)
318{
319    if (convertValue(output, input))
320        return true;
321
322    (*output) = fallback;
323    return false;
324}
325
326// Directly returns the converted value, even if the conversion was not successful.
327template<class FromType, class ToType>
328inline ToType getConvertedValue(const FromType& input)
329{
330    ToType output;
331    ConvertValue(&output, input);
332    return output;
333}
334
335// Directly returns the converted value, but uses the fallback on failure.
336template<class FromType, class ToType>
337inline ToType getConvertedValue(const FromType& input, const ToType& fallback)
338{
339    ToType output;
340    ConvertValue(&output, input, fallback);
341    return output;
342}
343
344// Like getConvertedValue, but the template argument order is in reverse.
345// That means you can call it exactly like static_cast<ToType>(fromTypeValue).
346template<class ToType, class FromType>
347inline ToType conversion_cast(const FromType& input)
348{
349    ToType output;
350    ConvertValue(&output, input);
351    return output;
352}
353
354// Like conversion_cast above, but uses a fallback on failure.
355template<class ToType, class FromType>
356inline ToType conversion_cast(const FromType& input, const ToType& fallback)
357{
358    ToType output;
359    ConvertValue(&output, input, fallback);
360    return output;
361}
362
363
364////////////////////////////////
365// Special string conversions //
366////////////////////////////////
367
368// delegate conversion from const char* to std::string
369template <class ToType>
370inline bool explicitConversion(ToType* output, const char* input)
371{
372    return convertValue<ToType, std::string>(output, input);
373}
374
375// These conversions would exhibit ambiguous << or >> operators when using stringstream
376inline bool explicitConversion(std::string* output, const char input)
377{
378    *output = std::string(1, input);
379    return true;
380}
381inline bool explicitConversion(std::string* output, const unsigned char input)
382{
383    *output = std::string(1, input);
384    return true;
385}
386inline bool explicitConversion(char* output, const std::string input)
387{
388    if (input != "")
389        *output = input[0];
390    else
391        *output = '\0';
392    return true;
393}
394inline bool explicitConversion(unsigned char* output, const std::string input)
395{
396    if (input != "")
397        *output = input[0];
398    else
399        *output = '\0';
400    return true;
401}
402
403#endif /* _Convert_H__ */
Note: See TracBrowser for help on using the repository browser.