Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/objecthierarchy/src/util/Convert.h @ 2051

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

Changed conversion_cast≠() to omni_cast≠() and added support for dynamic_cast.
Whenever you want to convert 2 pointers, it uses static_cast if the conversion is implicit (derived to base class) and dynamic_cast otherwise.
There is a drawback however: When static_cast is not possible and at least one of the two pointers is not from a polymorphic class, you get a compiler error that tells you exactly this. But who would want to convert an int* to OrxonoxClass* ?

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