diff --git a/projectfiles/CodeBlocks-SCons/wesnoth.cbp b/projectfiles/CodeBlocks-SCons/wesnoth.cbp
index a1bc60e1137..25eef962f3c 100644
--- a/projectfiles/CodeBlocks-SCons/wesnoth.cbp
+++ b/projectfiles/CodeBlocks-SCons/wesnoth.cbp
@@ -163,8 +163,6 @@
-
-
@@ -753,6 +751,7 @@
+
@@ -948,6 +947,8 @@
+
+
diff --git a/projectfiles/CodeBlocks/wesnoth.cbp b/projectfiles/CodeBlocks/wesnoth.cbp
index 9c0204fbe0a..1607da4b60f 100644
--- a/projectfiles/CodeBlocks/wesnoth.cbp
+++ b/projectfiles/CodeBlocks/wesnoth.cbp
@@ -788,6 +788,7 @@
+
diff --git a/projectfiles/CodeLite/wesnoth.project b/projectfiles/CodeLite/wesnoth.project
index 25fc6eb8eeb..69487f8250a 100644
--- a/projectfiles/CodeLite/wesnoth.project
+++ b/projectfiles/CodeLite/wesnoth.project
@@ -3384,7 +3384,8 @@
-
+
+
@@ -4424,6 +4425,7 @@
+
diff --git a/projectfiles/VC9/wesnoth.vcproj b/projectfiles/VC9/wesnoth.vcproj
index b27518da9a0..2382e405415 100644
--- a/projectfiles/VC9/wesnoth.vcproj
+++ b/projectfiles/VC9/wesnoth.vcproj
@@ -16555,6 +16555,62 @@
/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -20182,6 +20238,10 @@
RelativePath="..\..\src\lua_jailbreak_exception.hpp"
>
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 94feccae60e..59258a429b5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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
diff --git a/src/SConscript b/src/SConscript
index 23b6d142cb1..51edf252c57 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -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
diff --git a/src/make_enum.hpp b/src/make_enum.hpp
new file mode 100644
index 00000000000..4b49d351ddb
--- /dev/null
+++ b/src/make_enum.hpp
@@ -0,0 +1,212 @@
+/*
+ Copyright (C) 2014 by Chris Beck
+ 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
+#include
+
+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
diff --git a/src/tests/test_make_enum.cpp b/src/tests/test_make_enum.cpp
new file mode 100644
index 00000000000..2c919e9825a
--- /dev/null
+++ b/src/tests/test_make_enum.cpp
@@ -0,0 +1,146 @@
+/*
+ Copyright (C) 2014 by Chris Beck
+ 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
+
+#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 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 (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 ("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 ("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 (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 ("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 ("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()