PageRenderTime 30ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/lleventdispatcher.h

https://bitbucket.org/lindenlab/viewer-beta/
C++ Header | 541 lines | 258 code | 54 blank | 229 comment | 1 complexity | 672eb9173ffdbae8ca494c34c51241b7 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lleventdispatcher.h
  3. * @author Nat Goodspeed
  4. * @date 2009-06-18
  5. * @brief Central mechanism for dispatching events by string name. This is
  6. * useful when you have a single LLEventPump listener on which you can
  7. * request different operations, vs. instantiating a different
  8. * LLEventPump for each such operation.
  9. *
  10. * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  11. * Second Life Viewer Source Code
  12. * Copyright (C) 2010, Linden Research, Inc.
  13. *
  14. * This library is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU Lesser General Public
  16. * License as published by the Free Software Foundation;
  17. * version 2.1 of the License only.
  18. *
  19. * This library is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  22. * Lesser General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU Lesser General Public
  25. * License along with this library; if not, write to the Free Software
  26. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  27. *
  28. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  29. * $/LicenseInfo$
  30. *
  31. * The invoker machinery that constructs a boost::fusion argument list for use
  32. * with boost::fusion::invoke() is derived from
  33. * http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp
  34. * whose license information is copied below:
  35. *
  36. * "(C) Copyright Tobias Schwinger
  37. *
  38. * Use modification and distribution are subject to the boost Software License,
  39. * Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)."
  40. */
  41. #if ! defined(LL_LLEVENTDISPATCHER_H)
  42. #define LL_LLEVENTDISPATCHER_H
  43. // nil is too generic a term to be allowed to be a global macro. In
  44. // particular, boost::fusion defines a 'class nil' (properly encapsulated in a
  45. // namespace) that a global 'nil' macro breaks badly.
  46. #if defined(nil)
  47. // Capture the value of the macro 'nil', hoping int is an appropriate type.
  48. static const int nil_(nil);
  49. // Now forget the macro.
  50. #undef nil
  51. // Finally, reintroduce 'nil' as a properly-scoped alias for the previously-
  52. // defined const 'nil_'. Make it static since otherwise it produces duplicate-
  53. // symbol link errors later.
  54. static const int& nil(nil_);
  55. #endif
  56. #include <string>
  57. #include <boost/shared_ptr.hpp>
  58. #include <boost/function.hpp>
  59. #include <boost/bind.hpp>
  60. #include <boost/iterator/transform_iterator.hpp>
  61. #include <boost/utility/enable_if.hpp>
  62. #include <boost/function_types/is_nonmember_callable_builtin.hpp>
  63. #include <boost/function_types/parameter_types.hpp>
  64. #include <boost/function_types/function_arity.hpp>
  65. #include <boost/type_traits/remove_cv.hpp>
  66. #include <boost/type_traits/remove_reference.hpp>
  67. #include <boost/fusion/include/push_back.hpp>
  68. #include <boost/fusion/include/cons.hpp>
  69. #include <boost/fusion/include/invoke.hpp>
  70. #include <boost/mpl/begin.hpp>
  71. #include <boost/mpl/end.hpp>
  72. #include <boost/mpl/next.hpp>
  73. #include <boost/mpl/deref.hpp>
  74. #include <typeinfo>
  75. #include "llevents.h"
  76. #include "llsdutil.h"
  77. class LLSD;
  78. /**
  79. * Given an LLSD map, examine a string-valued key and call a corresponding
  80. * callable. This class is designed to be contained by an LLEventPump
  81. * listener class that will register some of its own methods, though any
  82. * callable can be used.
  83. */
  84. class LL_COMMON_API LLEventDispatcher
  85. {
  86. public:
  87. LLEventDispatcher(const std::string& desc, const std::string& key);
  88. virtual ~LLEventDispatcher();
  89. /// @name Register functions accepting(const LLSD&)
  90. //@{
  91. /// Accept any C++ callable with the right signature, typically a
  92. /// boost::bind() expression
  93. typedef boost::function<void(const LLSD&)> Callable;
  94. /**
  95. * Register a @a callable by @a name. The passed @a callable accepts a
  96. * single LLSD value and uses it in any way desired, e.g. extract
  97. * parameters and call some other function. The optional @a required
  98. * parameter is used to validate the structure of each incoming event (see
  99. * llsd_matches()).
  100. */
  101. void add(const std::string& name,
  102. const std::string& desc,
  103. const Callable& callable,
  104. const LLSD& required=LLSD());
  105. /**
  106. * The case of a free function (or static method) accepting(const LLSD&)
  107. * could also be intercepted by the arbitrary-args overload below. Ensure
  108. * that it's directed to the Callable overload above instead.
  109. */
  110. void add(const std::string& name,
  111. const std::string& desc,
  112. void (*f)(const LLSD&),
  113. const LLSD& required=LLSD())
  114. {
  115. add(name, desc, Callable(f), required);
  116. }
  117. /**
  118. * Special case: a subclass of this class can pass an unbound member
  119. * function pointer (of an LLEventDispatcher subclass) without explicitly
  120. * specifying the <tt>boost::bind()</tt> expression. The passed @a method
  121. * accepts a single LLSD value, presumably containing other parameters.
  122. */
  123. template <class CLASS>
  124. void add(const std::string& name,
  125. const std::string& desc,
  126. void (CLASS::*method)(const LLSD&),
  127. const LLSD& required=LLSD())
  128. {
  129. addMethod<CLASS>(name, desc, method, required);
  130. }
  131. /// Overload for both const and non-const methods. The passed @a method
  132. /// accepts a single LLSD value, presumably containing other parameters.
  133. template <class CLASS>
  134. void add(const std::string& name,
  135. const std::string& desc,
  136. void (CLASS::*method)(const LLSD&) const,
  137. const LLSD& required=LLSD())
  138. {
  139. addMethod<CLASS>(name, desc, method, required);
  140. }
  141. //@}
  142. /// @name Register functions with arbitrary param lists
  143. //@{
  144. /**
  145. * Register a free function with arbitrary parameters. (This also works
  146. * for static class methods.)
  147. *
  148. * @note This supports functions with up to about 6 parameters -- after
  149. * that you start getting dismaying compile errors in which
  150. * boost::fusion::joint_view is mentioned a surprising number of times.
  151. *
  152. * When calling this name, pass an LLSD::Array. Each entry in turn will be
  153. * converted to the corresponding parameter type using LLSDParam.
  154. */
  155. template<typename Function>
  156. typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
  157. >::type add(const std::string& name,
  158. const std::string& desc,
  159. Function f);
  160. /**
  161. * Register a nonstatic class method with arbitrary parameters.
  162. *
  163. * @note This supports functions with up to about 6 parameters -- after
  164. * that you start getting dismaying compile errors in which
  165. * boost::fusion::joint_view is mentioned a surprising number of times.
  166. *
  167. * To cover cases such as a method on an LLSingleton we don't yet want to
  168. * instantiate, instead of directly storing an instance pointer, accept a
  169. * nullary callable returning a pointer/reference to the desired class
  170. * instance. If you already have an instance in hand,
  171. * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
  172. * produce suitable callables.
  173. *
  174. * When calling this name, pass an LLSD::Array. Each entry in turn will be
  175. * converted to the corresponding parameter type using LLSDParam.
  176. */
  177. template<typename Method, typename InstanceGetter>
  178. typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
  179. >::type add(const std::string& name,
  180. const std::string& desc,
  181. Method f,
  182. const InstanceGetter& getter);
  183. /**
  184. * Register a free function with arbitrary parameters. (This also works
  185. * for static class methods.)
  186. *
  187. * @note This supports functions with up to about 6 parameters -- after
  188. * that you start getting dismaying compile errors in which
  189. * boost::fusion::joint_view is mentioned a surprising number of times.
  190. *
  191. * Pass an LLSD::Array of parameter names, and optionally another
  192. * LLSD::Array of default parameter values, a la LLSDArgsMapper.
  193. *
  194. * When calling this name, pass an LLSD::Map. We will internally generate
  195. * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
  196. * to the corresponding parameter type using LLSDParam.
  197. */
  198. template<typename Function>
  199. typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
  200. >::type add(const std::string& name,
  201. const std::string& desc,
  202. Function f,
  203. const LLSD& params,
  204. const LLSD& defaults=LLSD());
  205. /**
  206. * Register a nonstatic class method with arbitrary parameters.
  207. *
  208. * @note This supports functions with up to about 6 parameters -- after
  209. * that you start getting dismaying compile errors in which
  210. * boost::fusion::joint_view is mentioned a surprising number of times.
  211. *
  212. * To cover cases such as a method on an LLSingleton we don't yet want to
  213. * instantiate, instead of directly storing an instance pointer, accept a
  214. * nullary callable returning a pointer/reference to the desired class
  215. * instance. If you already have an instance in hand,
  216. * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
  217. * produce suitable callables.
  218. *
  219. * Pass an LLSD::Array of parameter names, and optionally another
  220. * LLSD::Array of default parameter values, a la LLSDArgsMapper.
  221. *
  222. * When calling this name, pass an LLSD::Map. We will internally generate
  223. * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
  224. * to the corresponding parameter type using LLSDParam.
  225. */
  226. template<typename Method, typename InstanceGetter>
  227. typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
  228. >::type add(const std::string& name,
  229. const std::string& desc,
  230. Method f,
  231. const InstanceGetter& getter,
  232. const LLSD& params,
  233. const LLSD& defaults=LLSD());
  234. //@}
  235. /// Unregister a callable
  236. bool remove(const std::string& name);
  237. /// Call a registered callable with an explicitly-specified name. If no
  238. /// such callable exists, die with LL_ERRS. If the @a event fails to match
  239. /// the @a required prototype specified at add() time, die with LL_ERRS.
  240. void operator()(const std::string& name, const LLSD& event) const;
  241. /// Call a registered callable with an explicitly-specified name and
  242. /// return <tt>true</tt>. If no such callable exists, return
  243. /// <tt>false</tt>. If the @a event fails to match the @a required
  244. /// prototype specified at add() time, die with LL_ERRS.
  245. bool try_call(const std::string& name, const LLSD& event) const;
  246. /// Extract the @a key value from the incoming @a event, and call the
  247. /// callable whose name is specified by that map @a key. If no such
  248. /// callable exists, die with LL_ERRS. If the @a event fails to match the
  249. /// @a required prototype specified at add() time, die with LL_ERRS.
  250. void operator()(const LLSD& event) const;
  251. /// Extract the @a key value from the incoming @a event, call the callable
  252. /// whose name is specified by that map @a key and return <tt>true</tt>.
  253. /// If no such callable exists, return <tt>false</tt>. If the @a event
  254. /// fails to match the @a required prototype specified at add() time, die
  255. /// with LL_ERRS.
  256. bool try_call(const LLSD& event) const;
  257. /// @name Iterate over defined names
  258. //@{
  259. typedef std::pair<std::string, std::string> NameDesc;
  260. private:
  261. struct DispatchEntry
  262. {
  263. DispatchEntry(const std::string& desc);
  264. virtual ~DispatchEntry() {} // suppress MSVC warning, sigh
  265. std::string mDesc;
  266. virtual void call(const std::string& desc, const LLSD& event) const = 0;
  267. virtual LLSD addMetadata(LLSD) const = 0;
  268. };
  269. // Tried using boost::ptr_map<std::string, DispatchEntry>, but ptr_map<>
  270. // wants its value type to be "clonable," even just to dereference an
  271. // iterator. I don't want to clone entries -- if I have to copy an entry
  272. // around, I want it to continue pointing to the same DispatchEntry
  273. // subclass object. However, I definitely want DispatchMap to destroy
  274. // DispatchEntry if no references are outstanding at the time an entry is
  275. // removed. This looks like a job for boost::shared_ptr.
  276. typedef std::map<std::string, boost::shared_ptr<DispatchEntry> > DispatchMap;
  277. public:
  278. /// We want the flexibility to redefine what data we store per name,
  279. /// therefore our public interface doesn't expose DispatchMap iterators,
  280. /// or DispatchMap itself, or DispatchEntry. Instead we explicitly
  281. /// transform each DispatchMap item to NameDesc on dereferencing.
  282. typedef boost::transform_iterator<NameDesc(*)(const DispatchMap::value_type&), DispatchMap::const_iterator> const_iterator;
  283. const_iterator begin() const
  284. {
  285. return boost::make_transform_iterator(mDispatch.begin(), makeNameDesc);
  286. }
  287. const_iterator end() const
  288. {
  289. return boost::make_transform_iterator(mDispatch.end(), makeNameDesc);
  290. }
  291. //@}
  292. /// Get information about a specific Callable
  293. LLSD getMetadata(const std::string& name) const;
  294. /// Retrieve the LLSD key we use for one-arg <tt>operator()</tt> method
  295. std::string getDispatchKey() const { return mKey; }
  296. private:
  297. template <class CLASS, typename METHOD>
  298. void addMethod(const std::string& name, const std::string& desc,
  299. const METHOD& method, const LLSD& required)
  300. {
  301. CLASS* downcast = dynamic_cast<CLASS*>(this);
  302. if (! downcast)
  303. {
  304. addFail(name, typeid(CLASS).name());
  305. }
  306. else
  307. {
  308. add(name, desc, boost::bind(method, downcast, _1), required);
  309. }
  310. }
  311. void addFail(const std::string& name, const std::string& classname) const;
  312. std::string mDesc, mKey;
  313. DispatchMap mDispatch;
  314. static NameDesc makeNameDesc(const DispatchMap::value_type& item)
  315. {
  316. return NameDesc(item.first, item.second->mDesc);
  317. }
  318. struct LLSDDispatchEntry;
  319. struct ParamsDispatchEntry;
  320. struct ArrayParamsDispatchEntry;
  321. struct MapParamsDispatchEntry;
  322. // Step 2 of parameter analysis. Instantiating invoker<some_function_type>
  323. // implicitly sets its From and To parameters to the (compile time) begin
  324. // and end iterators over that function's parameter types.
  325. template< typename Function
  326. , class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type
  327. , class To = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
  328. >
  329. struct invoker;
  330. // deliver LLSD arguments one at a time
  331. typedef boost::function<LLSD()> args_source;
  332. // obtain args from an args_source to build param list and call target
  333. // function
  334. typedef boost::function<void(const args_source&)> invoker_function;
  335. template <typename Function>
  336. invoker_function make_invoker(Function f);
  337. template <typename Method, typename InstanceGetter>
  338. invoker_function make_invoker(Method f, const InstanceGetter& getter);
  339. void addArrayParamsDispatchEntry(const std::string& name,
  340. const std::string& desc,
  341. const invoker_function& invoker,
  342. LLSD::Integer arity);
  343. void addMapParamsDispatchEntry(const std::string& name,
  344. const std::string& desc,
  345. const invoker_function& invoker,
  346. const LLSD& params,
  347. const LLSD& defaults);
  348. };
  349. /*****************************************************************************
  350. * LLEventDispatcher template implementation details
  351. *****************************************************************************/
  352. // Step 3 of parameter analysis, the recursive case.
  353. template<typename Function, class From, class To>
  354. struct LLEventDispatcher::invoker
  355. {
  356. template<typename T>
  357. struct remove_cv_ref
  358. : boost::remove_cv< typename boost::remove_reference<T>::type >
  359. { };
  360. // apply() accepts an arbitrary boost::fusion sequence as args. It
  361. // examines the next parameter type in the parameter-types sequence
  362. // bounded by From and To, obtains the next LLSD object from the passed
  363. // args_source and constructs an LLSDParam of appropriate type to try
  364. // to convert the value. It then recurs with the next parameter-types
  365. // iterator, passing the args sequence thus far.
  366. template<typename Args>
  367. static inline
  368. void apply(Function func, const args_source& argsrc, Args const & args)
  369. {
  370. typedef typename boost::mpl::deref<From>::type arg_type;
  371. typedef typename boost::mpl::next<From>::type next_iter_type;
  372. typedef typename remove_cv_ref<arg_type>::type plain_arg_type;
  373. invoker<Function, next_iter_type, To>::apply
  374. ( func, argsrc, boost::fusion::push_back(args, LLSDParam<plain_arg_type>(argsrc())));
  375. }
  376. // Special treatment for instance (first) parameter of a non-static member
  377. // function. Accept the instance-getter callable, calling that to produce
  378. // the first args value. Since we know we're at the top of the recursion
  379. // chain, we need not also require a partial args sequence from our caller.
  380. template <typename InstanceGetter>
  381. static inline
  382. void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter)
  383. {
  384. typedef typename boost::mpl::next<From>::type next_iter_type;
  385. // Instead of grabbing the first item from argsrc and making an
  386. // LLSDParam of it, call getter() and pass that as the instance param.
  387. invoker<Function, next_iter_type, To>::apply
  388. ( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter())));
  389. }
  390. };
  391. // Step 4 of parameter analysis, the leaf case. When the general
  392. // invoker<Function, From, To> logic has advanced From until it matches To,
  393. // the compiler will pick this template specialization.
  394. template<typename Function, class To>
  395. struct LLEventDispatcher::invoker<Function,To,To>
  396. {
  397. // the argument list is complete, now call the function
  398. template<typename Args>
  399. static inline
  400. void apply(Function func, const args_source&, Args const & args)
  401. {
  402. boost::fusion::invoke(func, args);
  403. }
  404. };
  405. template<typename Function>
  406. typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
  407. LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)
  408. {
  409. // Construct an invoker_function, a callable accepting const args_source&.
  410. // Add to DispatchMap an ArrayParamsDispatchEntry that will handle the
  411. // caller's LLSD::Array.
  412. addArrayParamsDispatchEntry(name, desc, make_invoker(f),
  413. boost::function_types::function_arity<Function>::value);
  414. }
  415. template<typename Method, typename InstanceGetter>
  416. typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
  417. LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
  418. const InstanceGetter& getter)
  419. {
  420. // Subtract 1 from the compile-time arity because the getter takes care of
  421. // the first parameter. We only need (arity - 1) additional arguments.
  422. addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter),
  423. boost::function_types::function_arity<Method>::value - 1);
  424. }
  425. template<typename Function>
  426. typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
  427. LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f,
  428. const LLSD& params, const LLSD& defaults)
  429. {
  430. // See comments for previous is_nonmember_callable_builtin add().
  431. addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults);
  432. }
  433. template<typename Method, typename InstanceGetter>
  434. typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
  435. LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
  436. const InstanceGetter& getter,
  437. const LLSD& params, const LLSD& defaults)
  438. {
  439. addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);
  440. }
  441. template <typename Function>
  442. LLEventDispatcher::invoker_function
  443. LLEventDispatcher::make_invoker(Function f)
  444. {
  445. // Step 1 of parameter analysis, the top of the recursion. Passing a
  446. // suitable f (see add()'s enable_if condition) to this method causes it
  447. // to infer the function type; specifying that function type to invoker<>
  448. // causes it to fill in the begin/end MPL iterators over the function's
  449. // list of parameter types.
  450. // While normally invoker::apply() could infer its template type from the
  451. // boost::fusion::nil parameter value, here we must be explicit since
  452. // we're boost::bind()ing it rather than calling it directly.
  453. return boost::bind(&invoker<Function>::template apply<boost::fusion::nil>,
  454. f,
  455. _1,
  456. boost::fusion::nil());
  457. }
  458. template <typename Method, typename InstanceGetter>
  459. LLEventDispatcher::invoker_function
  460. LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
  461. {
  462. // Use invoker::method_apply() to treat the instance (first) arg specially.
  463. return boost::bind(&invoker<Method>::template method_apply<InstanceGetter>,
  464. f,
  465. _1,
  466. getter);
  467. }
  468. /*****************************************************************************
  469. * LLDispatchListener
  470. *****************************************************************************/
  471. /**
  472. * Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
  473. * that contains (or derives from) LLDispatchListener need only specify the
  474. * LLEventPump name and dispatch key, and add() its methods. Incoming events
  475. * will automatically be dispatched.
  476. */
  477. class LL_COMMON_API LLDispatchListener: public LLEventDispatcher
  478. {
  479. public:
  480. LLDispatchListener(const std::string& pumpname, const std::string& key);
  481. std::string getPumpName() const { return mPump.getName(); }
  482. private:
  483. bool process(const LLSD& event);
  484. LLEventStream mPump;
  485. LLTempBoundListener mBoundListener;
  486. };
  487. #endif /* ! defined(LL_LLEVENTDISPATCHER_H) */