Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/doc/src/libraries/util/Convert.h @ 7364

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

Elaborated documentation for util/Convert.

  • Property svn:eol-style set to native
File size: 16.2 KB
RevLine 
[1052]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
[1505]3 *                    > www.orxonox.net <
[1052]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:
[2087]23 *      Reto Grieder
[1791]24 *      Fabian 'x3n' Landau
[1052]25 *      Benjamin Grauer
[1505]26 *   Co-authors:
[1052]27 *      ...
28 */
29
[7364]30/** Functions that convert values between different types.
31@file
32@par Usage
33    There are three ways to use the conversions depending on what you need. <br>
34    - For simply converting values without having to know whether the conversion
35      was successful (for instance float --> string), use orxonox::multi_cast
36      which effectively works exactly like static_cast, etc.
37      @code
38        float input = 42.0;
39        std::string output = multi_cast<std::string>(input);
40      @endcode
41    - If you care about whether the conversion was successful,
42      use orxonox::convertValue.
43      @code
44        std::string input("3.4");
45        float output;
46        bool success = convertValue(&output, input);
47      @endcode
48    - If you care about success and if you can also feed a fallback value,
49      use orxonox::convertValue.
50      @code
51        std::string input("3.4");
52        float output;
53        bool success = convertValue(&output, input, 0.0);
54      @endcode
55    - If success doesn't matter but you can feed a fallback value,
56      use orxonox::getConvertedValue.
57      @code
58        std::string input("3.4");
59        float output = getConvertedValue(input, 0.0);
60      @endcode
61@details
62    The back end of these functions are the actual implementations for the
63    specific conversions, for instance from Ogre::Vector3 to std::string and
64    vice versa. Some of them also use the iostream operators. <br>
65    The real deal is evaluating which function is needed for a conversion based
66    on the input and output type. But there are lots of catches in conjunction
67    with templates which explains why there are so many functions in this file.
68    <br> <br>
69@par Search Order
70    Finding the right function is governed by priority rules: <br>
71    -# (Partial) template specialisation of orxonox::ConverterExplicit::convert()
72    -# An implicit conversion. This includes 'FooBar' to 'int' if FooBar
73       defines operator int() or float().
74    -# Global or member operators for iostream when converting from or
75       to std::string (and FROM const char*)
76    -# (Partial) template specialisation of orxonox::ConverterFallback::convert()
77    -# Fallback function that displays "Could not convert value" with type
78       information obtained from typeid().
79@par Implementing conversion functions
80    To do that you probably need to know a thing or two about the types
81    involved. So, get ready with that. <br>
82    Usually the best way to do it is specialising of the orxonox::ConverterFallback
83    template, like this:
84    @code
85    template <>
86    struct _UtilExport ConverterFallback<std::string, MyType>
87    {
88        static bool convert(MyType* output, const std::string& input)
89        {
90           ...
91           return success;
92        }
93    };
94    @endcode
95    This piece of code converts an std::string to MyType and returns whether the
96    conversion was successful. You can also use partial specialisation.<br>
97    The advantage with orxonox::ConverterFallback is that it has a low priority
98    meaning that when there is an implicit conversion or an iostream method, that
99    comes first and you don't have to deal with it (and the accompanying
100    function call ambiguity). <br>
101    However sometimes you would like to explicitely replace such a conversion.
102    That's where orxonox::ConverterExplicit comes in handy (for instance we
103    replaced the operator << conversions for Ogre::VectorX with our own functions).
104@note
105    There has to be an exact type match when using template specialisations. <br>
106    Template specialisations can be defined after including this file.
107    But any implicit cast function or iostream operator has to be included
108    in this file!
109@par Understanding the Code
110    In order to understand how the templates work, it is probably best to study
111    the functions in order of calling. There are lots of comments explaining
112    what happens, but you'll need to understand a deal about partial template
113    specialisation and function headers are matched in C++.
[1052]114*/
115
[2087]116#ifndef _Converter_H__
117#define _Converter_H__
[1052]118
[1062]119#include "UtilPrereqs.h"
120
[1052]121#include <string>
122#include <sstream>
[1837]123#include <typeinfo>
[7266]124#include <loki/TypeManip.h>
[1052]125
[1747]126#include "Debug.h"
[3232]127#include "TemplateUtils.h"
[1064]128
[2171]129namespace orxonox
[1505]130{
[2171]131    ///////////////////
132    // No Conversion //
133    ///////////////////
[1505]134
[7364]135    /// Default template. No conversion available at all.
[2171]136    template <class FromType, class ToType>
137    struct ConverterFallback
[1505]138    {
[3196]139        FORCEINLINE static bool convert(ToType* output, const FromType& input)
[2171]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    };
[2087]146
[7364]147    /// If all else fails, try a dynamic_cast for pointer types.
[2171]148    template <class FromType, class ToType>
149    struct ConverterFallback<FromType*, ToType*>
[1505]150    {
[3196]151        FORCEINLINE static bool convert(ToType** output, FromType* const input)
[2087]152        {
[2171]153            ToType* temp = dynamic_cast<ToType*>(input);
154            if (temp)
155            {
156                *output = temp;
157                return true;
158            }
159            else
160                return false;
[2087]161        }
[2171]162    };
[7284]163
164    ////////////
165    // upcast //
166    ////////////
167    namespace detail
168    {
169        // perform a static cast if ToType is a base of FromType
170        template<class ToType, class FromType>
171        FORCEINLINE ToType upcast(FromType input, Loki::Int2Type<true>)
172        {
173            return static_cast<ToType>(input);
174        }
175
176        // return zero if ToType is not a base of FromType
177        template<class ToType, class FromType>
178        FORCEINLINE ToType upcast(FromType input, Loki::Int2Type<false>)
179        {
180            return 0;
181        }
182    }
183
184    // performs an upcast if ToType is a base of FromType, returns zero otherwise
185    template <class ToType, class FromType>
186    FORCEINLINE ToType upcast(FromType input)
187    {
188        enum { probe = ImplicitConversion<FromType, ToType>::exists };
189        return detail::upcast<ToType, FromType>(input, Loki::Int2Type<probe>());
190    }
[2171]191}
[1505]192
193
[2087]194///////////////////////
195// ConverterFallback //
196///////////////////////
197
[7364]198/** Fallback template for stringstream
199@details
200    Neither FromType nor ToType was std::string, therefore
201    delegate to orxonox::ConverterFallback
202*/
[2087]203template <class FromType, class ToType>
204struct ConverterStringStream
[1505]205{
[3196]206    FORCEINLINE static bool convert(ToType* output, const FromType& input)
[1505]207    {
[2171]208        return orxonox::ConverterFallback<FromType, ToType>::convert(output, input);
[1505]209    }
210};
211
212
213/////////////
[2087]214// OStream //
[1505]215/////////////
216
[7364]217/// Extra namespace to avoid exposing the iostream operators in it
[2087]218namespace fallbackTemplates
[1505]219{
[7364]220    /// Fallback operator <<() (delegates to orxonox::ConverterFallback)
[2087]221    template <class FromType>
[3196]222    FORCEINLINE bool operator <<(std::ostream& outstream,  const FromType& input)
[2087]223    {
224        std::string temp;
[2171]225        if (orxonox::ConverterFallback<FromType, std::string>::convert(&temp, input))
[2087]226        {
227            std::operator <<(outstream, temp);
228            return true;
229        }
230        else
231            return false;
232    }
233}
[1505]234
[7364]235/// Template that evaluates whether we can convert to std::string via ostringstream
[1505]236template <class FromType>
[2087]237struct ConverterStringStream<FromType, std::string>
[1505]238{
[3196]239    FORCEINLINE static bool convert(std::string* output, const FromType& input)
[1505]240    {
[2087]241        using namespace fallbackTemplates;
[7364]242        // this operator call only chooses fallbackTemplates::operator<<()
243        // if there's no other fitting function
[1505]244        std::ostringstream oss;
[7364]245        // Note: std::ostream has operator!() to tell whether any error flag was set
[1505]246        if (oss << input)
247        {
248            (*output) = oss.str();
249            return true;
250        }
251        else
252            return false;
253    }
254};
255
[2087]256
257/////////////
258// IStream //
259/////////////
260
261namespace fallbackTemplates
[1625]262{
[7364]263    /// Fallback operator >>() (delegates to orxonox::ConverterFallback)
[2087]264    template <class ToType>
[3196]265    FORCEINLINE bool operator >>(std::istream& instream, ToType& output)
[2087]266    {
[7364]267        std::string input(static_cast<std::istringstream&>(instream).str());
268        return orxonox::ConverterFallback<std::string, ToType>::convert(&output, input);
[2087]269    }
[1625]270}
271
[7364]272/// Template that evaluates whether we can convert from std::string via istringstream
[1505]273template <class ToType>
[2087]274struct ConverterStringStream<std::string, ToType>
[1505]275{
[3196]276    FORCEINLINE static bool convert(ToType* output, const std::string& input)
[1505]277    {
[2087]278        using namespace fallbackTemplates;
[7364]279        // this operator call chooses fallbackTemplates::operator>>()
280        // only if there's no other fitting function
[1505]281        std::istringstream iss(input);
[7364]282        // Note: std::istream has operator!() to tell whether any error flag was set
[1505]283        if (iss >> (*output))
[2087]284        {
[1505]285            return true;
[2087]286        }
[1505]287        else
288            return false;
289    }
290};
291
[2171]292namespace orxonox
[1625]293{
[2171]294    ///////////////////
295    // Implicit Cast //
296    ///////////////////
[1625]297
[7364]298    /// %Template delegates to ::ConverterStringStream
[2171]299    template <class FromType, class ToType>
[7266]300    FORCEINLINE bool convertImplicitely(ToType* output, const FromType& input, Loki::Int2Type<false>)
[1505]301    {
[2171]302        return ConverterStringStream<FromType, ToType>::convert(output, input);
[1505]303    }
304
[7364]305    /// Makes an implicit cast from \a FromType to \a ToType
[2171]306    template <class FromType, class ToType>
[7266]307    FORCEINLINE bool convertImplicitely(ToType* output, const FromType& input, Loki::Int2Type<true>)
[2171]308    {
309        (*output) = static_cast<ToType>(input);
[2087]310        return true;
[1505]311    }
312
313
[2171]314    ////////////////////////////////
315    // ConverterExplicit Fallback //
316    ////////////////////////////////
[1505]317
[7364]318    /** Default template if no orxonox::ConverterExplicit is available
319    @details
320        Evaluates whether \a FromType can be implicitly converted to \a ToType
321        by the use the ImplicitConversion magic.
322    */
[2171]323    template <class FromType, class ToType>
324    struct ConverterExplicit
325    {
[3234]326        enum { probe = ImplicitConversion<FromType, ToType>::exists };
[3196]327        FORCEINLINE static bool convert(ToType* output, const FromType& input)
[2171]328        {
[7364]329            // Use the probe's value to delegate to the right function
[7266]330            return convertImplicitely(output, input, Loki::Int2Type<probe>());
[2171]331        }
332    };
[2087]333
334
[2171]335    //////////////////////
336    // Public Functions //
337    //////////////////////
[2087]338
[2171]339    /**
340    @brief
341        Converts any value to any other as long as there exists a conversion.
[7364]342    @details
[2171]343        Otherwise, the conversion will generate a runtime warning and return false.
[7364]344    @see Convert.h
[7297]345    @param output
346        A pointer to the variable where the converted value will be stored
347    @param input
348        The original value
[2171]349    */
350    template <class FromType, class ToType>
[3196]351    FORCEINLINE bool convertValue(ToType* output, const FromType& input)
[2171]352    {
353        return ConverterExplicit<FromType, ToType>::convert(output, input);
354    }
[2087]355
[2171]356    // Calls convertValue and returns true if the conversion was successful.
357    // Otherwise the fallback is used.
358    /**
359    @brief
360        Converts any value to any other as long as there exists a conversion.
361        Otherwise, the conversion will generate a runtime warning and return false.
[7364]362        If the conversion doesn't succeed, \a fallback is written to \a output.
363    @see Convert.h
[7297]364    @param output
365        A pointer to the variable where the converted value will be stored
366    @param input
367        The original value
[2171]368    @param fallback
369        A default value that gets written to '*output' if there is no conversion.
370    */
371    template<class FromType, class ToType>
[3196]372    FORCEINLINE bool convertValue(ToType* output, const FromType& input, const ToType& fallback)
[1625]373    {
[2171]374        if (convertValue(output, input))
375            return true;
376        else
377        {
378            (*output) = fallback;
379            return false;
380        }
[1625]381    }
382
[2171]383    template<class FromType, class ToType>
[3196]384    FORCEINLINE ToType getConvertedValue(const FromType& input)
[1505]385    {
[2171]386        ToType output;
387        convertValue(&output, input);
388        return output;
[1505]389    }
[2171]390
[7364]391    /// Directly returns the converted value, but uses the fallback on failure. @see convertValue
[2171]392    template<class FromType, class ToType>
[3196]393    FORCEINLINE ToType getConvertedValue(const FromType& input, const ToType& fallback)
[1505]394    {
[2171]395        ToType output;
396        convertValue(&output, input, fallback);
397        return output;
[1505]398    }
[2171]399
[7364]400    /**
401    @brief
402        Converts any value to any other as long as there exists a conversion.
403    @details
404        Use exactly the way you use static_cast, etc. <br>
405        A failed conversion will return a default instance of \a ToType
406        (possibly uninitialised)
407    @see Convert.h
408    @param input
409        The original value
410    */
[2171]411    template<class ToType, class FromType>
[3196]412    FORCEINLINE ToType multi_cast(const FromType& input)
[1505]413    {
[2171]414        ToType output;
415        convertValue(&output, input);
416        return output;
[1505]417    }
418
[2171]419    ////////////////////////////////
420    // Special string conversions //
421    ////////////////////////////////
422
[7364]423    /// Delegates conversion from const char* to std::string
[2171]424    template <class ToType>
425    struct ConverterExplicit<const char*, ToType>
[1625]426    {
[3196]427        FORCEINLINE static bool convert(ToType* output, const char* input)
[1625]428        {
[2171]429            return convertValue<std::string, ToType>(output, input);
[1625]430        }
[2171]431    };
432
[7364]433    /// Conversion would exhibit ambiguous << or >> operators when using iostream
[2171]434    template <>
435    struct ConverterExplicit<char, std::string>
436    {
[3196]437        FORCEINLINE static bool convert(std::string* output, const char input)
[1625]438        {
[6417]439            *output = input;
[2171]440            return true;
[1625]441        }
[2171]442    };
[7364]443    /// Conversion would exhibit ambiguous << or >> operators when using iostream
[2171]444    template <>
[7364]445    template <>
[2171]446    struct ConverterExplicit<unsigned char, std::string>
447    {
[3196]448        FORCEINLINE static bool convert(std::string* output, const unsigned char input)
[2171]449        {
[6417]450            *output = input;
[2171]451            return true;
452        }
453    };
[7364]454    /// Conversion would exhibit ambiguous << or >> operators when using iostream
[2171]455    template <>
[7364]456    template <>
[2171]457    struct ConverterExplicit<std::string, char>
458    {
[6417]459        FORCEINLINE static bool convert(char* output, const std::string& input)
[2171]460        {
[6417]461            if (!input.empty())
[2171]462                *output = input[0];
463            else
464                *output = '\0';
465            return true;
466        }
467    };
[7364]468    /// Conversion would exhibit ambiguous << or >> operators when using iostream
[2171]469    template <>
470    struct ConverterExplicit<std::string, unsigned char>
471    {
[6417]472        FORCEINLINE static bool convert(unsigned char* output, const std::string& input)
[2171]473        {
[6417]474            if (!input.empty())
[2171]475                *output = input[0];
476            else
477                *output = '\0';
478            return true;
479        }
480    };
[1625]481
[2171]482
[7364]483    /// Conversion from bool to std::string
[2171]484    template <>
485    struct ConverterExplicit<bool, std::string>
486    {
[3196]487        FORCEINLINE static bool convert(std::string* output, const bool& input)
[2171]488        {
489            if (input)
490              *output = "true";
491            else
492              *output = "false";
[2662]493            return true;
[2171]494        }
495    };
[1625]496
[7364]497    /// Conversion from std::string to bool
[2171]498    template <>
[7163]499    struct _UtilExport ConverterExplicit<std::string, bool>
[2171]500    {
[7163]501        static bool convert(bool* output, const std::string& input);
[2171]502    };
503}
504
[1052]505#endif /* _Convert_H__ */
Note: See TracBrowser for help on using the repository browser.