Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/util/Convert.h @ 12123

Last change on this file since 12123 was 11098, checked in by muemart, 9 years ago

Minor C++11 improvements:

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