/*
* ORXONOX - the hottest 3D action shooter ever to exist
* > www.orxonox.net <
*
*
* License notice:
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Author:
* Reto Grieder
* Fabian 'x3n' Landau
* Benjamin Grauer
* Co-authors:
* ...
*/
/**
@defgroup Convert Conversion functions
@ingroup Util
*/
/** Functions that convert values between different types.
@file
@ingroup Convert
@par Usage
There are three ways to use the conversions depending on what you need.
- For simply converting values without having to know whether the conversion
was successful (for instance float --> string), use orxonox::multi_cast
which effectively works exactly like static_cast, etc.
@code
float input = 42.0;
std::string output = multi_cast(input);
@endcode
- If you care about whether the conversion was successful,
use orxonox::convertValue.
@code
std::string input("3.4");
float output;
bool success = convertValue(&output, input);
@endcode
- If you care about success and if you can also feed a fallback value,
use orxonox::convertValue.
@code
std::string input("3.4");
float output;
bool success = convertValue(&output, input, 0.0);
@endcode
- If success doesn't matter but you can feed a fallback value,
use orxonox::getConvertedValue.
@code
std::string input("3.4");
float output = getConvertedValue(input, 0.0);
@endcode
@details
The back end of these functions are the actual implementations for the
specific conversions, for instance from Ogre::Vector3 to std::string and
vice versa. Some of them also use the iostream operators.
The real deal is evaluating which function is needed for a conversion based
on the input and output type. But there are lots of catches in conjunction
with templates which explains why there are so many functions in this file.
@par Search Order
Finding the right function is governed by priority rules:
-# (Partial) template specialisation of orxonox::ConverterExplicit::convert()
-# An implicit conversion. This includes 'FooBar' to 'int' if FooBar
defines operator int() or float().
-# Global or member operators for iostream when converting from or
to std::string (and FROM const char*)
-# (Partial) template specialisation of orxonox::ConverterFallback::convert()
-# Fallback function that displays "Could not convert value" with type
information obtained from typeid().
@par Implementing conversion functions
To do that you probably need to know a thing or two about the types
involved. So, get ready with that.
Usually the best way to do it is specialising of the orxonox::ConverterFallback
template, like this:
@code
template <>
struct _UtilExport ConverterFallback
{
static bool convert(MyType* output, const std::string& input)
{
...
return success;
}
};
@endcode
This piece of code converts an std::string to MyType and returns whether the
conversion was successful. You can also use partial specialisation.
The advantage with orxonox::ConverterFallback is that it has a low priority
meaning that when there is an implicit conversion or an iostream method, that
comes first and you don't have to deal with it (and the accompanying
function call ambiguity).
However sometimes you would like to explicitely replace such a conversion.
That's where orxonox::ConverterExplicit comes in handy (for instance we
replaced the operator << conversions for Ogre::VectorX with our own functions).
@note
There has to be an exact type match when using template specialisations.
Template specialisations can be defined after including this file.
But any implicit cast function or iostream operator has to be included
in this file!
@par Understanding the Code
In order to understand how the templates work, it is probably best to study
the functions in order of calling. There are lots of comments explaining
what happens, but you'll need to understand a deal about partial template
specialisation and function headers are matched in C++.
*/
#ifndef _Converter_H__
#define _Converter_H__
#include "UtilPrereqs.h"
#include
#include
#include
#include
#include
#include "Output.h"
// disable warnings about possible loss of data
#ifdef ORXONOX_COMPILER_MSVC
# pragma warning(push)
# pragma warning(disable:4244)
#endif
namespace orxonox
{
///////////////////
// No Conversion //
///////////////////
/// Default template. No conversion available at all.
template
struct ConverterFallback
{
ORX_FORCEINLINE static bool convert(ToType* /*output*/, const FromType& /*input*/)
{
orxout(internal_warning) << "Could not convert value of type " << typeid(FromType).name()
<< " to type " << typeid(ToType).name() << endl;
return false;
}
};
/// If all else fails, try a dynamic_cast for pointer types.
template
struct ConverterFallback
{
ORX_FORCEINLINE static bool convert(ToType** output, FromType* const input)
{
ToType* temp = dynamic_cast(input);
if (temp)
{
*output = temp;
return true;
}
else
return false;
}
};
}
///////////////////////
// ConverterFallback //
///////////////////////
/** Fallback template for stringstream
@details
Neither FromType nor ToType was std::string, therefore
delegate to orxonox::ConverterFallback
*/
template
struct ConverterStringStream
{
ORX_FORCEINLINE static bool convert(ToType* output, const FromType& input)
{
return orxonox::ConverterFallback::convert(output, input);
}
};
/////////////
// OStream //
/////////////
/// Extra namespace to avoid exposing the iostream operators in it
namespace fallbackTemplates
{
/// Fallback operator <<() (delegates to orxonox::ConverterFallback)
template
ORX_FORCEINLINE bool operator <<(std::ostream& outstream, const FromType& input)
{
std::string temp;
if (orxonox::ConverterFallback::convert(&temp, input))
{
std::operator <<(outstream, temp);
return true;
}
else
return false;
}
}
/// Template that evaluates whether we can convert to std::string via ostringstream
template
struct ConverterStringStream
{
ORX_FORCEINLINE static bool convert(std::string* output, const FromType& input)
{
using namespace fallbackTemplates;
// this operator call only chooses fallbackTemplates::operator<<()
// if there's no other fitting function
std::ostringstream oss;
// Note: std::ostream has operator!() to tell whether any error flag was set
if (oss << input)
{
(*output) = oss.str();
return true;
}
else
return false;
}
};
/////////////
// IStream //
/////////////
namespace fallbackTemplates
{
/// Fallback operator >>() (delegates to orxonox::ConverterFallback)
template
ORX_FORCEINLINE bool operator >>(std::istream& instream, ToType& output)
{
std::string input(static_cast(instream).str());
return orxonox::ConverterFallback::convert(&output, input);
}
}
/// Template that evaluates whether we can convert from std::string via istringstream
template
struct ConverterStringStream
{
ORX_FORCEINLINE static bool convert(ToType* output, const std::string& input)
{
using namespace fallbackTemplates;
// this operator call chooses fallbackTemplates::operator>>()
// only if there's no other fitting function
std::istringstream iss(input);
// Note: std::istream has operator!() to tell whether any error flag was set
if (iss >> (*output))
{
return true;
}
else
return false;
}
};
namespace orxonox
{
///////////////////
// Implicit Cast //
///////////////////
/// %Template delegates to ::ConverterStringStream
template
ORX_FORCEINLINE bool convertImplicitely(ToType* output, const FromType& input, Loki::Int2Type)
{
return ConverterStringStream::convert(output, input);
}
/// Makes an implicit cast from \a FromType to \a ToType
template
ORX_FORCEINLINE bool convertImplicitely(ToType* output, const FromType& input, Loki::Int2Type)
{
(*output) = static_cast(input);
return true;
}
////////////////////////////////
// ConverterExplicit Fallback //
////////////////////////////////
/** Default template if no orxonox::ConverterExplicit is available
@details
Evaluates whether \a FromType can be implicitly converted to \a ToType
by the use the ImplicitConversion magic.
*/
template
struct ConverterExplicit
{
static constexpr bool probe = std::is_convertible::value;
ORX_FORCEINLINE static bool convert(ToType* output, const FromType& input)
{
// Use the probe's value to delegate to the right function
return convertImplicitely(output, input, Loki::Int2Type());
}
};
//////////////////////
// Public Functions //
//////////////////////
/**
@brief
Converts any value to any other as long as there exists a conversion.
@details
Otherwise, the conversion will generate a runtime warning and return false.
@see Convert.h
@param output
A pointer to the variable where the converted value will be stored
@param input
The original value
*/
template
ORX_FORCEINLINE bool convertValue(ToType* output, const FromType& input)
{
return ConverterExplicit::convert(output, input);
}
// Calls convertValue and returns true if the conversion was successful.
// Otherwise the fallback is used.
/**
@brief
Converts any value to any other as long as there exists a conversion.
Otherwise, the conversion will generate a runtime warning and return false.
If the conversion doesn't succeed, \a fallback is written to \a output.
@see Convert.h
@param output
A pointer to the variable where the converted value will be stored
@param input
The original value
@param fallback
A default value that gets written to '*output' if there is no conversion.
*/
template
ORX_FORCEINLINE bool convertValue(ToType* output, const FromType& input, const ToType& fallback)
{
if (convertValue(output, input))
return true;
else
{
(*output) = fallback;
return false;
}
}
/// Directly returns the converted value, but uses the fallback on failure. @see convertValue
template
ORX_FORCEINLINE ToType getConvertedValue(const FromType& input, const ToType& fallback)
{
ToType output;
convertValue(&output, input, fallback);
return output;
}
/**
@brief
Converts any value to any other as long as there exists a conversion.
@details
Use exactly the way you use static_cast, etc.
A failed conversion will return a default instance of \a ToType
(possibly uninitialised)
@see Convert.h
@param input
The original value
*/
template
ORX_FORCEINLINE ToType multi_cast(const FromType& input)
{
ToType output;
convertValue(&output, input);
return output;
}
////////////////////////////////
// Special string conversions //
////////////////////////////////
/// Delegates conversion from const char* to std::string
template
struct ConverterExplicit
{
ORX_FORCEINLINE static bool convert(ToType* output, const char* input)
{
return convertValue(output, input);
}
};
/// Conversion would exhibit ambiguous << or >> operators when using iostream
template <>
struct ConverterExplicit
{
ORX_FORCEINLINE static bool convert(std::string* output, const char input)
{
*output = input;
return true;
}
};
/// Conversion would exhibit ambiguous << or >> operators when using iostream
template <>
struct ConverterExplicit
{
ORX_FORCEINLINE static bool convert(std::string* output, const unsigned char input)
{
*output = input;
return true;
}
};
/// Conversion would exhibit ambiguous << or >> operators when using iostream
template <>
struct ConverterExplicit
{
ORX_FORCEINLINE static bool convert(char* output, const std::string& input)
{
if (!input.empty())
*output = input[0];
else
*output = '\0';
return true;
}
};
/// Conversion would exhibit ambiguous << or >> operators when using iostream
template <>
struct ConverterExplicit
{
ORX_FORCEINLINE static bool convert(unsigned char* output, const std::string& input)
{
if (!input.empty())
*output = input[0];
else
*output = '\0';
return true;
}
};
/// Conversion from bool to std::string
template <>
struct ConverterExplicit
{
ORX_FORCEINLINE static bool convert(std::string* output, const bool& input)
{
if (input)
*output = "true";
else
*output = "false";
return true;
}
};
/// Conversion from std::string to bool
template <>
struct _UtilExport ConverterExplicit
{
static bool convert(bool* output, const std::string& input);
};
}
// Reinstate warnings
#ifdef ORXONOX_COMPILER_MSVC
# pragma warning(pop)
#endif
#endif /* _Convert_H__ */