///////////////////////////////////////////////////////////////////////////
// senf::console::OverloadedCommandNode
+///////////////////////////////////////////////////////////////////////////
+// So soll die doku aussehen:
+//
+// Usage:
+// 1- foo arg1:int arg2:double
+// 2- foo arg3:string
+// 3- foo
+//
+// With:
+// arg1 - arg1-doc
+// arg2 - arg2-doc
+// default: 1.23
+// arg3 - arg3-doc
+//
+// Generic documentation foo blalsdljfl laj flkajslkjs fdlkj oiwlksdj ;llkaj
+// sdflkja sldkfjslkdfj sdlkfj lskjdf lskjdf lksj dflkj lsdkfj lskdjf lskjkd
+// Generic documentation foo blalsdljfl laj flkajslkjs fdlkj oiwlksdj ;llkaj
+// sdflkja sldkfjslkdfj sdlkfj lskjdf lskjdf lksj dflkj lsdkfj lskdjf lskjkd
+// Generic documentation foo blalsdljfl laj flkajslkjs fdlkj oiwlksdj ;llkaj
+// sdflkja sldkfjslkdfj sdlkfj lskjdf lskjdf lksj dflkj lsdkfj lskdjf lskjkd
+//
+// Variant 1:
+// Variant 1 doc la;ksjf lkj sdlkfj lkjekj sdflkj ekljsdlkfj wlej
+// slkj dkj sldkfj lwekljsdf skldjf lskjdf l jsd
+//
+// Variant 2:
+// Variant 2 doc lskdfj lwkej lksjdflksjf
+//
+// Variatn 3:
+// Variant 3 doc slkjflw ekj lskdfj lskdjf laksdj flksj elkj aldskjf lwkejlksdj
+// ldkfaj wlekj slkdfj lskdjf lwkejlkasdjf
+
prefix_ void senf::console::OverloadedCommandNode::v_help(std::ostream & os)
const
{
- os << doc_;
- Overloads::const_iterator i (overloads_.begin());
- Overloads::const_iterator const i_end (overloads_.end());
- for (; i != i_end; ++i) {
- os << "\n\n";
- (*i)->help(os);
+ typedef std::vector<ArgumentDoc> ArgumentDocs;
+ ArgumentDocs argumentDocs;
+ bool haveDocumentedArg (false);
+
+ os << "Usage:\n";
+ {
+ Overloads::const_iterator i (overloads_.begin());
+ Overloads::const_iterator const i_end (overloads_.end());
+ unsigned index (1);
+ for (; i != i_end; ++i, ++index) {
+ os << " " << index << "- " << name();
+ for (unsigned j (0); j < (*i)->numArguments(); ++j) {
+ ArgumentDoc arg;
+ (*i)->argumentDoc(j, arg);
+
+ os << ' ';
+ if (! arg.defaultValue.empty())
+ os << '[';
+ if (! arg.name.empty()) os << arg.name;
+ if (! arg.type.empty()) os << ':' << arg.type;
+ if (arg.name.empty() && arg.type.empty()) os << "...";
+ if (! arg.defaultValue.empty())
+ os << ']';
+
+ if (! arg.name.empty()) {
+ ArgumentDocs::iterator k (argumentDocs.begin());
+ ArgumentDocs::iterator const k_end (argumentDocs.end());
+ for (; k != k_end; ++k)
+ if (k->name == arg.name && k->defaultValue == arg.defaultValue) {
+ if (! arg.doc.empty() && k->doc.empty()) {
+ k->doc == arg.doc;
+ haveDocumentedArg = true;
+ }
+ break;
+ }
+ if (k == k_end) {
+ argumentDocs.push_back(arg);
+ if (! arg.doc.empty())
+ haveDocumentedArg = true;
+ }
+ }
+ }
+ os << '\n';
+ }
+ }
+
+ if (haveDocumentedArg) {
+ os << "\n" "With:\n";
+ ArgumentDocs::const_iterator i (argumentDocs.begin());
+ ArgumentDocs::const_iterator const i_end (argumentDocs.end());
+ for (; i != i_end; ++i) {
+ if (! i->doc.empty()) {
+ os << " "
+ << i->name
+ << std::string(i->name.length()<8 ? 8-i->name.length() : 0, ' ')
+ << " "
+ << i->doc
+ << '\n';
+ if (! i->defaultValue.empty())
+ os << " default: " << i->defaultValue << '\n';
+ }
+ }
+ }
+
+ if (! doc_.empty())
+ os << "\n" << doc_ << "\n";
+
+ {
+ Overloads::const_iterator i (overloads_.begin());
+ Overloads::const_iterator const i_end (overloads_.end());
+ unsigned index (1);
+ for (; i != i_end; ++i, ++index) {
+ std::string overloadDoc ((*i)->doc());
+ if (! overloadDoc.empty())
+ os << "\n" << "Variant " << index << ":\n"
+ << overloadDoc << "\n";
+ }
}
}
///////////////////////////////////////////////////////////////////////////
// senf::console::SimpleCommandOverload
-prefix_ void senf::console::SimpleCommandOverload::v_help(std::ostream & os)
+prefix_ unsigned senf::console::SimpleCommandOverload::v_numArguments()
+ const
+{
+ return 1;
+}
+
+prefix_ void senf::console::SimpleCommandOverload::v_argumentDoc(unsigned index, ArgumentDoc & doc)
+ const
+{}
+
+prefix_ std::string senf::console::SimpleCommandOverload::v_doc()
const
{
- os << doc_;
+ return doc_;
}
prefix_ void senf::console::SimpleCommandOverload::v_execute(std::ostream & os,
execute(os, command);
}
-prefix_ void senf::console::CommandOverload::help(std::ostream & os)
+prefix_ unsigned senf::console::CommandOverload::numArguments()
+ const
+{
+ return v_numArguments();
+}
+
+prefix_ void senf::console::CommandOverload::argumentDoc(unsigned index, ArgumentDoc & doc)
+ const
+{
+ return v_argumentDoc(index,doc);
+}
+
+prefix_ std::string senf::console::CommandOverload::doc()
+ const
{
- v_help(os);
+ return v_doc();
}
prefix_ senf::console::OverloadedCommandNode & senf::console::CommandOverload::node()
+ const
{
SENF_ASSERT( node_ );
return *node_;
}
+prefix_ unsigned senf::console::CommandOverload::overloadIndex()
+ const
+{
+ return node().overloadIndex(*this);
+}
+
prefix_ senf::console::CommandOverload::CommandOverload()
: node_(0)
{}
return *this;
}
+prefix_ unsigned
+senf::console::OverloadedCommandNode::overloadIndex(CommandOverload const & overload)
+{
+ return find(overloads_.begin(), overloads_.end(), CommandOverload::cptr(&overload))
+ - overloads_.begin() + 1;
+}
+
prefix_ senf::console::OverloadedCommandNode::OverloadedCommandNode()
{}
senf::console::OverloadedCommandNode::add(boost::intrusive_ptr<Command> overload)
{
overloads_.push_back(overload);
+ overload->node_ = this;
return *overload;
}
class OverloadedCommandNode;
+ struct ArgumentDoc {
+ std::string name;
+ std::string type;
+ std::string defaultValue;
+ std::string doc;
+ };
+
/** \brief Base class for command overload of OverloadedCommandNode
This class is the base class of the commands which may be added to an
// Types
typedef boost::intrusive_ptr<CommandOverload> ptr;
+ typedef boost::intrusive_ptr<CommandOverload const> cptr;
///////////////////////////////////////////////////////////////////////////
/**< If the \a arguments are not acceptable for this
overload, a SyntaxErrorException must be thrown.
Same as execute() */
-
- void help(std::ostream & os); ///< Provide help for this specific overload
-
- OverloadedCommandNode & node(); ///< Access owning node
+
+ unsigned numArguments() const;
+ void argumentDoc(unsigned index, ArgumentDoc & doc) const;
+ std::string doc() const;
+
+ OverloadedCommandNode & node() const; ///< Access owning node
/**< \pre The command \e must have been added to an
OverloadedCommandNode. */
+ unsigned overloadIndex() const;
protected:
CommandOverload();
#ifndef DOXYGEN
private:
#endif
- virtual void v_help(std::ostream & os) const = 0;
+ virtual unsigned v_numArguments() const = 0;
+ virtual void v_argumentDoc(unsigned index, ArgumentDoc & doc) const = 0;
+ virtual std::string v_doc() const = 0;
virtual void v_execute(std::ostream & os, ParseCommandInfo const & command) const = 0;
private:
OverloadedCommandNode & doc(std::string const & doc);
///< Assign global help for all overloads
+ unsigned overloadIndex(CommandOverload const & overload);
+
protected:
private:
private:
SimpleCommandOverload(Function fn);
- virtual void v_help(std::ostream & os) const;
+ virtual unsigned v_numArguments() const;
+ virtual void v_argumentDoc(unsigned index, ArgumentDoc & doc) const;
+ virtual std::string v_doc() const;
virtual void v_execute(std::ostream & os, ParseCommandInfo const & command) const;
Function fn_;
{
std::stringstream ss;
cmd.help(ss);
- BOOST_CHECK_EQUAL( ss.str(), "cmd\n\nfn1\n\nfn2\n\nfn3" );
+ BOOST_CHECK_EQUAL( ss.str(),
+ "Usage:\n"
+ " 1- overload ...\n"
+ " 2- overload ...\n"
+ " 3- overload ...\n"
+ "\n"
+ "cmd\n"
+ "\n"
+ "Variant 1:\n"
+ "fn1\n"
+ "\n"
+ "Variant 2:\n"
+ "fn2\n"
+ "\n"
+ "Variant 3:\n"
+ "fn3\n" );
}
cmd.unlink();
defaultValue ()
{}
+template <class ParameterType>
+prefix_ std::string senf::console::detail::ParameterInfo<ParameterType>::defaultValueStr()
+ const
+{
+ return hasDefault ? ParameterTraits<ParameterType>::dump(defaultValue) : "";
+}
+
///////////////////////////////////////////////////////////////////////////
// senf::console::detail::ReturnValueTraits<Type>
template <class Type>
prefix_ std::string senf::console::detail::ParameterTraits<Type>::typeDescription()
{
- return prettyName(typeid(Type));
+ std::string type (prettyName(typeid(Type)));
+ std::string::size_type i (type.rfind(':'));
+ return i == std::string::npos ? type : type.substr(i+1);
+}
+
+template <class Type>
+prefix_ std::string senf::console::detail::ParameterTraits<Type>::dump(Type const & value)
+{
+ return boost::lexical_cast<std::string>(value);
}
///////////////////////////////cti.e///////////////////////////////////////
std::string type;
std::string name;
bool hasDefault;
+ std::string doc;
ParameterInfoBase(std::string const & type);
+
+ virtual std::string defaultValueStr() const = 0;
};
template <class ParameterType>
ParameterInfo();
ParameterType defaultValue;
+
+ virtual std::string defaultValueStr() const;
};
template <class Type>
typedef Type type;
static void parse(ParseCommandInfo::TokensRange const & tokens, Type & out);
static std::string typeDescription();
+ static std::string dump(Type const & value);
};
}}}
///////////////////////////////////////////////////////////////////////////
// senf::console::ParsedCommandOverloadBase
-prefix_ void senf::console::ParsedCommandOverloadBase::v_help(std::ostream & os)
+prefix_ unsigned senf::console::ParsedCommandOverloadBase::v_numArguments()
const
-{}
+{
+ return parameters_.size();
+}
+
+prefix_ void senf::console::ParsedCommandOverloadBase::v_argumentDoc(unsigned index,
+ ArgumentDoc & doc)
+ const
+{
+ BOOST_ASSERT( index < parameters_.size() );
+ detail::ParameterInfoBase & arg (*parameters_[index]);
+ doc.name = arg.name.empty()
+ ? (boost::format("arg%d%d") % overloadIndex() % (index+1)).str()
+ : arg.name;
+ doc.type = arg.type;
+ doc.defaultValue = arg.defaultValueStr();
+ doc.doc = arg.doc;
+}
+
+prefix_ std::string senf::console::ParsedCommandOverloadBase::v_doc()
+ const
+{
+ return doc_;
+}
///////////////////////////////cc.e////////////////////////////////////////
#undef prefix_
return * parameters_[n];
}
+prefix_ void senf::console::ParsedCommandOverloadBase::doc(std::string const & d)
+{
+ doc_ = d;
+}
+
///////////////////////////////cci.e///////////////////////////////////////
#undef prefix_
// Custom includes
#include "../Utils/membind.hh"
+#include <boost/format.hpp>
#define prefix_ inline
///////////////////////////////cti.p///////////////////////////////////////
senf::console::ParsedCommandOverloadBase::arg(unsigned n)
const
{
- return static_cast<detail::ParameterInfo<Type> &>(arg(n));
+ return dynamic_cast<detail::ParameterInfo<Type> &>(arg(n));
}
template <class Type>
public:
typedef boost::intrusive_ptr<ParsedCommandOverloadBase> ptr;
- protected:
- ParsedCommandOverloadBase();
-
detail::ParameterInfoBase & arg(unsigned n) const;
template <class Type> detail::ParameterInfo<Type> & arg(unsigned n) const;
+ void doc(std::string const & d);
+
+ protected:
+ ParsedCommandOverloadBase();
+
template <class Type>
void addParameter();
private:
- virtual void v_help(std::ostream & os) const;
+ virtual unsigned v_numArguments() const;
+ virtual void v_argumentDoc(unsigned index, ArgumentDoc & doc) const;
+ virtual std::string v_doc() const;
typedef std::vector<detail::ParameterInfoBase::ptr> Parameters;
Parameters parameters_;
+ std::string doc_;
};
template <class FunctionTraits, unsigned arity=FunctionTraits::arity>
{
if ( command.arguments().size() > BOOST_PP_ITERATION()
|| (command.arguments().size() < BOOST_PP_ITERATION()
- && ! arg( BOOST_PP_ITERATION()-1 ).hasDefault) )
+ && ! arg( command.arguments().size() ).hasDefault) )
throw SyntaxErrorException("invalid number of arguments");
- // First define local variables argN for the parameters. The variables are initialized to their
- // default values
-# define mpp_l(z,n,d) mpp_ArgTypeN(n) mpp_ArgN(n) (arg< mpp_ArgTypeN(n) >( n ).defaultValue);
- BOOST_PP_REPEAT( BOOST_PP_ITERATION(), mpp_l, _ )
-# undef mpp_l
-
ParseCommandInfo::argument_iterator i (command.arguments().begin());
ParseCommandInfo::argument_iterator const i_end (command.arguments().end());
- // Now parse the arguments which are provided leaving the trailing arguments at their default
- // value. We have already checked above, whether those default values are valid. Be aware, that
- // the following cases do NOT have 'break' statements !
-
- switch (BOOST_PP_ITERATION() - command.arguments().size()) {
-
# define mpp_l(z,n,d) \
- case n : \
+ mpp_ArgTypeN(n) mpp_ArgN(n) (arg< mpp_ArgTypeN(n) >( n ).defaultValue); \
+ if (i != i_end) \
detail::ParameterTraits< mpp_ArgTypeN(n) >::parse( *(i++), mpp_ArgN(n) );
BOOST_PP_REPEAT( BOOST_PP_ITERATION(), mpp_l, _ )
# undef mpp_l
- default : // This happens, if ALL arguments are defaulted
- ;
- }
-
// Now call the function binding the arguments to the values parsed above. callAndWrite is
// specialized to ignore a 'void' return value but automatically write all other values to the
// output stream.
//#include "ParsedCommand.test.ih"
// Custom includes
+#include <iostream>
#include <sstream>
#include "ParsedCommand.hh"
#include "Executor.hh"
namespace {
int cb1(int a, double b) { return int(a+b); }
- double cb2() { return 1.2; }
- void cb3(int) {}
+ double cb2(){ return 1.2; }
+ void cb3(int i) { }
std::string cb4(std::ostream & os) { os << "text\n"; return "value"; }
- void cb5(std::ostream & os, int v) { os << "Value: " << v << "\n"; }
+ void cb5(std::ostream & os, std::string v) { os << "Value: " << v << "\n"; }
}
BOOST_AUTO_UNIT_TEST(parsedCommand)
{
std::stringstream ss;
- dir.add("cb", &cb1);
- dir.add("cb", &cb5);
+ senf::console::ParsedCommandOverloadBase & c1 (dir.add("cb", &cb1));
+ c1.doc(
+ "Lo nam balnearius Opprimo Pennatus, no decentia sui, dicto esse se pulchritudo,\n"
+ "pupa Sive res indifferenter. Captivo pa.");
+ c1.arg(0).doc = "Bar didelfrump di desgorb. Nu widsoflar brimeldrgf.";
+ c1.arg(1).name = "checkup";
+ c1.arg(1).doc = "Florgel, dargel and durgel";
+ c1.arg<double>(1).defaultValue = 2.1;
+ c1.arg(1).hasDefault = true;
+ senf::console::ParsedCommandOverloadBase & c5 (dir.add("cb", &cb5));
+ c5.doc(
+ "Uus Primordia fundo falsidicus corium, diurnitas humo pro leto. Sui Ueraciter\n"
+ "hio eruca lenis qua Agalmate ut fors penitentia. Iugum obdormio anxio nuncupo\n"
+ "iam, in vos nam Custodi.");
dir.add("cb", &cb2);
- parser.parse("test/cb 111 222.4; test/cb 222; test/cb",
+ static_cast<senf::console::OverloadedCommandNode&>(dir("cb")).doc(
+ "Ops fortunate, ops me ut orgia vociferatio contumax per, rudo re loco emitto\n"
+ "intolerabiliter ita iugo. Subcribo gravo. Devenio luna fonticulus Castanea\n"
+ "horum fascino Os interpretor non ipse conjuratio hora, qui filius denuntio ait\n"
+ "sono te odium Anhelo. Dum Cedo audax celox alius una Agnosco hic, ibi retineo\n"
+ "lux sto ioco. Per Re dono. Copiose reus scitus jus diligens sis scapulare\n"
+ "Servitium transi.");
+ parser.parse("test/cb 111 222.4; test/cb 222; test/cb foo; test/cb",
boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 ));
- BOOST_CHECK_EQUAL( ss.str(), "333\n" "Value: 222\n" "1.2\n" );
+ BOOST_CHECK_EQUAL( ss.str(), "333\n" "224\n" "Value: foo\n" "1.2\n" );
+ }
+
+ {
+ std::stringstream ss;
+ senf::console::root()["test"]("cb").help(ss);
+ BOOST_CHECK_EQUAL(
+ ss.str(),
+ "Usage:\n"
+ " 1- cb arg11:int [checkup:double]\n"
+ " 2- cb arg21:string\n"
+ " 3- cb\n"
+ "\n"
+ "With:\n"
+ " arg11 Bar didelfrump di desgorb. Nu widsoflar brimeldrgf.\n"
+ " checkup Florgel, dargel and durgel\n"
+ " default: 2.1\n"
+ "\n"
+ "Ops fortunate, ops me ut orgia vociferatio contumax per, rudo re loco emitto\n"
+ "intolerabiliter ita iugo. Subcribo gravo. Devenio luna fonticulus Castanea\n"
+ "horum fascino Os interpretor non ipse conjuratio hora, qui filius denuntio ait\n"
+ "sono te odium Anhelo. Dum Cedo audax celox alius una Agnosco hic, ibi retineo\n"
+ "lux sto ioco. Per Re dono. Copiose reus scitus jus diligens sis scapulare\n"
+ "Servitium transi.\n"
+ "\n"
+ "Variant 1:\n"
+ "Lo nam balnearius Opprimo Pennatus, no decentia sui, dicto esse se pulchritudo,\n"
+ "pupa Sive res indifferenter. Captivo pa.\n"
+ "\n"
+ "Variant 2:\n"
+ "Uus Primordia fundo falsidicus corium, diurnitas humo pro leto. Sui Ueraciter\n"
+ "hio eruca lenis qua Agalmate ut fors penitentia. Iugum obdormio anxio nuncupo\n"
+ "iam, in vos nam Custodi.\n" );
}
}
std::string name(std::string const & suffix) {
return name_ + suffix;
}
-
};
+
}
BOOST_AUTO_UNIT_TEST(memberParsedCommand)
}
template <class Self>
-prefix_ void senf::intrusive_ptr_add_ref(intrusive_refcount_t<Self>* p)
+prefix_ void senf::intrusive_ptr_add_ref(intrusive_refcount_t<Self> const * p)
{
- p->intrusive_ptr_add_ref<Self>();
+ const_cast<intrusive_refcount_t<Self>*>(p)->intrusive_ptr_add_ref<Self>();
}
template <class Self>
-prefix_ void senf::intrusive_ptr_release(intrusive_refcount_t<Self>* p)
+prefix_ void senf::intrusive_ptr_release(intrusive_refcount_t<Self> const * p)
{
- p->intrusive_ptr_release<Self>();
+ const_cast<intrusive_refcount_t<Self>*>(p)->intrusive_ptr_release<Self>();
}
template <class Self>
template <class S> void intrusive_ptr_release();
template <class S>
- friend void senf::intrusive_ptr_add_ref(intrusive_refcount_t<S> * p);
+ friend void senf::intrusive_ptr_add_ref(intrusive_refcount_t<S> const * p);
template <class S>
- friend void senf::intrusive_ptr_release(intrusive_refcount_t<S> * p);
+ friend void senf::intrusive_ptr_release(intrusive_refcount_t<S> const * p);
};
/** \brief Customizable reference count mixin for intrusive_ptr
};
template <class Self>
- void intrusive_ptr_add_ref(intrusive_refcount_t<Self> * p);
+ void intrusive_ptr_add_ref(intrusive_refcount_t<Self> const * p);
template <class Self>
- void intrusive_ptr_release(intrusive_refcount_t<Self> * p);
+ void intrusive_ptr_release(intrusive_refcount_t<Self> const * p);
}