Skip to content
Snippets Groups Projects
Commit b7288635 authored by g0dil's avatar g0dil
Browse files

Utils/Console: Extend enum parsing to ignore case and accept arbitrary unique...

Utils/Console: Extend enum parsing to ignore case and accept arbitrary unique initial substrings as valid enum value
parent d30a92af
No related branches found
No related tags found
No related merge requests found
...@@ -39,10 +39,21 @@ prefix_ long senf::console::detail::parseEnum(EnumTable const & table, ...@@ -39,10 +39,21 @@ prefix_ long senf::console::detail::parseEnum(EnumTable const & table,
if (tokens.size() != 1) if (tokens.size() != 1)
throw SyntaxErrorException("parameter syntax error"); throw SyntaxErrorException("parameter syntax error");
EnumTable::left_map::const_iterator i (table.left.find(tokens.begin()[0].value())); std::string sym (tokens.begin()[0].value());
if (i == table.left.end()) boost::algorithm::to_lower(sym);
throw SyntaxErrorException("parameter syntax error: invalid enum value"); EnumTable::left_map::const_iterator i1 (table.left.lower_bound(sym));
return i->second; EnumTable::left_map::const_iterator i2 (table.left.lower_bound(sym+"\xff"));
if (i1 == i2)
throw SyntaxErrorException("parameter syntax error: invalid enum value: ")
<< tokens.begin()[0].value();
long v (i1->second);
if (boost::algorithm::to_lower_copy(i1->first) == sym)
return v;
++i1;
if (i1 != i2)
throw SyntaxErrorException("parameter syntax error: ambiguous enum value: ")
<< tokens.begin()[0].value();
return v;
} }
prefix_ std::string senf::console::detail::formatEnum(EnumTable const & table, long value) prefix_ std::string senf::console::detail::formatEnum(EnumTable const & table, long value)
......
...@@ -184,6 +184,10 @@ namespace console { ...@@ -184,6 +184,10 @@ namespace console {
This macro will register an enum type and it's enumerators defined at namespace scope. See This macro will register an enum type and it's enumerators defined at namespace scope. See
\ref SENF_CONSOLE_REGISTER_ENUM_MEMBER to register a member enum type. \ref SENF_CONSOLE_REGISTER_ENUM_MEMBER to register a member enum type.
\note All enumerator values must be unique ignoring case.
The enum parser will accept any unique initial substring ignoring case as valid enum value.
\ingroup console_commands \ingroup console_commands
*/ */
# define SENF_CONSOLE_REGISTER_ENUM(Type, Values) \ # define SENF_CONSOLE_REGISTER_ENUM(Type, Values) \
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <boost/preprocessor/facilities/empty.hpp> #include <boost/preprocessor/facilities/empty.hpp>
#include <boost/bimap.hpp> #include <boost/bimap.hpp>
#include <boost/assign/list_inserter.hpp> #include <boost/assign/list_inserter.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include "../../Utils/singleton.hh" #include "../../Utils/singleton.hh"
///////////////////////////////ih.p//////////////////////////////////////// ///////////////////////////////ih.p////////////////////////////////////////
...@@ -42,45 +43,41 @@ namespace console { ...@@ -42,45 +43,41 @@ namespace console {
namespace detail { namespace detail {
#ifndef DOXYGEN #ifndef DOXYGEN
struct StringILess
{
bool operator()(std::string const & left, std::string const & right) const
{ return boost::algorithm::to_lower_copy(left)
< boost::algorithm::to_lower_copy(right); }
};
typedef boost::bimap<std::string, long> EnumTable; typedef boost::bimap<boost::bimaps::set_of<std::string, StringILess>, long> EnumTable;
long parseEnum(EnumTable const & table, ParseCommandInfo::TokensRange const & tokens); long parseEnum(EnumTable const & table, ParseCommandInfo::TokensRange const & tokens);
std::string formatEnum(EnumTable const & table, long value); std::string formatEnum(EnumTable const & table, long value);
template <class EnumType>
struct EnumTraits : public senf::singleton< EnumTraits<EnumType> >
{
using senf::singleton< EnumTraits<EnumType> >::instance;
EnumTable table;
};
# define SENF_CONSOLE_REGISTER_ENUM_ELT(r,d,e) \ # define SENF_CONSOLE_REGISTER_ENUM_ELT(r,d,e) \
(BOOST_PP_STRINGIZE(e), static_cast<long>(d e)) ( BOOST_PP_STRINGIZE(e), static_cast<long>(d e) )
# define SENF_CONSOLE_REGISTER_ENUM_(Prefix, Type, Values) \ # define SENF_CONSOLE_REGISTER_ENUM_(Prefix, Type, Values) \
void senf_console_init_enum_table(Prefix Type) \ senf::console::detail::EnumTable & senf_console_enum_table(Prefix Type) \
{ \ { \
senf::console::detail::EnumTraits<Prefix Type> & traits ( \ static senf::console::detail::EnumTable table; \
senf::console::detail::EnumTraits<Prefix Type>::instance() ); \ if (table.empty()) \
if (traits.table.empty()) \ boost::assign::insert(table) \
boost::assign::insert(traits.table) \
BOOST_PP_SEQ_FOR_EACH( SENF_CONSOLE_REGISTER_ENUM_ELT, Prefix, Values ); \ BOOST_PP_SEQ_FOR_EACH( SENF_CONSOLE_REGISTER_ENUM_ELT, Prefix, Values ); \
return table; \
} \ } \
void senf_console_parse_argument( \ void senf_console_parse_argument( \
senf::console::ParseCommandInfo::TokensRange const & tokens, Prefix Type & out) \ senf::console::ParseCommandInfo::TokensRange const & tokens, Prefix Type & out) \
{ \ { \
senf_console_init_enum_table( Prefix Type() ); \
out = static_cast<Prefix Type>( \ out = static_cast<Prefix Type>( \
senf::console::detail::parseEnum( \ senf::console::detail::parseEnum( \
senf::console::detail::EnumTraits<Prefix Type>::instance().table, tokens)); \ senf_console_enum_table( Prefix Type() ), tokens) ); \
} \ } \
void senf_console_format_value(Prefix Type value, std::ostream & os) \ void senf_console_format_value(Prefix Type value, std::ostream & os) \
{ \ { \
senf_console_init_enum_table( Prefix Type() ); \
os << senf::console::detail::formatEnum( \ os << senf::console::detail::formatEnum( \
senf::console::detail::EnumTraits<Prefix Type>::instance().table, \ senf_console_enum_table( Prefix Type() ), static_cast<long>(value) ); \
static_cast<long>(value) ); \
} }
#endif #endif
......
...@@ -40,8 +40,8 @@ ...@@ -40,8 +40,8 @@
///////////////////////////////cc.p//////////////////////////////////////// ///////////////////////////////cc.p////////////////////////////////////////
namespace { namespace {
enum TestEnum { Foo, Bar }; enum TestEnum { Foo, Bar, FooBar };
SENF_CONSOLE_REGISTER_ENUM( TestEnum, (Foo)(Bar) ); SENF_CONSOLE_REGISTER_ENUM( TestEnum, (Foo)(Bar)(FooBar) );
TestEnum test (TestEnum value) { return value; } TestEnum test (TestEnum value) { return value; }
...@@ -139,6 +139,24 @@ BOOST_AUTO_UNIT_TEST(enumSupport) ...@@ -139,6 +139,24 @@ BOOST_AUTO_UNIT_TEST(enumSupport)
parser.parse("test/member MemberBar", parser.parse("test/member MemberBar",
boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) ); boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
BOOST_CHECK_EQUAL( ss.str(), "MemberBar\n" ); BOOST_CHECK_EQUAL( ss.str(), "MemberBar\n" );
ss.str("");
SENF_CHECK_NO_THROW(
parser.parse("test/test foob",
boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
BOOST_CHECK_EQUAL( ss.str(), "FooBar\n" );
ss.str("");
SENF_CHECK_NO_THROW(
parser.parse("test/test b",
boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
BOOST_CHECK_EQUAL( ss.str(), "Bar\n" );
ss.str("");
SENF_CHECK_NO_THROW(
parser.parse("test/test foo",
boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
BOOST_CHECK_EQUAL( ss.str(), "Foo\n" );
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment