\code
void fun1(std::ostream & os, senf::console::ParseCommandInfo const & command)
{
- ParseCommandInfo::ArgumentsRange args (command.arguments());
- ParseCommandInfo::ArgumentsRange::iterator arg (args.begin());
-
- // Check that we are not missing our argument
- if (arg == args.end())
- raise senf::console::SyntaxErrorException("invalid number of arguments");
-
- senf::console::ParseCommandInfo::TokensRange & arg1Tokens ( *(arg++) );
-
- // Check that we don't have additional arguments
- if (arg != args.end())
- raise senf::console::SyntaxErrorException("invalid number of arguments");
-
- // The argument must have exactly one token
- if (arg1Tokens.size() != 1)
- raise senf::console::SyntaxErrorException("argument syntax error");
-
- // Retrieve the token value
- std::string arg (argTokens[0].value());
-
- // In this example, we just write the argument to the output stream
- os << arg << std::endl;
+ // Here we declare variables for the arguments
+ std::string value;
+
+ {
+ // We parse the arguments using the CheckedArgumentIteratorWrapper. This wrapper
+ // will throw a SyntaxErrorException if we access a nonexistent argument or if we
+ // do not parse all arguments.
+ senf::console::CheckedArgumentIteratorWrapper args (command.arguments());
+
+ senf::console::ParseCommandInfo::TokensRange argTokens ( *(args++) );
+ if (arg1Tokens.size() != 1)
+ raise senf::console::SyntaxErrorException("argument syntax error");
+ value = arg1Tokens[0];
+ }
+
+ os << value << std::endl;
}
\endcode
void senf_console_parse_argument(senf::console::ParseCommandInfo::TokensRange const & tokens,
Coordinate & out)
{
- if (tokens.size() != 2)
- throw SyntaxErrorException("parameter syntax error");
- senf::console::ArgumentTraits<int>::parse(
- senf::console::ParseCommandInfo::TokensRange( tokens.begin(), tokens.begin()+1 ),
- out.x )
- senf::console::ArgumentTraits<int>::parse(
- senf::console::ParseCommandInfo::TokensRange( tokens.begin()+1, tokens.end() ),
- out.y )
+ senf::console::CheckedArgumentIteratorWrapper arg (tokens);
+ senf::console::parse( *(arg++), out.x );
+ senf::console::parse( *(arg++), out.y );
}
void senf_console_format_value(Coordinate const & value, std::ostream & os)
{
os << '(' << value.x << ' ' << value.y << ')';
}
- \endcode
+ \endcode
+
The parser will accept an argument with two tokens which are each forwarded to the integer
- parser. The formatter writes out the value as a parenthesized pair.
+ parser. The senf::console::CheckedArgumentIteratorWrapper ensures two things: That all input
+ tokens are parsed and no extra trailing tokens are left unparsed and it checks, that all
+ referenced tokens really exist.
+
+ The formatter writes out the value as a parenthesized pair.
\code
Coordinate fun5(Coordinate const & p) { return Coordinate(2*p.x, 2*p.y) }
{};
#endif
- /** \brief Syntax error parsing command arguments exception
-
- All errors while parsing the arguments of a command must be signaled by throwing an instance
- of SyntaxErrorException. This is important, so command overloading works.
- */
- struct SyntaxErrorException : public std::exception
- {
- explicit SyntaxErrorException(std::string const & msg = "");
- virtual ~SyntaxErrorException() throw();
-
- virtual char const * what() const throw();
- std::string const & message() const;
-
- private:
- std::string message_;
- };
-
/** \brief Config/console tree command node
The CommandNode is the base-class for the tree leaf nodes. Concrete command node
}
///////////////////////////////////////////////////////////////////////////
+
+prefix_ senf::console::CheckedArgumentIteratorWrapper::
+CheckedArgumentIteratorWrapper(ParseCommandInfo::ArgumentsRange const & range,
+ std::string const & msg)
+ : i_ (range.begin()), e_ (range.end()), msg_ (msg)
+{}
+
+prefix_ senf::console::CheckedArgumentIteratorWrapper::
+CheckedArgumentIteratorWrapper(ParseCommandInfo::TokensRange const & range,
+ std::string const & msg)
+ : i_ (range.begin()), e_ (range.end()), msg_ (msg)
+{}
+
+prefix_ senf::console::CheckedArgumentIteratorWrapper::~CheckedArgumentIteratorWrapper()
+{
+ if (i_ != e_ && ! std::uncaught_exception())
+ throw SyntaxErrorException(msg_);
+}
+
+prefix_ senf::console::CheckedArgumentIteratorWrapper::operator ParseCommandInfo::ArgumentIterator()
+{
+ return i_;
+}
+
+prefix_ bool senf::console::CheckedArgumentIteratorWrapper::boolean_test()
+ const
+{
+ return i_ != e_;
+}
+
+prefix_ bool senf::console::CheckedArgumentIteratorWrapper::done()
+ const
+{
+ return i_ == e_;
+}
+
+prefix_ void senf::console::CheckedArgumentIteratorWrapper::clear()
+{
+ i_ = e_;
+}
+
+prefix_ senf::console::CheckedArgumentIteratorWrapper::reference
+senf::console::CheckedArgumentIteratorWrapper::dereference()
+ const
+{
+ if (i_ == e_)
+ throw SyntaxErrorException(msg_);
+ return *i_;
+}
+
+prefix_ void senf::console::CheckedArgumentIteratorWrapper::increment()
+{
+ if (i_ == e_)
+ throw SyntaxErrorException(msg_);
+ ++ i_;
+}
+
+prefix_ bool senf::console::CheckedArgumentIteratorWrapper::
+operator==(ParseCommandInfo::ArgumentIterator const & other)
+ const
+{
+ return i_ == other;
+}
+
+prefix_ bool senf::console::CheckedArgumentIteratorWrapper::
+operator!=(ParseCommandInfo::ArgumentIterator const & other)
+ const
+{
+ return i_ != other;
+}
+
+prefix_ senf::console::ParseCommandInfo::ArgumentIterator
+senf::console::CheckedArgumentIteratorWrapper::operator++(int)
+{
+ ParseCommandInfo::ArgumentIterator i (i_);
+ increment();
+ return i;
+}
+
+///////////////////////////////////////////////////////////////////////////
// senf::console::SingleCommandParser
prefix_ senf::console::CommandParser::Impl & senf::console::CommandParser::impl()
#include <boost/range/iterator_range.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/function.hpp>
+#include "../Utils/safe_bool.hh"
//#include "Parse.mpp"
///////////////////////////////hh.p////////////////////////////////////////
{
typedef std::vector<ArgumentToken> Tokens;
typedef std::vector<std::string> CommandPath;
- class ArgumentIterator;
public:
+ class ArgumentIterator;
+
typedef CommandPath::const_iterator path_iterator;
typedef Tokens::const_iterator token_iterator;
typedef ArgumentIterator argument_iterator;
friend class detail::ParserAccess;
};
+ /** \brief Iterator parsing argument groups
+
+ This special iterator parses a token range returned by the parser into argument ranges. An
+ argument range is either a single token or it is a range of tokens enclosed in matching
+ parenthesis. The ParseCommandInfo::arguments() uses this iterator type. To recursively parse
+ complex arguments, you can however use this iterator to divide a multi-token argument into
+ further argument groups (e.g. to parse a list or vector of items).
+
+ This iterator is a bidirectional iterator \e not a random access iterator.
+ */
class ParseCommandInfo::ArgumentIterator
: public boost::iterator_facade< ParseCommandInfo::ArgumentIterator,
ParseCommandInfo::TokensRange,
boost::bidirectional_traversal_tag,
ParseCommandInfo::TokensRange >
{
+ public:
ArgumentIterator();
+ explicit ArgumentIterator(ParseCommandInfo::TokensRange::iterator i);
private:
- ArgumentIterator(ParseCommandInfo::TokensRange::iterator i);
-
reference dereference() const;
bool equal(ArgumentIterator const & other) const;
void increment();
friend class ParseCommandInfo;
};
+ /** \brief Syntax error parsing command arguments exception
+
+ All errors while parsing the arguments of a command must be signaled by throwing an instance
+ of SyntaxErrorException. This is important, so command overloading works.
+ */
+ struct SyntaxErrorException : public std::exception
+ {
+ explicit SyntaxErrorException(std::string const & msg = "");
+ virtual ~SyntaxErrorException() throw();
+
+ virtual char const * what() const throw();
+ std::string const & message() const;
+
+ private:
+ std::string message_;
+ };
+
+ /** \brief Wrapper checking argument iterator access for validity
+
+ CheckedArgumentIteratorWrapper is a wrapper around a range of arguments parsed using the
+ ParseCommandInfo::ArgumentIterator. It is used to parse arguments either in a command
+ (registered with manual argument parsing) or when defining a custom parser.
+ \code
+ void fn(std::ostream & out, senf::console::ParseCommandInfo command)
+ {
+ std:;string arg1;
+ unsigned arg2 (0);
+
+ {
+ senf::console::CheckedArgumentIteratorWrapper arg (command.arguments());
+ senf::console::parse( *(arg++), arg1 );
+ senf::console::parse( *(arg++), arg2 );
+ }
+
+ // ...
+ }
+ \endcode
+
+ To use the wrapper, you must ensure that:
+ \li You increment the iterator \e past all arguments you parse. The iterator must point to
+ the end of the range when parsing is complete.
+ \li The iterator wrapper is destroyed after parsing but before executing the command itself
+ begins.
+
+ Accessing a non-existent argument or failing to parse all arguments will raise a
+ senf::console::SyntaxErrorException.
+
+ \see \link console_arg_custom Example customer parser \endlink
+ */
+ class CheckedArgumentIteratorWrapper
+ : boost::noncopyable,
+ public boost::iterator_facade< CheckedArgumentIteratorWrapper,
+ ParseCommandInfo::TokensRange,
+ boost::forward_traversal_tag,
+ ParseCommandInfo::TokensRange >,
+ public senf::safe_bool<CheckedArgumentIteratorWrapper>
+
+ {
+ typedef boost::iterator_facade< CheckedArgumentIteratorWrapper,
+ ParseCommandInfo::TokensRange,
+ boost::forward_traversal_tag,
+ ParseCommandInfo::TokensRange > IteratorFacade;
+
+ public:
+ explicit CheckedArgumentIteratorWrapper(
+ ParseCommandInfo::ArgumentsRange const & range,
+ std::string const & msg = "invalid number of arguments");
+ ///< Make wrapper from ArgumentsRange
+ /**< This constructs a wrapper from a
+ ParseCommandInfo::ArgumentsRange.
+ \param[in] range Range of arguments to parse
+ \param[in] msg Error message */
+ explicit CheckedArgumentIteratorWrapper(
+ ParseCommandInfo::TokensRange const & range,
+ std::string const & msg = "invalid number of arguments");
+ ///< Make wrapper from TokensRange
+ /**< This constructs a wrapper from a
+ ParseCommandInfo::TokensRange. The TokensRange is first
+ converted into an ParseCommandInfo::ArgumentsRange
+ which is then wrapped.
+ \param[in] range Range of tokens to parse
+ \param[in] msg Error message */
+
+ ~CheckedArgumentIteratorWrapper(); ///< Check, if all arguments are parsed
+ /**< The destructor validates, that all arguments are parsed
+ correctly when leaving the scope, in which the wrapper
+ is instantiated normally (not by an exception).
+
+ \warning This destructor will throw a
+ SyntaxErrorException, if not all arguments are parsed
+ and when no other exception is in progress. */
+
+ operator ParseCommandInfo::ArgumentIterator();
+ ///< Use wrapper as ParseCommandInfo::ArgumentIterator
+
+ bool boolean_test() const; ///< \c true, if more arguments are available
+ bool done() const; ///< \c true, if all arguments are parsed
+
+ void clear(); ///< Set range empty
+ /**< This call will point the current iterator to the end of
+ the tokens range.
+ \post done() == \c true; */
+
+ bool operator==(ParseCommandInfo::ArgumentIterator const & other) const;
+ ///< Compare wrapper against ArgumentIterator
+ bool operator!=(ParseCommandInfo::ArgumentIterator const & other) const;
+ ///< Compare wrapper against ArgumentIterator
+
+ using IteratorFacade::operator++;
+ ParseCommandInfo::ArgumentIterator operator++(int);
+
+ private:
+ reference dereference() const;
+ void increment();
+
+ ParseCommandInfo::ArgumentIterator i_;
+ ParseCommandInfo::ArgumentIterator e_;
+ std::string msg_;
+
+ friend class boost::iterator_core_access;
+ };
+
/**< \brief Output ParseCommandInfo instance
\related ParseCommandInfo
*/
BOOST_CHECK( args == info.arguments().end() );
}
+namespace {
+ void parseArgs(senf::console::ParseCommandInfo::ArgumentsRange const & args)
+ {
+ senf::console::CheckedArgumentIteratorWrapper arg (args);
+ senf::console::ParseCommandInfo::TokensRange arg1 (*(arg++));
+ senf::console::ParseCommandInfo::TokensRange arg2 (*(arg++));
+ }
+}
+
+BOOST_AUTO_UNIT_TEST(checkedArgumentIterator)
+{
+ senf::console::CommandParser parser;
+
+ BOOST_CHECK( parser.parse("foo a", &setInfo) );
+ BOOST_CHECK_THROW( parseArgs(info.arguments()), senf::console::SyntaxErrorException );
+
+ BOOST_CHECK( parser.parse("foo a b", &setInfo) );
+ BOOST_CHECK_NO_THROW( parseArgs(info.arguments()) );
+
+ BOOST_CHECK( parser.parse("foo a b c", &setInfo) );
+ BOOST_CHECK_THROW( parseArgs(info.arguments()), senf::console::SyntaxErrorException );
+
+ senf::console::CheckedArgumentIteratorWrapper arg (info.arguments());
+ BOOST_CHECK( arg == info.arguments().begin() );
+ BOOST_CHECK( arg != info.arguments().end() );
+ BOOST_CHECK( arg );
+ ++ arg;
+ BOOST_CHECK( arg );
+ arg.clear();
+ BOOST_CHECK( arg.done() );
+
+ senf::console::ParseCommandInfo::ArgumentIterator i (arg);
+ BOOST_CHECK( i == info.arguments().end() );
+}
+
///////////////////////////////cc.e////////////////////////////////////////
#undef prefix_
--- /dev/null
+// $Id$
+//
+// Copyright (C) 2008
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+// Stefan Bund <g0dil@berlios.de>
+//
+// 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.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+/** \file
+ \brief Traits non-inline template implementation */
+
+#include "Traits.ih"
+
+// Custom includes
+
+#define prefix_
+///////////////////////////////ct.p////////////////////////////////////////
+
+template <class Type>
+prefix_ void
+senf::console::senf_console_parse_argument(ParseCommandInfo::TokensRange const & tokens,
+ Type & out)
+{
+ if (tokens.size() != 1)
+ throw SyntaxErrorException("parameter syntax error");
+
+ try {
+ out = boost::lexical_cast<Type>(tokens.begin()[0].value());
+ }
+ catch (std::bad_cast & ex) {
+ throw SyntaxErrorException("parameter syntax error");
+ }
+}
+
+///////////////////////////////ct.e////////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
}
template <class Type>
-prefix_ void
-senf::console::senf_console_parse_argument(ParseCommandInfo::TokensRange const & tokens,
- Type & out)
+prefix_ void senf::console::parse(ParseCommandInfo::TokensRange const & tokens, Type & out)
{
- if (tokens.size() != 1)
- throw SyntaxErrorException("parameter syntax error");
-
- try {
- out = boost::lexical_cast<Type>(tokens.begin()[0].value());
- }
- catch (std::bad_cast & ex) {
- throw SyntaxErrorException("parameter syntax error");
- }
+ ArgumentTraits<Type>::parse(tokens, out);
}
template <class Type>
template <class Type>
void senf_console_parse_argument(ParseCommandInfo::TokensRange const & tokens, Type & out);
+ /** \brief Parse token range
+
+ This helper will invoke the correct ArgumentTraits::parse function to parse the input tokens
+ into the passed in variable.
+
+ \see ArgumentTraits
+ */
+ template <class Type>
+ void parse(ParseCommandInfo::TokensRange const & tokens, Type & out);
+
/** \brief Register enum type for argument parsing
Enum types need to be registered explicitly to support parsing.
///////////////////////////////hh.e////////////////////////////////////////
//#include "Traits.cci"
-//#include "Traits.ct"
+#include "Traits.ct"
#include "Traits.cti"
#endif