Add make_enum macros and a unit test, also add to project files

This commit is contained in:
Chris Beck 2014-05-23 17:15:10 -04:00
parent e8266ba932
commit 215de94390
8 changed files with 427 additions and 3 deletions

View file

@ -163,8 +163,6 @@
<Unit filename="../../src/attack_prediction_display.hpp" />
<Unit filename="../../src/boilerplate-header.cpp" />
<Unit filename="../../src/buffered_istream.hpp" />
<Unit filename="../../src/terrain_builder.cpp" />
<Unit filename="../../src/terrain_builder.hpp" />
<Unit filename="../../src/callable_objects.cpp" />
<Unit filename="../../src/callable_objects.hpp" />
<Unit filename="../../src/campaign_server/campaign_server.cpp" />
@ -753,6 +751,7 @@
<Unit filename="../../src/lua/lzio.h" />
<Unit filename="../../src/lua_jailbreak_exception.cpp" />
<Unit filename="../../src/lua_jailbreak_exception.hpp" />
<Unit filename="../../src/make_enum.hpp" />
<Unit filename="../../src/map.cpp" />
<Unit filename="../../src/map.hpp" />
<Unit filename="../../src/map_exception.hpp" />
@ -948,6 +947,8 @@
<Unit filename="../../src/team.hpp" />
<Unit filename="../../src/terrain.cpp" />
<Unit filename="../../src/terrain.hpp" />
<Unit filename="../../src/terrain_builder.cpp" />
<Unit filename="../../src/terrain_builder.hpp" />
<Unit filename="../../src/terrain_filter.cpp" />
<Unit filename="../../src/terrain_filter.hpp" />
<Unit filename="../../src/terrain_translation.cpp" />

View file

@ -788,6 +788,7 @@
<Unit filename="..\..\src\lua\lzio.h" />
<Unit filename="..\..\src\lua_jailbreak_exception.cpp" />
<Unit filename="..\..\src\lua_jailbreak_exception.hpp" />
<Unit filename="..\..\src\make_enum.hpp" />
<Unit filename="..\..\src\map.cpp" />
<Unit filename="..\..\src\map.hpp" />
<Unit filename="..\..\src\map_exception.hpp" />

View file

@ -3384,7 +3384,8 @@
<File Name="../../src/tests/test_config.cpp"/>
<File Name="../../src/tests/test_config_cache.cpp"/>
<File Name="../../src/tests/test_lexical_cast.cpp"/>
<File Name="../../src/tests/map_location.cpp"/>
<File Name="../../src/tests/test_make_enum.cpp"/>
<File Name="../../src/tests/test_map_location.cpp"/>
<File Name="../../src/tests/test_mp_connect.cpp"/>
<File Name="../../src/tests/test_network_worker.cpp"/>
<File Name="../../src/tests/test_sdl_utils.hpp"/>
@ -4424,6 +4425,7 @@
<File Name="../../src/terrain_translation.hpp"/>
<File Name="../../src/game_errors.hpp"/>
<File Name="../../src/cavegen.cpp"/>
<File Name="../../src/make_enum.hpp"/>
<File Name="../../src/map.cpp"/>
<File Name="../../src/preferences_display.hpp"/>
<File Name="../../src/statistics_dialog.hpp"/>

View file

@ -16555,6 +16555,62 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\src\tests\test_make_enum.cpp"
>
<FileConfiguration
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\Tests\"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\Tests\"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug_with_VLD|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\Tests\"
/>
</FileConfiguration>
<FileConfiguration
Name="Test_Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\Tests\"
/>
</FileConfiguration>
<FileConfiguration
Name="Test_Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\Tests\"
/>
</FileConfiguration>
<FileConfiguration
Name="ReleaseDEBUG|Win32"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\Tests\"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\src\tests\test_map_location.cpp"
>
@ -20182,6 +20238,10 @@
RelativePath="..\..\src\lua_jailbreak_exception.hpp"
>
</File>
<File
RelativePath="..\..\src\make_enum.hpp"
>
</File>
<File
RelativePath="..\..\src\map.cpp"
>

View file

@ -1257,6 +1257,7 @@ if(ENABLE_TESTS)
tests/test_image_modifications.cpp
tests/test_lexical_cast.cpp
tests/test_map_location.cpp
tests/test_make_enum.cpp
tests/test_mp_connect.cpp
tests/test_network_worker.cpp
tests/test_sdl_utils.cpp

View file

@ -663,6 +663,7 @@ test_sources = Split("""
tests/test_image_modifications.cpp
tests/test_lexical_cast.cpp
tests/test_map_location.cpp
tests/test_make_enum.cpp
tests/test_mp_connect.cpp
tests/test_network_worker.cpp
tests/test_sdl_utils.cpp

212
src/make_enum.hpp Normal file
View file

@ -0,0 +1,212 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
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 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.
See the COPYING file for more details.
*/
// Defines the MAKE_ENUM macro,
// and companion macro MAKE_ENUM_STREAM_OPS, which also enables lexical_cast
// Currently this has 1 argument and 2 argument variety.
/**
*
* Example Usage:
*
*/
/*
namespace foo {
MAKE_ENUM(enumname,
(con1, name1)
(con2, name2)
(con3, name3)
)
MAKE_ENUM_STREAM_OPS1(enumname)
}
class bar {
public:
MAKE_ENUM(another,
(val1, name1)
(val2, name2)
(val3, name3)
)
};
MAKE_ENUM_STREAM_OPS2(bar , another)
*/
/**
*
* What it does:
*
* generates an enum foo::enumname, with functions to convert to and from string
*
*
* foo::string_to_enumname(std::string); //throws bad_enum_cast
* foo::string_to_enumname(std::string, foo::enumname default); //no throw
* foo::enumname_to_string(foo::enumname); //no throw
*
* The stream ops define
* std::ostream & operator<< (std::ostream &, foo::enumname)
* std::istream & operator>> (std::istream &, foo::enumname &)
*
* In case of a bad string -> enum conversion from istream output,
* the istream will have its fail bit set.
* This means that lexical_casts will throw a bad_lexical_cast,
* in the similar scenario.
*
*
*
*
* For example code, see src/tests/test_make_enum.cpp
*
**/
#ifndef MAKE_ENUM_HPP
#define MAKE_ENUM_HPP
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
class bad_enum_cast : public std::exception
{
public:
bad_enum_cast(const std::string& enumname, const std::string str)
: message("Failed to convert String \"" + str + "\" to type " + enumname)
{}
virtual ~bad_enum_cast() throw() {}
const char * what() const throw()
{
return message.c_str();
}
private:
const std::string message;
};
#define CAT2( A, B ) A ## B
#define CAT3( A, B, C ) A ## B ## C
//Clang apparently doesn't support __VA_ARGS__ with --C98 (???)
//Can try this again when we upgrade
/*
#define GET_COUNT(_1, _2, _3, _4, _5, COUNT, ...) COUNT
#define VA_SIZE(...) GET_COUNT(__VA_ARGS__, 5, 4, 3, 2, 1)
#define VA_SELECT(MNAME, ...) CAT2(MNAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)
*/
#define EXPANDENUMVALUE( a, b ) a ,
#define EXPANDENUMTYPE( r, data, elem ) EXPANDENUMVALUE elem
#define EXPANDENUMFUNCTIONRETURN( a, b ) if ( str == b ) return a;
#define EXPANDENUMFUNCTION( r, data, elem ) EXPANDENUMFUNCTIONRETURN elem
#define EXPANDENUMFUNCTIONREVRETURN( a, b ) if ( val == a ) return b;
#define EXPANDENUMFUNCTIONREV( r, data, elem ) EXPANDENUMFUNCTIONREVRETURN elem
#define ADD_PAREN_1( A, B ) ((A, B)) ADD_PAREN_2
#define ADD_PAREN_2( A, B ) ((A, B)) ADD_PAREN_1
#define ADD_PAREN_1_END
#define ADD_PAREN_2_END
#define MAKEPAIRS( INPUT ) BOOST_PP_CAT(ADD_PAREN_1 INPUT,_END)
#define MAKEENUMTYPE( NAME, CONTENT ) \
enum NAME { \
BOOST_PP_SEQ_FOR_EACH(EXPANDENUMTYPE, , MAKEPAIRS(CONTENT)) \
}; \
#define MAKEENUMCAST( NAME, PREFIX, CONTENT ) \
PREFIX NAME CAT3(string_to_, NAME, _default) (const std::string& str, NAME def) \
{ \
BOOST_PP_SEQ_FOR_EACH(EXPANDENUMFUNCTION, , MAKEPAIRS(CONTENT)) \
return def; \
} \
PREFIX NAME CAT2(string_to_,NAME) (const std::string& str) \
{ \
BOOST_PP_SEQ_FOR_EACH(EXPANDENUMFUNCTION, , MAKEPAIRS(CONTENT)) \
throw bad_enum_cast( #NAME , str); \
} \
PREFIX std::string CAT2(NAME,_to_string) (NAME val) \
{ \
BOOST_PP_SEQ_FOR_EACH(EXPANDENUMFUNCTIONREV, , MAKEPAIRS(CONTENT)) \
assert(false); \
}
#define MAKE_ENUM( NAME, CONTENT ) \
MAKEENUMTYPE( NAME, CONTENT ) \
MAKEENUMCAST( NAME, static , CONTENT )
#define MAKE_ENUM_STREAM_OPS1( NAME ) \
inline std::ostream& operator<< (std::ostream & os, NAME val) \
{ \
os << CAT2(NAME,_to_string) ( val ); \
return os; \
} \
inline std::istream& operator>> (std::istream & is, NAME & val) \
{ \
std::istream::sentry s(is, true); \
if(!s) return is; \
std::string temp; \
is >> temp; \
try { \
val = CAT2(string_to_, NAME) ( temp ); \
} catch (bad_enum_cast & e) { \
is.setstate(std::ios::failbit); \
} \
return is; \
} \
#define MAKE_ENUM_STREAM_OPS2( NAMESPACE, NAME ) \
inline std::ostream& operator<< (std::ostream & os, NAMESPACE::NAME val) \
{ \
os << CAT2(NAMESPACE::NAME,_to_string) ( val ); \
return os; \
} \
inline std::istream& operator>> (std::istream & is, NAMESPACE::NAME & val) \
{ \
std::istream::sentry s(is, true); \
if(!s) return is; \
std::string temp; \
is >> temp; \
try { \
val = CAT2(NAMESPACE::string_to_,NAME) ( temp ); \
} catch (bad_enum_cast & e) {\
is.setstate(std::ios::failbit); \
} \
return is; \
} \
//Clang apparently doesn't support __VA_ARGS__ with --C98 (???)
//Can try this again when we upgrade
//#define MAKE_ENUM_STREAM_OPS VA_SELECT(MAKE_ENUM_STREAM_OPS, __VA_ARGS__)
#endif

View file

@ -0,0 +1,146 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
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 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.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-test"
#include <boost/test/unit_test.hpp>
#include "make_enum.hpp"
#include "util.hpp"
namespace foo {
MAKE_ENUM(enumname,
(con1, "name1")
(con2, "name2")
(con3, "name3")
)
MAKE_ENUM_STREAM_OPS1(enumname)
}
//generates an enum foo::enumname with lexical casts
/*
namespace foo {
enum enumname {con1, con2 ,con3}
}
foo::enumname lexical_cast<std::string> ( std::string str ) throws bad_lexical_cast
{
...
}
*/
class bar {
public:
MAKE_ENUM(another,
(val1, "name1")
(val2, "name2")
(val3, "name3")
)
};
MAKE_ENUM_STREAM_OPS2(bar,another)
/** Tests begin **/
BOOST_AUTO_TEST_SUITE ( test_make_enum )
BOOST_AUTO_TEST_CASE ( test_make_enum_namespace )
{
foo::enumname e = foo::string_to_enumname_default("name2", foo::con1); //returns con2
BOOST_CHECK_EQUAL ( e, foo::con2 );
std::string str = foo::enumname_to_string(foo::con1); //returns "name1"
BOOST_CHECK_EQUAL ( str, "name1" );
std::string str2 = lexical_cast<std::string> (e); //returns "name2" since e is con2
BOOST_CHECK_EQUAL ( str2, "name2" );
bool threw_exception_when_it_wasnt_supposed_to = false;
try {
e = lexical_cast<foo::enumname> ("name3"); //returns con3
} catch (bad_lexical_cast & e) {
//std::cerr << "enum lexical cast didn't work!" << std::endl;
threw_exception_when_it_wasnt_supposed_to = true;
}
BOOST_CHECK( !threw_exception_when_it_wasnt_supposed_to );
bool threw_exception_when_it_was_supposed_to = false;
try {
e = lexical_cast<foo::enumname> ("name4"); //throw bad_lexical_cast
} catch (bad_lexical_cast & e) {
//std::cerr << "enum lexical cast worked!" << std::endl;
threw_exception_when_it_was_supposed_to = true;
}
BOOST_CHECK( threw_exception_when_it_was_supposed_to );
std::stringstream ss;
ss << e;
BOOST_CHECK_EQUAL (ss.str(), "name3");
}
BOOST_AUTO_TEST_CASE ( test_make_enum_class )
{
bar::another e = bar::string_to_another_default("name2", bar::val1); //returns val2
BOOST_CHECK_EQUAL ( e, bar::val2 );
std::string str = bar::another_to_string(bar::val1); //returns "name1"
BOOST_CHECK_EQUAL ( str, "name1" );
std::string str2 = lexical_cast<std::string> (e); //returns "name2" since e is val2
BOOST_CHECK_EQUAL ( str2, "name2" );
bool threw_exception_when_it_wasnt_supposed_to = false;
try {
e = lexical_cast<bar::another> ("name3"); //returns val3
} catch (bad_lexical_cast & e) {
//std::cerr << "enum lexical cast didn't work!" << std::endl;
threw_exception_when_it_wasnt_supposed_to = true;
}
BOOST_CHECK( !threw_exception_when_it_wasnt_supposed_to );
bool threw_exception_when_it_was_supposed_to = false;
try {
e = lexical_cast<bar::another> ("name4"); //throw bad_lexical_cast
} catch (bad_lexical_cast & e) {
//std::cerr << "enum lexical cast worked!" << std::endl;
threw_exception_when_it_was_supposed_to = true;
}
BOOST_CHECK( threw_exception_when_it_was_supposed_to );
std::stringstream ss;
ss << e;
BOOST_CHECK_EQUAL (ss.str(), "name3");
}
BOOST_AUTO_TEST_SUITE_END()