// -*- c-basic-offset: 4; related-file-name: "../../lib/handlercall.cc" -*- #ifndef CLICK_HANDLERCALL_HH #define CLICK_HANDLERCALL_HH #include CLICK_DECLS class VariableExpander; /** @brief Convenience class for calling handlers. * * The HandlerCall class simplifies the process of calling Click handlers. * (The lower-level interface is the Handler class.) HandlerCall is used in * two ways: (1) to call handlers immediately via static member functions, * and (2) to set up future handler calls via HandlerCall objects. The * immediate handler call functions take handler names as arguments and * perform all necessary error checks before calling handlers, if any. A * HandlerCall object encapsulates a handler reference (possibly including * parameters), again automating all necessary error checks. * *

Immediate Handler Calls

* * This example code shows how to use the HandlerCall functions for calling * handlers immediately. * * @code * class YourElement { ... * Element *_other; * } * * void YourElement::function() { * // Call _other's "config" read handler. * String result = HandlerCall::call_read(_other, "config"); * * // The same, providing an error handler to print errors. * ErrorHandler *errh = ErrorHandler::default_handler(); * result = HandlerCall::call_read(_other, "config", errh); * // (Each function takes an optional last argument of "errh".) * * // Call the "foo.config" read handler. Search for element "foo" in * // the current compound element context. * result = HandlerCall::call_read("foo.config", this); * * // Call the global "config" read handler for the current router. * result = HandlerCall::call_read("config", this); * * // Call _other's "stop" write handler with empty value. * int success = HandlerCall::call_write(_other, "stop"); * * // Call _other's "config" write handler with value "blah". * success = HandlerCall::call_write(_other, "config", "blah"); * * // Call the "foo.config" write handler with value "blah". * success = HandlerCall::call_write("foo.config blah", this); * // Or, alternately: * success = HandlerCall::call_write("foo.config", "blah", this); * } * @endcode * *

HandlerCall Objects

* * This example code shows how to use the HandlerCall objects to call * handlers with simplified error checking. * * @code * class YourElement { ... * HandlerCall _read_call; * HandlerCall _write_call; * } * * YourElement::YourElement() * : _read_call(), _write_call() { * } * * int YourElement::configure(Vector &conf, ErrorHandler *errh) { * return cp_va_kparse(conf, this, errh, * "READ_CALL", cpkP, cpHandlerCallRead, &_read_call, * "WRITE_CALL", cpkP, cpHandlerCallWrite, &_write_call, * cpEnd); * } * * int YourElement::initialize(ErrorHandler *errh) { * if ((_read_call && _read_call.initialize_read(this, errh) < 0) * || (_write_call && _write_call.initialize_write(this, errh) < 0)) * return -1; * return 0; * } * * void YourElement::function() { * // call _read_call, print result * if (_read_call) * click_chatter("%s", _read_call.call_read()); * * // call _write_call with error handler * if (_write_call) * _write_call.call_write(ErrorHandler::default_handler()); * } * @endcode * * If usually your element's handler calls aren't used, you can save a small * amount of space by using pointers to HandlerCall objects, as in this * example. The cpHandlerCallPtrRead and cpHandlerCallPtrWrite types allow * the _read_call and _write_call members to start out as null pointers. * * @code * class YourElement { ... * HandlerCall *_read_call; * HandlerCall *_write_call; * } * * YourElement::YourElement() * : _read_call(0), _write_call(0) { * } * * int YourElement::configure(Vector &conf, ErrorHandler *errh) { * return cp_va_kparse(conf, this, errh, * "READ_CALL", cpkP, cpHandlerCallPtrRead, &_read_call, * "WRITE_CALL", cpkP, cpHandlerCallPtrWrite, &_write_call, * cpEnd); * } * * int YourElement::initialize(ErrorHandler *errh) { * if ((_read_call && _read_call->initialize_read(this, errh) < 0) * || (_write_call && _write_call->initialize_write(this, errh) < 0)) * return -1; * return 0; * } * * void YourElement::cleanup(CleanupStage) { * delete _read_call; * delete _write_call; * } * * void YourElement::function() { * // call _read_call, print result * if (_read_call) * click_chatter("%s", _read_call->call_read()); * * // call _write_call with error handler * if (_write_call) * _write_call->call_write(ErrorHandler::default_handler()); * } * @endcode */ class HandlerCall { public: /** @name Immediate Handler Calls */ //@{ static String call_read(Element *e, const String &hname, ErrorHandler *errh = 0); static String call_read(const String &hdesc, const Element *context, ErrorHandler *errh = 0); static int call_write(Element *e, const String &hname, ErrorHandler *errh = 0); static int call_write(Element *e, const String &hname, const String &value, ErrorHandler *errh = 0); static int call_write(const String &hdesc, const Element *context, ErrorHandler *errh = 0); static int call_write(const String &hdesc, const String &value, const Element *context, ErrorHandler *errh = 0); //@} /** @brief Construct an empty HandlerCall. * * Any attempt to read, write, or initialize the HandlerCall will * fail. */ HandlerCall() : _e(0), _h(Handler::blank_handler()) { } /** @brief Construct a HandlerCall described by @a hdesc. * @param hdesc handler description "[ename.]hname[ value]" * * Although the resulting HandlerCall isn't empty, it must be initialized * before it can be used. It returns false for initialized(). The * handler description is not checked for syntax errors, though; * initialize() does that. */ HandlerCall(const String &hdesc) : _e(reinterpret_cast(4)), _h(Handler::blank_handler()), _value(hdesc) { } enum Flags { readable = Handler::f_read, f_read = Handler::f_read, writable = Handler::f_write, f_write = Handler::f_write, f_preinitialize = 4, f_unquote_param = 8 }; /** @brief Initialize the HandlerCall. * @param flags zero or more of f_read, f_write, f_preinitialize, * f_unquote_param * @param context optional element context * @param errh optional error handler * @return 0 on success, negative on failure * * Initializes the HandlerCall object. The handler description supplied * to the constructor is parsed and checked for syntax errors. Any * element reference is looked up relative to @a context, if any. (For * example, if @a hdesc was "x.config" and @a context's name is * "aaa/bbb/ccc", this will search for elements named aaa/bbb/x, aaa/x, * and finally x. If @a context is null, then the description must refer * to a global handler.) If f_read is set in @a flags, then there * must be a read handler named appropriately; if f_write is set, * then there must be a write handler. If f_unquote_param is set, then any * parameters are unquoted. * * Initialization fails if the handler description was bogus (for * example, an empty string, or something like "*#!$&!(#&$."), if the * named handler does not exist, if a read handler description has * parameters but the read handler doesn't actually take parameters, and * so forth. If @a errh is nonnull, errors are reported there. The * HandlerCall becomes empty on failure: empty() will return true, and * (bool) *this will return false. Future call_read() and call_write() * attempts will correctly fail. * * If the f_preinitialize flag is set, the initialize function will check * whether the router's handlers are ready (Router::handlers_ready()). * If handlers are not ready, then initialize() will check for syntax * errors, but not actually look up the handler (since we don't know yet * whether or not the handler exists). Absent a syntax error, * initialize() will return 0 for success even though the HandlerCall * remains uninitialized. */ int initialize(int flags, const Element *context, ErrorHandler *errh = 0); /** @brief Initialize the HandlerCall for reading. * @param context optional element context * @param errh optional error handler * * Equivalent to @link initialize(int, const Element*, ErrorHandler*) * initialize@endlink(f_read, @a context, @a errh). */ inline int initialize_read(const Element *context, ErrorHandler *errh = 0); /** @brief Initialize the HandlerCall for writing. * @param context optional element context * @param errh optional error handler * * Equivalent to @link initialize(int, const Element*, ErrorHandler*) * initialize@endlink(f_write, @a context, @a errh). */ inline int initialize_write(const Element *context, ErrorHandler *errh = 0); typedef bool (HandlerCall::*unspecified_bool_type)() const; /** @brief Test if HandlerCall is empty. * @return True if HandlerCall is not empty, false otherwise. * * Valid HandlerCall objects have been successfully initialized. */ operator unspecified_bool_type() const { return _h != Handler::blank_handler() || _e ? &HandlerCall::empty : 0; } /** @brief Test if HandlerCall is empty. * @return True if HandlerCall is empty, false otherwise. */ bool empty() const { return _h == Handler::blank_handler() && !_e; } /** @brief Test if HandlerCall is initialized. * @return True if HandlerCall is initialized, false otherwise. */ bool initialized() const { return _h != Handler::blank_handler(); } /** @brief Call a read handler. * @param errh optional error handler * @return Read handler result. * * Fails and returns the empty string if this HandlerCall is invalid or * not a read handler. If @a errh is nonnull, then any errors are * reported there, whether from HandlerCall or the handler itself. */ inline String call_read(ErrorHandler *errh = 0) const; /** @brief Call a write handler. * @param errh optional error handler * @return Write handler result. * * Fails and returns -EINVAL if this HandlerCall is invalid or not a * write handler. If @a errh is nonnull, then any errors are reported * there, whether from HandlerCall or the handler itself. */ inline int call_write(ErrorHandler *errh = 0) const; /** @brief Call a write handler, expanding its argument. * @param scope variable scope * @param errh optional error handler * @return Write handler result. * * The write value is expanded in @a scope before the handler is called. * Fails and returns -EINVAL if this HandlerCall is invalid or not a * write handler. If @a errh is nonnull, then any errors are reported * there, whether from HandlerCall or the handler itself. */ inline int call_write(const VariableExpander &scope, ErrorHandler *errh = 0) const; /** @brief Call a write handler with an additional value. * @param value_ext write value extension * @param errh optional error handler * @return Write handler result. * * The @a value_ext is appended to the write value before the handler is * called. (For example, consider a handler with description "a.set * value". call_write("foo") will call "a.set value foo".) Fails and * returns -EINVAL if this HandlerCall is invalid or not a write handler. * If @a errh is nonnull, then any errors are reported there, whether * from HandlerCall or the handler itself. */ inline int call_write(const String &value_ext, ErrorHandler *errh = 0) const; /** @brief Create and initialize a HandlerCall from @a hdesc. * @param hcall stores the HandlerCall result * @param hdesc handler description "[ename.]hname[ value]" * @param flags initialization flags (f_read, f_write, f_preinitialize) * @param context optional element context * @param errh optional error handler * @return 0 on success, -EINVAL on failure * * Creates a HandlerCall and initializes it. Behaves somewhat like: * * @code * hcall = new HandlerCall(hdesc); * return hcall->initialize(flags, context, errh); * @endcode * * However, (1) if initialization fails, then @a hcall is untouched; and * (2) if initialization succeeds and @a hcall is not null, then the * existing HandlerCall is assigned so that it corresponds to the new one * (no new memory allocations). * * If @a errh is nonnull, then any errors are reported there. */ static int reset(HandlerCall *&hcall, const String &hdesc, int flags, const Element *context, ErrorHandler *errh = 0); /** @brief Create and initialize a HandlerCall on element @a e. * @param hcall stores the HandlerCall result * @param e relevant element, if any * @param hname handler name * @param value handler value * @param flags initialization flags (f_read, f_write, f_preinitialize) * @param errh optional error handler * @return 0 on success, -EINVAL on failure * * Creates a HandlerCall and initializes it. Behaves analogously to * reset(HandlerCall*&, const String&, int, const Element*, ErrorHandler*). */ static int reset(HandlerCall *&hcall, Element *e, const String &hname, const String &value, int flags, ErrorHandler *errh = 0); /** @brief Create and initialize a read HandlerCall from @a hdesc. * @param hcall stores the HandlerCall result * @param hdesc handler description "[ename.]hdesc[ param]" * @param context optional element context * @param errh optional error handler * @return 0 on success, -EINVAL on failure * * Equivalent to * @link reset(HandlerCall*&, const String&, int, const Element*, ErrorHandler*) reset@endlink(@a hcall, @a hdesc, f_read, @a context, @a errh). */ static inline int reset_read(HandlerCall *&hcall, const String &hdesc, const Element *context, ErrorHandler *errh = 0); /** @brief Create and initialize a read HandlerCall from @a hdesc. * @param hcall stores the HandlerCall result * @param e relevant element, if any * @param hname handler name * @param errh optional error handler * @return 0 on success, -EINVAL on failure * * Equivalent to * @link reset(HandlerCall*&, Element*, const String&, const String&, int, ErrorHandler*) reset@endlink(@a hcall, @a e, @a hname, String(), f_read, @a context, @a errh). */ static inline int reset_read(HandlerCall *&hcall, Element *e, const String &hname, ErrorHandler *errh = 0); /** @brief Create and initialize a write HandlerCall from @a hdesc. * @param hcall stores the HandlerCall result * @param hdesc handler description "[ename.]hdesc[ value]" * @param context optional element context * @param errh optional error handler * @return 0 on success, -EINVAL on failure * * Equivalent to * @link reset(HandlerCall*&, const String&, int, const Element*, ErrorHandler*) reset@endlink(@a hcall, @a hdesc, f_write, @a context, @a errh). */ static inline int reset_write(HandlerCall *&hcall, const String &hdesc, const Element *context, ErrorHandler *errh = 0); /** @brief Create and initialize a read HandlerCall from @a hdesc. * @param hcall stores the HandlerCall result * @param e relevant element, if any * @param hname handler name * @param value write handler value * @param errh optional error handler * @return 0 on success, -EINVAL on failure * * Equivalent to * @link reset(HandlerCall*&, Element*, const String&, const String&, int, ErrorHandler*) reset@endlink(@a hcall, @a e, @a hname, @ value, f_write, @a context, @a errh). */ static inline int reset_write(HandlerCall *&hcall, Element *e, const String &hname, const String &value = String(), ErrorHandler *errh = 0); /** @brief Return the Element corresponding to this HandlerCall. * * Returns null if invalid. A global handler may return some * Router::root_element() or null. */ Element *element() const { return _e; } /** @brief Return the Handler corresponding to this HandlerCall. * * Returns Handler::blank_handler() if invalid. */ const Handler *handler() const { return _h; } /** @brief Return the write handler value and/or read handler parameters. * * Returns the empty string if invalid. */ const String &value() const { return initialized() ? _value : String::make_empty(); } /** @brief Sets the write handler value and/or read handler parameters. * @param value new value and/or parameters * * Does nothing if invalid. */ void set_value(const String &value) { if (initialized()) _value = value; } /** @brief Return a String that will parse into an equivalent HandlerCall. * * Will work even if the HandlerCall has not been initialized. */ String unparse() const; /** @brief Make this HandlerCall empty. * * Subsequent attempts to read, write, or initialize the HandlerCall will * fail. */ void clear() { _e = 0; _h = Handler::blank_handler(); _value = String(); } /** @cond never */ enum { CHECK_READ = f_read, OP_READ = f_read, h_read = f_read, CHECK_WRITE = f_write, OP_WRITE = f_write, h_write = f_write, PREINITIALIZE = f_preinitialize, h_preinitialize = f_preinitialize, UNQUOTE_PARAM = f_unquote_param, h_unquote_param = f_unquote_param }; /** @endcond never */ private: Element *_e; const Handler *_h; String _value; int parse(int flags, Element*, ErrorHandler*); int assign(Element*, const String&, const String&, int flags, ErrorHandler*); }; inline int HandlerCall::reset_read(HandlerCall*& hcall, const String& hdesc, const Element* context, ErrorHandler* errh) { return reset(hcall, hdesc, f_read, context, errh); } inline int HandlerCall::reset_write(HandlerCall*& hcall, const String& hdesc, const Element* context, ErrorHandler* errh) { return reset(hcall, hdesc, f_write, context, errh); } inline int HandlerCall::reset_read(HandlerCall*& hcall, Element* e, const String& hname, ErrorHandler* errh) { return reset(hcall, e, hname, String(), f_read, errh); } inline int HandlerCall::reset_write(HandlerCall*& hcall, Element* e, const String& hname, const String& value, ErrorHandler* errh) { return reset(hcall, e, hname, value, f_write, errh); } inline int HandlerCall::initialize_read(const Element* context, ErrorHandler* errh) { return initialize(f_read, context, errh); } inline int HandlerCall::initialize_write(const Element* context, ErrorHandler* errh) { return initialize(f_write, context, errh); } inline String HandlerCall::call_read(ErrorHandler *errh) const { return _h->call_read(_e, _value, errh); } inline int HandlerCall::call_write(ErrorHandler *errh) const { return _h->call_write(_value, _e, errh); } String cp_expand(const String &str, const VariableExpander &env, bool expand_quote, int depth); inline int HandlerCall::call_write(const VariableExpander &scope, ErrorHandler *errh) const { return _h->call_write(cp_expand(_value, scope, false, 0), _e, errh); } inline int HandlerCall::call_write(const String &value_ext, ErrorHandler *errh) const { if (_value && value_ext) return _h->call_write(_value + " " + value_ext, _e, errh); else return _h->call_write(_value ? _value : value_ext, _e, errh); } /** @brief Call a write handler specified by element and handler name. * @param e relevant element, if any * @param hname handler name * @param errh optional error handler * @return handler result, or -EINVAL on error * * Searches for a write handler named @a hname on element @a e. If the * handler exists, calls it (with empty write value) and returns the result. * If @a errh is nonnull, then errors, such as a missing handler or a * read-only handler, are reported there. If @a e is some router's @link * Router::root_element() root element@endlink, calls the global write * handler named @a hname on that router. */ inline int HandlerCall::call_write(Element *e, const String &hname, ErrorHandler *errh) { return call_write(e, hname, String(), errh); } /** @class HandlerCallArg @brief Parser class for handler call specifications. The constructor argument should generally be either HandlerCall::writable or HandlerCall::readable. For example: @code HandlerCall end_h; ... Args(...) ... .read("END_CALL", HandlerCallArg(HandlerCall::writable), end_h) ... @endcode */ struct HandlerCallArg { HandlerCallArg(int f) : flags(f) { } bool parse(const String &str, HandlerCall &result, const ArgContext &args); int flags; }; CLICK_ENDDECLS #endif