Add a new initial implementation for lexical_cast.
The code is only used in the unit test at the moment. It would be nice if others can test whether the code works on all supported platforms since it makes more use of templates. If there are no reports of issues, I'll start to use this code to replace the old lexical cast.
This commit is contained in:
parent
22ee67f40f
commit
a0a25fb48f
3 changed files with 300 additions and 0 deletions
|
@ -497,6 +497,7 @@ SET(test_SRC
|
|||
tests/utils/play_scenario.cpp
|
||||
tests/test_config_cache.cpp
|
||||
tests/test_formula_ai.cpp
|
||||
tests/test_lexical_cast.cpp
|
||||
tests/test_network_worker.cpp
|
||||
tests/test_policy.cpp
|
||||
tests/test_team.cpp
|
||||
|
|
181
src/lexical_cast.hpp
Normal file
181
src/lexical_cast.hpp
Normal file
|
@ -0,0 +1,181 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2009 by Mark de Wever <koraq@xs4all.nl>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
or at your option any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file lexical_cast.hpp
|
||||
* New lexcical_cast header.
|
||||
*
|
||||
* For debugging you can include this header _in_ a namespace (to honour ODR)
|
||||
* and have a set of functions that throws exceptions instead of doing the
|
||||
* real job. This is done for the unit tests but should normally not be done.
|
||||
*/
|
||||
|
||||
#ifdef LEXICAL_CAST_DEBUG
|
||||
#undef LEXICAL_CAST_HPP_INCLUDED
|
||||
#endif
|
||||
|
||||
#ifndef LEXICAL_CAST_HPP_INCLUDED
|
||||
#define LEXICAL_CAST_HPP_INCLUDED
|
||||
|
||||
|
||||
#ifdef LEXICAL_CAST_DEBUG
|
||||
|
||||
#undef SIGNATURE_2
|
||||
|
||||
/**
|
||||
* Signature for a function with two parameters.
|
||||
*
|
||||
* This version throws an exception with the typeid of the function used.
|
||||
*
|
||||
* @param res The result type.
|
||||
* @param name The name of the function.
|
||||
* @param p1 Parameter 1, both type and variable name.
|
||||
* @param p2 Parameter 2, both type and variable name.
|
||||
*/
|
||||
#define SIGNATURE_2(res, name, p1, p2) res name(p1, p2) { \
|
||||
static const std::type_info& type = \
|
||||
typeid((res (tclass::*)(p1, p2)) &tclass::name); \
|
||||
throw(&type);
|
||||
|
||||
#else
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <typeinfo>
|
||||
#include <boost/type_traits.hpp>
|
||||
|
||||
#define SIGNATURE_2(res, name, p1, p2) res name(p1, p2) {
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @namespace implementation
|
||||
* Contains the implementation details for lexical_cast and shouldn't be used
|
||||
* directly.
|
||||
*//*
|
||||
namespace implementation {
|
||||
|
||||
template<typename To, typename From>
|
||||
struct tlexical_cast;
|
||||
|
||||
} // namespace implementation */
|
||||
|
||||
/** Thrown when a lexical_cast fails. */
|
||||
struct bad_lexical_cast {};
|
||||
|
||||
namespace implementation {
|
||||
|
||||
/** Fallback if no specialized cast exists. */
|
||||
template<typename To, typename From>
|
||||
To lexical_cast_generic(From value)
|
||||
{
|
||||
To result;
|
||||
std::stringstream sstr;
|
||||
|
||||
if(!(sstr << value && sstr >> result)) {
|
||||
throw bad_lexical_cast();
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for the conversion.
|
||||
*
|
||||
* Since functions can't be partially specialized we use a class, which can be
|
||||
* partially specialized for the conversion.
|
||||
*
|
||||
* @tparam To The type to convert to.
|
||||
* @tparam From The type to convert from.
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
struct tlexical_cast
|
||||
{
|
||||
/**
|
||||
* The conversion operator.
|
||||
*
|
||||
* All (partially) specialized classes need to implement this function to
|
||||
* do the conversion.
|
||||
*
|
||||
* @tparam To The type to convert to.
|
||||
* @tparam From The type to convert from.
|
||||
*
|
||||
* @param value The value to convert.
|
||||
*
|
||||
* @returns The converted value.
|
||||
*/
|
||||
To operator()(From value)
|
||||
{
|
||||
return lexical_cast_generic<To/*,
|
||||
typename boost::add_reference<
|
||||
typename boost::add_const<From>::type>::type*/>(value);
|
||||
}
|
||||
};
|
||||
/*
|
||||
template<typename To, typename From>
|
||||
struct tlexical_cast<To, From*>
|
||||
{
|
||||
To operator()(From* value)
|
||||
{
|
||||
return lexical_cast_generic<To, const From*>(value);
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
/** Specialized class to return strings. */
|
||||
template<typename From>
|
||||
struct tlexical_cast<std::string, From>
|
||||
{
|
||||
typedef tlexical_cast<std::string, From> tclass;
|
||||
|
||||
template <class T>
|
||||
SIGNATURE_2(std::string, cast, T value, const boost::true_type&)
|
||||
std::stringstream sstr;
|
||||
sstr << value;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
SIGNATURE_2(std::string, cast, T value, const boost::false_type&)
|
||||
|
||||
return lexical_cast_generic<std::string, T>(value);
|
||||
}
|
||||
|
||||
std::string operator()(From value)
|
||||
{
|
||||
return this->cast<From>(value, boost::is_integral<
|
||||
typename boost::remove_pointer<From>::type>());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
|
||||
/**
|
||||
* Lexical cast converts one type to another.
|
||||
*
|
||||
* @tparam To The type to convert to.
|
||||
* @tparam From The type to convert from.
|
||||
*
|
||||
* @param value The value to convert.
|
||||
*
|
||||
* @returns The converted value.
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
inline To lexical_cast(From value)
|
||||
{
|
||||
return implementation::tlexical_cast<To, From>().operator()(value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
118
src/tests/test_lexical_cast.cpp
Normal file
118
src/tests/test_lexical_cast.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2009 by Mark de Wever <koraq@xs4all.nl>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
or at your option any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "utils/test_support.hpp"
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-test"
|
||||
|
||||
#include "lexical_cast.hpp"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace test_throw {
|
||||
|
||||
#define LEXICAL_CAST_DEBUG
|
||||
#include "lexical_cast.hpp"
|
||||
|
||||
#define TEST_CASE(type_send, type_used, initializer) \
|
||||
do { \
|
||||
typedef type_send TS; \
|
||||
TS val = initializer value; \
|
||||
\
|
||||
typedef type_used TU; \
|
||||
typedef implementation::tlexical_cast<std::string, TU> tclass; \
|
||||
try { \
|
||||
lexical_cast<std::string>(val); \
|
||||
} catch(const std::type_info* type) { \
|
||||
static const std::type_info& expected_type = \
|
||||
typeid((std::string (tclass::*) \
|
||||
(TU, const boost::true_type&)) &tclass::cast); \
|
||||
\
|
||||
BOOST_REQUIRE_MESSAGE((*type == expected_type) == match, \
|
||||
"Test failed : Excpected result " \
|
||||
<< (match ? "equal " : "not equal") << '\n' \
|
||||
<< "type: " << typeid(TS).name() << '\n' \
|
||||
<< "caught: " <<type->name() << '\n' \
|
||||
<< "expected: " << expected_type.name() << '\n'); \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
BOOST_CHECK(false); \
|
||||
} while(0)
|
||||
|
||||
|
||||
template<class T>
|
||||
void test_intergral(const bool match = true)
|
||||
{
|
||||
T value = T();
|
||||
|
||||
TEST_CASE(T, T, );
|
||||
TEST_CASE(const T, T, );
|
||||
|
||||
TEST_CASE(T&, T, );
|
||||
TEST_CASE(const T&, T, );
|
||||
|
||||
TEST_CASE(T*, T*, &);
|
||||
TEST_CASE(const T*, const T*, &);
|
||||
|
||||
TEST_CASE(T* const, T*, &);
|
||||
TEST_CASE(const T* const, const T*, &);
|
||||
}
|
||||
#undef TEST_CASE
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_lexical_cast_throw)
|
||||
{
|
||||
/* note Wesnoth's coding style doesn't allow w_char so ignore them. */
|
||||
|
||||
test_intergral<bool>();
|
||||
|
||||
/*
|
||||
* We don't want chars to match since a string cast of a char is
|
||||
* ambiguous; does the user want it interpreted as a char or as a number?
|
||||
* But as long as that hasn't been fixed, leave the char.
|
||||
*/
|
||||
test_intergral<char>();
|
||||
test_intergral<signed char>();
|
||||
test_intergral<unsigned char>();
|
||||
|
||||
test_intergral<short>();
|
||||
test_intergral<int>();
|
||||
test_intergral<long>();
|
||||
test_intergral<long long>();
|
||||
|
||||
test_intergral<unsigned short>();
|
||||
test_intergral<unsigned int>();
|
||||
test_intergral<unsigned long>();
|
||||
test_intergral<unsigned long long>();
|
||||
|
||||
test_intergral<float>(false);
|
||||
test_intergral<double>(false);
|
||||
test_intergral<long double>(false);
|
||||
}
|
||||
|
||||
} // namespace test_throw
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_lexical_cast_result)
|
||||
{
|
||||
BOOST_CHECK(lexical_cast<std::string>(true) == "1");
|
||||
BOOST_CHECK(lexical_cast<std::string>(false) == "0");
|
||||
|
||||
BOOST_CHECK(lexical_cast<std::string>(1) == "1");
|
||||
BOOST_CHECK(lexical_cast<std::string>(1u) == "1");
|
||||
|
||||
BOOST_CHECK(lexical_cast<std::string>(1.2f) == "1.2");
|
||||
BOOST_CHECK(lexical_cast<std::string>(1.2) == "1.2");
|
||||
}
|
Loading…
Add table
Reference in a new issue