/indra/llcommon/lleventdispatcher.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 672 lines · 423 code · 56 blank · 193 comment · 57 complexity · fd77be389a54e4d834a3278638616a20 MD5 · raw file

  1. /**
  2. * @file lleventdispatcher.cpp
  3. * @author Nat Goodspeed
  4. * @date 2009-06-18
  5. * @brief Implementation for lleventdispatcher.
  6. *
  7. * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  8. * Second Life Viewer Source Code
  9. * Copyright (C) 2010, Linden Research, Inc.
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation;
  14. * version 2.1 of the License only.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  26. * $/LicenseInfo$
  27. */
  28. #if LL_WINDOWS
  29. #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
  30. #endif
  31. // Precompiled header
  32. #include "linden_common.h"
  33. // associated header
  34. #include "lleventdispatcher.h"
  35. // STL headers
  36. // std headers
  37. // external library headers
  38. // other Linden headers
  39. #include "llevents.h"
  40. #include "llerror.h"
  41. #include "llsdutil.h"
  42. #include "stringize.h"
  43. #include <memory> // std::auto_ptr
  44. /*****************************************************************************
  45. * LLSDArgsSource
  46. *****************************************************************************/
  47. /**
  48. * Store an LLSD array, producing its elements one at a time. Die with LL_ERRS
  49. * if the consumer requests more elements than the array contains.
  50. */
  51. class LL_COMMON_API LLSDArgsSource
  52. {
  53. public:
  54. LLSDArgsSource(const std::string function, const LLSD& args);
  55. ~LLSDArgsSource();
  56. LLSD next();
  57. void done() const;
  58. private:
  59. std::string _function;
  60. LLSD _args;
  61. LLSD::Integer _index;
  62. };
  63. LLSDArgsSource::LLSDArgsSource(const std::string function, const LLSD& args):
  64. _function(function),
  65. _args(args),
  66. _index(0)
  67. {
  68. if (! (_args.isUndefined() || _args.isArray()))
  69. {
  70. LL_ERRS("LLSDArgsSource") << _function << " needs an args array instead of "
  71. << _args << LL_ENDL;
  72. }
  73. }
  74. LLSDArgsSource::~LLSDArgsSource()
  75. {
  76. done();
  77. }
  78. LLSD LLSDArgsSource::next()
  79. {
  80. if (_index >= _args.size())
  81. {
  82. LL_ERRS("LLSDArgsSource") << _function << " requires more arguments than the "
  83. << _args.size() << " provided: " << _args << LL_ENDL;
  84. }
  85. return _args[_index++];
  86. }
  87. void LLSDArgsSource::done() const
  88. {
  89. if (_index < _args.size())
  90. {
  91. LL_WARNS("LLSDArgsSource") << _function << " only consumed " << _index
  92. << " of the " << _args.size() << " arguments provided: "
  93. << _args << LL_ENDL;
  94. }
  95. }
  96. /*****************************************************************************
  97. * LLSDArgsMapper
  98. *****************************************************************************/
  99. /**
  100. * From a formal parameters description and a map of arguments, construct an
  101. * arguments array.
  102. *
  103. * That is, given:
  104. * - an LLSD array of length n containing parameter-name strings,
  105. * corresponding to the arguments of a function of interest
  106. * - an LLSD collection specifying default parameter values, either:
  107. * - an LLSD array of length m <= n, matching the rightmost m params, or
  108. * - an LLSD map explicitly stating default name=value pairs
  109. * - an LLSD map of parameter names and actual values for a particular
  110. * function call
  111. * construct an LLSD array of actual argument values for this function call.
  112. *
  113. * The parameter-names array and the defaults collection describe the function
  114. * being called. The map might vary with every call, providing argument values
  115. * for the described parameters.
  116. *
  117. * The array of parameter names must match the number of parameters expected
  118. * by the function of interest.
  119. *
  120. * If you pass a map of default parameter values, it provides default values
  121. * as you might expect. It is an error to specify a default value for a name
  122. * not listed in the parameters array.
  123. *
  124. * If you pass an array of default parameter values, it is mapped to the
  125. * rightmost m of the n parameter names. It is an error if the default-values
  126. * array is longer than the parameter-names array. Consider the following
  127. * parameter names: ["a", "b", "c", "d"].
  128. *
  129. * - An empty array of default values (or an isUndefined() value) asserts that
  130. * every one of the above parameter names is required.
  131. * - An array of four default values [1, 2, 3, 4] asserts that every one of
  132. * the above parameters is optional. If the current parameter map is empty,
  133. * they will be passed to the function as [1, 2, 3, 4].
  134. * - An array of two default values [11, 12] asserts that parameters "a" and
  135. * "b" are required, while "c" and "d" are optional, having default values
  136. * "c"=11 and "d"=12.
  137. *
  138. * The arguments array is constructed as follows:
  139. *
  140. * - Arguments-map keys not found in the parameter-names array are ignored.
  141. * - Entries from the map provide values for an improper subset of the
  142. * parameters named in the parameter-names array. This results in a
  143. * tentative values array with "holes." (size of map) + (number of holes) =
  144. * (size of names array)
  145. * - Holes are filled with the default values.
  146. * - Any remaining holes constitute an error.
  147. */
  148. class LL_COMMON_API LLSDArgsMapper
  149. {
  150. public:
  151. /// Accept description of function: function name, param names, param
  152. /// default values
  153. LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults);
  154. /// Given arguments map, return LLSD::Array of parameter values, or LL_ERRS.
  155. LLSD map(const LLSD& argsmap) const;
  156. private:
  157. static std::string formatlist(const LLSD&);
  158. // The function-name string is purely descriptive. We want error messages
  159. // to be able to indicate which function's LLSDArgsMapper has the problem.
  160. std::string _function;
  161. // Store the names array pretty much as given.
  162. LLSD _names;
  163. // Though we're handed an array of name strings, it's more useful to us to
  164. // store it as a map from name string to position index. Of course that's
  165. // easy to generate from the incoming names array, but why do it more than
  166. // once?
  167. typedef std::map<LLSD::String, LLSD::Integer> IndexMap;
  168. IndexMap _indexes;
  169. // Generated array of default values, aligned with the array of param names.
  170. LLSD _defaults;
  171. // Indicate whether we have a default value for each param.
  172. typedef std::vector<char> FilledVector;
  173. FilledVector _has_dft;
  174. };
  175. LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
  176. const LLSD& names, const LLSD& defaults):
  177. _function(function),
  178. _names(names),
  179. _has_dft(names.size())
  180. {
  181. if (! (_names.isUndefined() || _names.isArray()))
  182. {
  183. LL_ERRS("LLSDArgsMapper") << function << " names must be an array, not " << names << LL_ENDL;
  184. }
  185. LLSD::Integer nparams(_names.size());
  186. // From _names generate _indexes.
  187. for (LLSD::Integer ni = 0, nend = _names.size(); ni < nend; ++ni)
  188. {
  189. _indexes[_names[ni]] = ni;
  190. }
  191. // Presize _defaults() array so we don't have to resize it more than once.
  192. // All entries are initialized to LLSD(); but since _has_dft is still all
  193. // 0, they're all "holes" for now.
  194. if (nparams)
  195. {
  196. _defaults[nparams - 1] = LLSD();
  197. }
  198. if (defaults.isUndefined() || defaults.isArray())
  199. {
  200. LLSD::Integer ndefaults = defaults.size();
  201. // defaults is a (possibly empty) array. Right-align it with names.
  202. if (ndefaults > nparams)
  203. {
  204. LL_ERRS("LLSDArgsMapper") << function << " names array " << names
  205. << " shorter than defaults array " << defaults << LL_ENDL;
  206. }
  207. // Offset by which we slide defaults array right to right-align with
  208. // _names array
  209. LLSD::Integer offset = nparams - ndefaults;
  210. // Fill rightmost _defaults entries from defaults, and mark them as
  211. // filled
  212. for (LLSD::Integer i = 0, iend = ndefaults; i < iend; ++i)
  213. {
  214. _defaults[i + offset] = defaults[i];
  215. _has_dft[i + offset] = 1;
  216. }
  217. }
  218. else if (defaults.isMap())
  219. {
  220. // defaults is a map. Use it to populate the _defaults array.
  221. LLSD bogus;
  222. for (LLSD::map_const_iterator mi(defaults.beginMap()), mend(defaults.endMap());
  223. mi != mend; ++mi)
  224. {
  225. IndexMap::const_iterator ixit(_indexes.find(mi->first));
  226. if (ixit == _indexes.end())
  227. {
  228. bogus.append(mi->first);
  229. continue;
  230. }
  231. LLSD::Integer pos = ixit->second;
  232. // Store default value at that position in the _defaults array.
  233. _defaults[pos] = mi->second;
  234. // Don't forget to record the fact that we've filled this
  235. // position.
  236. _has_dft[pos] = 1;
  237. }
  238. if (bogus.size())
  239. {
  240. LL_ERRS("LLSDArgsMapper") << function << " defaults specified for nonexistent params "
  241. << formatlist(bogus) << LL_ENDL;
  242. }
  243. }
  244. else
  245. {
  246. LL_ERRS("LLSDArgsMapper") << function << " defaults must be a map or an array, not "
  247. << defaults << LL_ENDL;
  248. }
  249. }
  250. LLSD LLSDArgsMapper::map(const LLSD& argsmap) const
  251. {
  252. if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray()))
  253. {
  254. LL_ERRS("LLSDArgsMapper") << _function << " map() needs a map or array, not "
  255. << argsmap << LL_ENDL;
  256. }
  257. // Initialize the args array. Indexing a non-const LLSD array grows it
  258. // to appropriate size, but we don't want to resize this one on each
  259. // new operation. Just make it as big as we need before we start
  260. // stuffing values into it.
  261. LLSD args(LLSD::emptyArray());
  262. if (_defaults.size() == 0)
  263. {
  264. // If this function requires no arguments, fast exit. (Don't try to
  265. // assign to args[-1].)
  266. return args;
  267. }
  268. args[_defaults.size() - 1] = LLSD();
  269. // Get a vector of chars to indicate holes. It's tempting to just scan
  270. // for LLSD::isUndefined() values after filling the args array from
  271. // the map, but it's plausible for caller to explicitly pass
  272. // isUndefined() as the value of some parameter name. That's legal
  273. // since isUndefined() has well-defined conversions (default value)
  274. // for LLSD data types. So use a whole separate array for detecting
  275. // holes. (Avoid std::vector<bool> which is known to be odd -- can we
  276. // iterate?)
  277. FilledVector filled(args.size());
  278. if (argsmap.isArray())
  279. {
  280. // Fill args from array. If there are too many args in passed array,
  281. // ignore the rest.
  282. LLSD::Integer size(argsmap.size());
  283. if (size > args.size())
  284. {
  285. // We don't just use std::min() because we want to sneak in this
  286. // warning if caller passes too many args.
  287. LL_WARNS("LLSDArgsMapper") << _function << " needs " << args.size()
  288. << " params, ignoring last " << (size - args.size())
  289. << " of passed " << size << ": " << argsmap << LL_ENDL;
  290. size = args.size();
  291. }
  292. for (LLSD::Integer i(0); i < size; ++i)
  293. {
  294. // Copy the actual argument from argsmap
  295. args[i] = argsmap[i];
  296. // Note that it's been filled
  297. filled[i] = 1;
  298. }
  299. }
  300. else
  301. {
  302. // argsmap is in fact a map. Walk the map.
  303. for (LLSD::map_const_iterator mi(argsmap.beginMap()), mend(argsmap.endMap());
  304. mi != mend; ++mi)
  305. {
  306. // mi->first is a parameter-name string, with mi->second its
  307. // value. Look up the name's position index in _indexes.
  308. IndexMap::const_iterator ixit(_indexes.find(mi->first));
  309. if (ixit == _indexes.end())
  310. {
  311. // Allow for a map containing more params than were passed in
  312. // our names array. Caller typically receives a map containing
  313. // the function name, cruft such as reqid, etc. Ignore keys
  314. // not defined in _indexes.
  315. LL_DEBUGS("LLSDArgsMapper") << _function << " ignoring "
  316. << mi->first << "=" << mi->second << LL_ENDL;
  317. continue;
  318. }
  319. LLSD::Integer pos = ixit->second;
  320. // Store the value at that position in the args array.
  321. args[pos] = mi->second;
  322. // Don't forget to record the fact that we've filled this
  323. // position.
  324. filled[pos] = 1;
  325. }
  326. }
  327. // Fill any remaining holes from _defaults.
  328. LLSD unfilled(LLSD::emptyArray());
  329. for (LLSD::Integer i = 0, iend = args.size(); i < iend; ++i)
  330. {
  331. if (! filled[i])
  332. {
  333. // If there's no default value for this parameter, that's an
  334. // error.
  335. if (! _has_dft[i])
  336. {
  337. unfilled.append(_names[i]);
  338. }
  339. else
  340. {
  341. args[i] = _defaults[i];
  342. }
  343. }
  344. }
  345. // If any required args -- args without defaults -- were left unfilled
  346. // by argsmap, that's a problem.
  347. if (unfilled.size())
  348. {
  349. LL_ERRS("LLSDArgsMapper") << _function << " missing required arguments "
  350. << formatlist(unfilled) << " from " << argsmap << LL_ENDL;
  351. }
  352. // done
  353. return args;
  354. }
  355. std::string LLSDArgsMapper::formatlist(const LLSD& list)
  356. {
  357. std::ostringstream out;
  358. const char* delim = "";
  359. for (LLSD::array_const_iterator li(list.beginArray()), lend(list.endArray());
  360. li != lend; ++li)
  361. {
  362. out << delim << li->asString();
  363. delim = ", ";
  364. }
  365. return out.str();
  366. }
  367. LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
  368. mDesc(desc),
  369. mKey(key)
  370. {
  371. }
  372. LLEventDispatcher::~LLEventDispatcher()
  373. {
  374. }
  375. /**
  376. * DispatchEntry subclass used for callables accepting(const LLSD&)
  377. */
  378. struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry
  379. {
  380. LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required):
  381. DispatchEntry(desc),
  382. mFunc(func),
  383. mRequired(required)
  384. {}
  385. Callable mFunc;
  386. LLSD mRequired;
  387. virtual void call(const std::string& desc, const LLSD& event) const
  388. {
  389. // Validate the syntax of the event itself.
  390. std::string mismatch(llsd_matches(mRequired, event));
  391. if (! mismatch.empty())
  392. {
  393. LL_ERRS("LLEventDispatcher") << desc << ": bad request: " << mismatch << LL_ENDL;
  394. }
  395. // Event syntax looks good, go for it!
  396. mFunc(event);
  397. }
  398. virtual LLSD addMetadata(LLSD meta) const
  399. {
  400. meta["required"] = mRequired;
  401. return meta;
  402. }
  403. };
  404. /**
  405. * DispatchEntry subclass for passing LLSD to functions accepting
  406. * arbitrary argument types (convertible via LLSDParam)
  407. */
  408. struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry
  409. {
  410. ParamsDispatchEntry(const std::string& desc, const invoker_function& func):
  411. DispatchEntry(desc),
  412. mInvoker(func)
  413. {}
  414. invoker_function mInvoker;
  415. virtual void call(const std::string& desc, const LLSD& event) const
  416. {
  417. LLSDArgsSource src(desc, event);
  418. mInvoker(boost::bind(&LLSDArgsSource::next, boost::ref(src)));
  419. }
  420. };
  421. /**
  422. * DispatchEntry subclass for dispatching LLSD::Array to functions accepting
  423. * arbitrary argument types (convertible via LLSDParam)
  424. */
  425. struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
  426. {
  427. ArrayParamsDispatchEntry(const std::string& desc, const invoker_function& func,
  428. LLSD::Integer arity):
  429. ParamsDispatchEntry(desc, func),
  430. mArity(arity)
  431. {}
  432. LLSD::Integer mArity;
  433. virtual LLSD addMetadata(LLSD meta) const
  434. {
  435. LLSD array(LLSD::emptyArray());
  436. // Resize to number of arguments required
  437. if (mArity)
  438. array[mArity - 1] = LLSD();
  439. llassert_always(array.size() == mArity);
  440. meta["required"] = array;
  441. return meta;
  442. }
  443. };
  444. /**
  445. * DispatchEntry subclass for dispatching LLSD::Map to functions accepting
  446. * arbitrary argument types (convertible via LLSDParam)
  447. */
  448. struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
  449. {
  450. MapParamsDispatchEntry(const std::string& name, const std::string& desc,
  451. const invoker_function& func,
  452. const LLSD& params, const LLSD& defaults):
  453. ParamsDispatchEntry(desc, func),
  454. mMapper(name, params, defaults),
  455. mRequired(LLSD::emptyMap())
  456. {
  457. // Build the set of all param keys, then delete the ones that are
  458. // optional. What's left are the ones that are required.
  459. for (LLSD::array_const_iterator pi(params.beginArray()), pend(params.endArray());
  460. pi != pend; ++pi)
  461. {
  462. mRequired[pi->asString()] = LLSD();
  463. }
  464. if (defaults.isArray() || defaults.isUndefined())
  465. {
  466. // Right-align the params and defaults arrays.
  467. LLSD::Integer offset = params.size() - defaults.size();
  468. // Now the name of every defaults[i] is at params[i + offset].
  469. for (LLSD::Integer i(0), iend(defaults.size()); i < iend; ++i)
  470. {
  471. // Erase this optional param from mRequired.
  472. mRequired.erase(params[i + offset].asString());
  473. // Instead, make an entry in mOptional with the default
  474. // param's name and value.
  475. mOptional[params[i + offset].asString()] = defaults[i];
  476. }
  477. }
  478. else if (defaults.isMap())
  479. {
  480. // if defaults is already a map, then it's already in the form we
  481. // intend to deliver in metadata
  482. mOptional = defaults;
  483. // Just delete from mRequired every key appearing in mOptional.
  484. for (LLSD::map_const_iterator mi(mOptional.beginMap()), mend(mOptional.endMap());
  485. mi != mend; ++mi)
  486. {
  487. mRequired.erase(mi->first);
  488. }
  489. }
  490. }
  491. LLSDArgsMapper mMapper;
  492. LLSD mRequired;
  493. LLSD mOptional;
  494. virtual void call(const std::string& desc, const LLSD& event) const
  495. {
  496. // Just convert from LLSD::Map to LLSD::Array using mMapper, then pass
  497. // to base-class call() method.
  498. ParamsDispatchEntry::call(desc, mMapper.map(event));
  499. }
  500. virtual LLSD addMetadata(LLSD meta) const
  501. {
  502. meta["required"] = mRequired;
  503. meta["optional"] = mOptional;
  504. return meta;
  505. }
  506. };
  507. void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name,
  508. const std::string& desc,
  509. const invoker_function& invoker,
  510. LLSD::Integer arity)
  511. {
  512. mDispatch.insert(
  513. DispatchMap::value_type(name, DispatchMap::mapped_type(
  514. new ArrayParamsDispatchEntry(desc, invoker, arity))));
  515. }
  516. void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
  517. const std::string& desc,
  518. const invoker_function& invoker,
  519. const LLSD& params,
  520. const LLSD& defaults)
  521. {
  522. mDispatch.insert(
  523. DispatchMap::value_type(name, DispatchMap::mapped_type(
  524. new MapParamsDispatchEntry(name, desc, invoker, params, defaults))));
  525. }
  526. /// Register a callable by name
  527. void LLEventDispatcher::add(const std::string& name, const std::string& desc,
  528. const Callable& callable, const LLSD& required)
  529. {
  530. mDispatch.insert(
  531. DispatchMap::value_type(name, DispatchMap::mapped_type(
  532. new LLSDDispatchEntry(desc, callable, required))));
  533. }
  534. void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
  535. {
  536. LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ")::add(" << name
  537. << "): " << classname << " is not a subclass "
  538. << "of LLEventDispatcher" << LL_ENDL;
  539. }
  540. /// Unregister a callable
  541. bool LLEventDispatcher::remove(const std::string& name)
  542. {
  543. DispatchMap::iterator found = mDispatch.find(name);
  544. if (found == mDispatch.end())
  545. {
  546. return false;
  547. }
  548. mDispatch.erase(found);
  549. return true;
  550. }
  551. /// Call a registered callable with an explicitly-specified name. If no
  552. /// such callable exists, die with LL_ERRS.
  553. void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
  554. {
  555. if (! try_call(name, event))
  556. {
  557. LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
  558. << "' not found" << LL_ENDL;
  559. }
  560. }
  561. /// Extract the @a key value from the incoming @a event, and call the
  562. /// callable whose name is specified by that map @a key. If no such
  563. /// callable exists, die with LL_ERRS.
  564. void LLEventDispatcher::operator()(const LLSD& event) const
  565. {
  566. // This could/should be implemented in terms of the two-arg overload.
  567. // However -- we can produce a more informative error message.
  568. std::string name(event[mKey]);
  569. if (! try_call(name, event))
  570. {
  571. LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
  572. << " value '" << name << "'" << LL_ENDL;
  573. }
  574. }
  575. bool LLEventDispatcher::try_call(const LLSD& event) const
  576. {
  577. return try_call(event[mKey], event);
  578. }
  579. bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
  580. {
  581. DispatchMap::const_iterator found = mDispatch.find(name);
  582. if (found == mDispatch.end())
  583. {
  584. return false;
  585. }
  586. // Found the name, so it's plausible to even attempt the call.
  587. found->second->call(STRINGIZE("LLEventDispatcher(" << mDesc << ") calling '" << name << "'"),
  588. event);
  589. return true; // tell caller we were able to call
  590. }
  591. LLSD LLEventDispatcher::getMetadata(const std::string& name) const
  592. {
  593. DispatchMap::const_iterator found = mDispatch.find(name);
  594. if (found == mDispatch.end())
  595. {
  596. return LLSD();
  597. }
  598. LLSD meta;
  599. meta["name"] = name;
  600. meta["desc"] = found->second->mDesc;
  601. return found->second->addMetadata(meta);
  602. }
  603. LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
  604. LLEventDispatcher(pumpname, key),
  605. mPump(pumpname, true), // allow tweaking for uniqueness
  606. mBoundListener(mPump.listen("self", boost::bind(&LLDispatchListener::process, this, _1)))
  607. {
  608. }
  609. bool LLDispatchListener::process(const LLSD& event)
  610. {
  611. (*this)(event);
  612. return false;
  613. }
  614. LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):
  615. mDesc(desc)
  616. {}