match_results.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. /*
  2. *
  3. * Copyright (c) 1998-2009
  4. * John Maddock
  5. *
  6. * Use, modification and distribution are subject to the
  7. * Boost Software License, Version 1.0. (See accompanying file
  8. * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. *
  10. */
  11. /*
  12. * LOCATION: see http://www.boost.org for most recent version.
  13. * FILE match_results.cpp
  14. * VERSION see <boost/version.hpp>
  15. * DESCRIPTION: Declares template class match_results.
  16. */
  17. #ifndef BOOST_REGEX_V5_MATCH_RESULTS_HPP
  18. #define BOOST_REGEX_V5_MATCH_RESULTS_HPP
  19. namespace boost{
  20. #ifdef BOOST_REGEX_MSVC
  21. #pragma warning(push)
  22. #pragma warning(disable : 4251 4459)
  23. #if BOOST_REGEX_MSVC < 1700
  24. # pragma warning(disable : 4231)
  25. #endif
  26. # if BOOST_REGEX_MSVC < 1600
  27. # pragma warning(disable : 4660)
  28. # endif
  29. #endif
  30. namespace BOOST_REGEX_DETAIL_NS{
  31. class named_subexpressions;
  32. }
  33. template <class BidiIterator, class Allocator>
  34. class match_results
  35. {
  36. private:
  37. typedef std::vector<sub_match<BidiIterator>, Allocator> vector_type;
  38. public:
  39. typedef sub_match<BidiIterator> value_type;
  40. typedef typename std::allocator_traits<Allocator>::value_type const & const_reference;
  41. typedef const_reference reference;
  42. typedef typename vector_type::const_iterator const_iterator;
  43. typedef const_iterator iterator;
  44. typedef typename std::iterator_traits<
  45. BidiIterator>::difference_type difference_type;
  46. typedef typename std::allocator_traits<Allocator>::size_type size_type;
  47. typedef Allocator allocator_type;
  48. typedef typename std::iterator_traits<
  49. BidiIterator>::value_type char_type;
  50. typedef std::basic_string<char_type> string_type;
  51. typedef BOOST_REGEX_DETAIL_NS::named_subexpressions named_sub_type;
  52. // construct/copy/destroy:
  53. explicit match_results(const Allocator& a = Allocator())
  54. : m_subs(a), m_base(), m_null(), m_last_closed_paren(0), m_is_singular(true) {}
  55. //
  56. // IMPORTANT: in the code below, the crazy looking checks around m_is_singular are
  57. // all required because it is illegal to copy a singular iterator.
  58. // See https://svn.boost.org/trac/boost/ticket/3632.
  59. //
  60. match_results(const match_results& m)
  61. : m_subs(m.m_subs), m_base(), m_null(), m_named_subs(m.m_named_subs), m_last_closed_paren(m.m_last_closed_paren), m_is_singular(m.m_is_singular)
  62. {
  63. if(!m_is_singular)
  64. {
  65. m_base = m.m_base;
  66. m_null = m.m_null;
  67. }
  68. }
  69. match_results& operator=(const match_results& m)
  70. {
  71. m_subs = m.m_subs;
  72. m_named_subs = m.m_named_subs;
  73. m_last_closed_paren = m.m_last_closed_paren;
  74. m_is_singular = m.m_is_singular;
  75. if(!m_is_singular)
  76. {
  77. m_base = m.m_base;
  78. m_null = m.m_null;
  79. }
  80. return *this;
  81. }
  82. ~match_results(){}
  83. // size:
  84. size_type size() const
  85. { return empty() ? 0 : m_subs.size() - 2; }
  86. size_type max_size() const
  87. { return m_subs.max_size(); }
  88. bool empty() const
  89. { return m_subs.size() < 2; }
  90. // element access:
  91. difference_type length(int sub = 0) const
  92. {
  93. if(m_is_singular)
  94. raise_logic_error();
  95. sub += 2;
  96. if((sub < (int)m_subs.size()) && (sub > 0))
  97. return m_subs[sub].length();
  98. return 0;
  99. }
  100. difference_type length(const char_type* sub) const
  101. {
  102. if(m_is_singular)
  103. raise_logic_error();
  104. const char_type* sub_end = sub;
  105. while(*sub_end) ++sub_end;
  106. return length(named_subexpression_index(sub, sub_end));
  107. }
  108. template <class charT>
  109. difference_type length(const charT* sub) const
  110. {
  111. if(m_is_singular)
  112. raise_logic_error();
  113. const charT* sub_end = sub;
  114. while(*sub_end) ++sub_end;
  115. return length(named_subexpression_index(sub, sub_end));
  116. }
  117. template <class charT, class Traits, class A>
  118. difference_type length(const std::basic_string<charT, Traits, A>& sub) const
  119. {
  120. return length(sub.c_str());
  121. }
  122. difference_type position(size_type sub = 0) const
  123. {
  124. if(m_is_singular)
  125. raise_logic_error();
  126. sub += 2;
  127. if(sub < m_subs.size())
  128. {
  129. const sub_match<BidiIterator>& s = m_subs[sub];
  130. if(s.matched || (sub == 2))
  131. {
  132. return std::distance((BidiIterator)(m_base), (BidiIterator)(s.first));
  133. }
  134. }
  135. return ~static_cast<difference_type>(0);
  136. }
  137. difference_type position(const char_type* sub) const
  138. {
  139. const char_type* sub_end = sub;
  140. while(*sub_end) ++sub_end;
  141. return position(named_subexpression_index(sub, sub_end));
  142. }
  143. template <class charT>
  144. difference_type position(const charT* sub) const
  145. {
  146. const charT* sub_end = sub;
  147. while(*sub_end) ++sub_end;
  148. return position(named_subexpression_index(sub, sub_end));
  149. }
  150. template <class charT, class Traits, class A>
  151. difference_type position(const std::basic_string<charT, Traits, A>& sub) const
  152. {
  153. return position(sub.c_str());
  154. }
  155. string_type str(int sub = 0) const
  156. {
  157. if(m_is_singular)
  158. raise_logic_error();
  159. sub += 2;
  160. string_type result;
  161. if(sub < (int)m_subs.size() && (sub > 0))
  162. {
  163. const sub_match<BidiIterator>& s = m_subs[sub];
  164. if(s.matched)
  165. {
  166. result = s.str();
  167. }
  168. }
  169. return result;
  170. }
  171. string_type str(const char_type* sub) const
  172. {
  173. return (*this)[sub].str();
  174. }
  175. template <class Traits, class A>
  176. string_type str(const std::basic_string<char_type, Traits, A>& sub) const
  177. {
  178. return (*this)[sub].str();
  179. }
  180. template <class charT>
  181. string_type str(const charT* sub) const
  182. {
  183. return (*this)[sub].str();
  184. }
  185. template <class charT, class Traits, class A>
  186. string_type str(const std::basic_string<charT, Traits, A>& sub) const
  187. {
  188. return (*this)[sub].str();
  189. }
  190. const_reference operator[](int sub) const
  191. {
  192. if(m_is_singular && m_subs.empty())
  193. raise_logic_error();
  194. sub += 2;
  195. if(sub < (int)m_subs.size() && (sub >= 0))
  196. {
  197. return m_subs[sub];
  198. }
  199. return m_null;
  200. }
  201. //
  202. // Named sub-expressions:
  203. //
  204. const_reference named_subexpression(const char_type* i, const char_type* j) const
  205. {
  206. //
  207. // Scan for the leftmost *matched* subexpression with the specified named:
  208. //
  209. if(m_is_singular)
  210. raise_logic_error();
  211. BOOST_REGEX_DETAIL_NS::named_subexpressions::range_type r = m_named_subs->equal_range(i, j);
  212. while((r.first != r.second) && ((*this)[r.first->index].matched == false))
  213. ++r.first;
  214. return r.first != r.second ? (*this)[r.first->index] : m_null;
  215. }
  216. template <class charT>
  217. const_reference named_subexpression(const charT* i, const charT* j) const
  218. {
  219. static_assert(sizeof(charT) <= sizeof(char_type), "Failed internal logic");
  220. if(i == j)
  221. return m_null;
  222. std::vector<char_type> s;
  223. while(i != j)
  224. s.insert(s.end(), *i++);
  225. return named_subexpression(&*s.begin(), &*s.begin() + s.size());
  226. }
  227. int named_subexpression_index(const char_type* i, const char_type* j) const
  228. {
  229. //
  230. // Scan for the leftmost *matched* subexpression with the specified named.
  231. // If none found then return the leftmost expression with that name,
  232. // otherwise an invalid index:
  233. //
  234. if(m_is_singular)
  235. raise_logic_error();
  236. BOOST_REGEX_DETAIL_NS::named_subexpressions::range_type s, r;
  237. s = r = m_named_subs->equal_range(i, j);
  238. while((r.first != r.second) && ((*this)[r.first->index].matched == false))
  239. ++r.first;
  240. if(r.first == r.second)
  241. r = s;
  242. return r.first != r.second ? r.first->index : -20;
  243. }
  244. template <class charT>
  245. int named_subexpression_index(const charT* i, const charT* j) const
  246. {
  247. static_assert(sizeof(charT) <= sizeof(char_type), "Failed internal logic");
  248. if(i == j)
  249. return -20;
  250. std::vector<char_type> s;
  251. while(i != j)
  252. s.insert(s.end(), *i++);
  253. return named_subexpression_index(&*s.begin(), &*s.begin() + s.size());
  254. }
  255. template <class Traits, class A>
  256. const_reference operator[](const std::basic_string<char_type, Traits, A>& s) const
  257. {
  258. return named_subexpression(s.c_str(), s.c_str() + s.size());
  259. }
  260. const_reference operator[](const char_type* p) const
  261. {
  262. const char_type* e = p;
  263. while(*e) ++e;
  264. return named_subexpression(p, e);
  265. }
  266. template <class charT>
  267. const_reference operator[](const charT* p) const
  268. {
  269. static_assert(sizeof(charT) <= sizeof(char_type), "Failed internal logic");
  270. if(*p == 0)
  271. return m_null;
  272. std::vector<char_type> s;
  273. while(*p)
  274. s.insert(s.end(), *p++);
  275. return named_subexpression(&*s.begin(), &*s.begin() + s.size());
  276. }
  277. template <class charT, class Traits, class A>
  278. const_reference operator[](const std::basic_string<charT, Traits, A>& ns) const
  279. {
  280. static_assert(sizeof(charT) <= sizeof(char_type), "Failed internal logic");
  281. if(ns.empty())
  282. return m_null;
  283. std::vector<char_type> s;
  284. for(unsigned i = 0; i < ns.size(); ++i)
  285. s.insert(s.end(), ns[i]);
  286. return named_subexpression(&*s.begin(), &*s.begin() + s.size());
  287. }
  288. const_reference prefix() const
  289. {
  290. if(m_is_singular)
  291. raise_logic_error();
  292. return (*this)[-1];
  293. }
  294. const_reference suffix() const
  295. {
  296. if(m_is_singular)
  297. raise_logic_error();
  298. return (*this)[-2];
  299. }
  300. const_iterator begin() const
  301. {
  302. return (m_subs.size() > 2) ? (m_subs.begin() + 2) : m_subs.end();
  303. }
  304. const_iterator end() const
  305. {
  306. return m_subs.end();
  307. }
  308. // format:
  309. template <class OutputIterator, class Functor>
  310. OutputIterator format(OutputIterator out,
  311. Functor fmt,
  312. match_flag_type flags = format_default) const
  313. {
  314. if(m_is_singular)
  315. raise_logic_error();
  316. typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, OutputIterator>::type F;
  317. F func(fmt);
  318. return func(*this, out, flags);
  319. }
  320. template <class Functor>
  321. string_type format(Functor fmt, match_flag_type flags = format_default) const
  322. {
  323. if(m_is_singular)
  324. raise_logic_error();
  325. std::basic_string<char_type> result;
  326. BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> > i(result);
  327. typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> > >::type F;
  328. F func(fmt);
  329. func(*this, i, flags);
  330. return result;
  331. }
  332. // format with locale:
  333. template <class OutputIterator, class Functor, class RegexT>
  334. OutputIterator format(OutputIterator out,
  335. Functor fmt,
  336. match_flag_type flags,
  337. const RegexT& re) const
  338. {
  339. if(m_is_singular)
  340. raise_logic_error();
  341. typedef ::boost::regex_traits_wrapper<typename RegexT::traits_type> traits_type;
  342. typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, OutputIterator, traits_type>::type F;
  343. F func(fmt);
  344. return func(*this, out, flags, re.get_traits());
  345. }
  346. template <class RegexT, class Functor>
  347. string_type format(Functor fmt,
  348. match_flag_type flags,
  349. const RegexT& re) const
  350. {
  351. if(m_is_singular)
  352. raise_logic_error();
  353. typedef ::boost::regex_traits_wrapper<typename RegexT::traits_type> traits_type;
  354. std::basic_string<char_type> result;
  355. BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> > i(result);
  356. typedef typename BOOST_REGEX_DETAIL_NS::compute_functor_type<Functor, match_results<BidiIterator, Allocator>, BOOST_REGEX_DETAIL_NS::string_out_iterator<std::basic_string<char_type> >, traits_type >::type F;
  357. F func(fmt);
  358. func(*this, i, flags, re.get_traits());
  359. return result;
  360. }
  361. const_reference get_last_closed_paren()const
  362. {
  363. if(m_is_singular)
  364. raise_logic_error();
  365. return m_last_closed_paren == 0 ? m_null : (*this)[m_last_closed_paren];
  366. }
  367. allocator_type get_allocator() const
  368. {
  369. return m_subs.get_allocator();
  370. }
  371. void swap(match_results& that)
  372. {
  373. std::swap(m_subs, that.m_subs);
  374. std::swap(m_named_subs, that.m_named_subs);
  375. std::swap(m_last_closed_paren, that.m_last_closed_paren);
  376. if(m_is_singular)
  377. {
  378. if(!that.m_is_singular)
  379. {
  380. m_base = that.m_base;
  381. m_null = that.m_null;
  382. }
  383. }
  384. else if(that.m_is_singular)
  385. {
  386. that.m_base = m_base;
  387. that.m_null = m_null;
  388. }
  389. else
  390. {
  391. std::swap(m_base, that.m_base);
  392. std::swap(m_null, that.m_null);
  393. }
  394. std::swap(m_is_singular, that.m_is_singular);
  395. }
  396. bool operator==(const match_results& that)const
  397. {
  398. if(m_is_singular)
  399. {
  400. return that.m_is_singular;
  401. }
  402. else if(that.m_is_singular)
  403. {
  404. return false;
  405. }
  406. return (m_subs == that.m_subs) && (m_base == that.m_base) && (m_last_closed_paren == that.m_last_closed_paren);
  407. }
  408. bool operator!=(const match_results& that)const
  409. { return !(*this == that); }
  410. #ifdef BOOST_REGEX_MATCH_EXTRA
  411. typedef typename sub_match<BidiIterator>::capture_sequence_type capture_sequence_type;
  412. const capture_sequence_type& captures(int i)const
  413. {
  414. if(m_is_singular)
  415. raise_logic_error();
  416. return (*this)[i].captures();
  417. }
  418. #endif
  419. //
  420. // private access functions:
  421. void set_second(BidiIterator i)
  422. {
  423. BOOST_REGEX_ASSERT(m_subs.size() > 2);
  424. m_subs[2].second = i;
  425. m_subs[2].matched = true;
  426. m_subs[0].first = i;
  427. m_subs[0].matched = (m_subs[0].first != m_subs[0].second);
  428. m_null.first = i;
  429. m_null.second = i;
  430. m_null.matched = false;
  431. m_is_singular = false;
  432. }
  433. void set_second(BidiIterator i, size_type pos, bool m = true, bool escape_k = false)
  434. {
  435. if(pos)
  436. m_last_closed_paren = static_cast<int>(pos);
  437. pos += 2;
  438. BOOST_REGEX_ASSERT(m_subs.size() > pos);
  439. m_subs[pos].second = i;
  440. m_subs[pos].matched = m;
  441. if((pos == 2) && !escape_k)
  442. {
  443. m_subs[0].first = i;
  444. m_subs[0].matched = (m_subs[0].first != m_subs[0].second);
  445. m_null.first = i;
  446. m_null.second = i;
  447. m_null.matched = false;
  448. m_is_singular = false;
  449. }
  450. }
  451. void set_size(size_type n, BidiIterator i, BidiIterator j)
  452. {
  453. value_type v(j);
  454. size_type len = m_subs.size();
  455. if(len > n + 2)
  456. {
  457. m_subs.erase(m_subs.begin()+n+2, m_subs.end());
  458. std::fill(m_subs.begin(), m_subs.end(), v);
  459. }
  460. else
  461. {
  462. std::fill(m_subs.begin(), m_subs.end(), v);
  463. if(n+2 != len)
  464. m_subs.insert(m_subs.end(), n+2-len, v);
  465. }
  466. m_subs[1].first = i;
  467. m_last_closed_paren = 0;
  468. }
  469. void set_base(BidiIterator pos)
  470. {
  471. m_base = pos;
  472. }
  473. BidiIterator base()const
  474. {
  475. return m_base;
  476. }
  477. void set_first(BidiIterator i)
  478. {
  479. BOOST_REGEX_ASSERT(m_subs.size() > 2);
  480. // set up prefix:
  481. m_subs[1].second = i;
  482. m_subs[1].matched = (m_subs[1].first != i);
  483. // set up $0:
  484. m_subs[2].first = i;
  485. // zero out everything else:
  486. for(size_type n = 3; n < m_subs.size(); ++n)
  487. {
  488. m_subs[n].first = m_subs[n].second = m_subs[0].second;
  489. m_subs[n].matched = false;
  490. }
  491. }
  492. void set_first(BidiIterator i, size_type pos, bool escape_k = false)
  493. {
  494. BOOST_REGEX_ASSERT(pos+2 < m_subs.size());
  495. if(pos || escape_k)
  496. {
  497. m_subs[pos+2].first = i;
  498. if(escape_k)
  499. {
  500. m_subs[1].second = i;
  501. m_subs[1].matched = (m_subs[1].first != m_subs[1].second);
  502. }
  503. }
  504. else
  505. set_first(i);
  506. }
  507. void maybe_assign(const match_results<BidiIterator, Allocator>& m);
  508. void set_named_subs(std::shared_ptr<named_sub_type> subs)
  509. {
  510. m_named_subs = subs;
  511. }
  512. private:
  513. //
  514. // Error handler called when an uninitialized match_results is accessed:
  515. //
  516. static void raise_logic_error()
  517. {
  518. std::logic_error e("Attempt to access an uninitialized boost::match_results<> class.");
  519. #ifndef BOOST_REGEX_STANDALONE
  520. boost::throw_exception(e);
  521. #else
  522. throw e;
  523. #endif
  524. }
  525. vector_type m_subs; // subexpressions
  526. BidiIterator m_base; // where the search started from
  527. sub_match<BidiIterator> m_null; // a null match
  528. std::shared_ptr<named_sub_type> m_named_subs; // Shared copy of named subs in the regex object
  529. int m_last_closed_paren; // Last ) to be seen - used for formatting
  530. bool m_is_singular; // True if our stored iterators are singular
  531. };
  532. template <class BidiIterator, class Allocator>
  533. void match_results<BidiIterator, Allocator>::maybe_assign(const match_results<BidiIterator, Allocator>& m)
  534. {
  535. if(m_is_singular)
  536. {
  537. *this = m;
  538. return;
  539. }
  540. const_iterator p1, p2;
  541. p1 = begin();
  542. p2 = m.begin();
  543. //
  544. // Distances are measured from the start of *this* match, unless this isn't
  545. // a valid match in which case we use the start of the whole sequence. Note that
  546. // no subsequent match-candidate can ever be to the left of the first match found.
  547. // This ensures that when we are using bidirectional iterators, that distances
  548. // measured are as short as possible, and therefore as efficient as possible
  549. // to compute. Finally note that we don't use the "matched" data member to test
  550. // whether a sub-expression is a valid match, because partial matches set this
  551. // to false for sub-expression 0.
  552. //
  553. BidiIterator l_end = this->suffix().second;
  554. BidiIterator l_base = (p1->first == l_end) ? this->prefix().first : (*this)[0].first;
  555. difference_type len1 = 0;
  556. difference_type len2 = 0;
  557. difference_type base1 = 0;
  558. difference_type base2 = 0;
  559. std::size_t i;
  560. for(i = 0; i < size(); ++i, ++p1, ++p2)
  561. {
  562. //
  563. // Leftmost takes priority over longest; handle special cases
  564. // where distances need not be computed first (an optimisation
  565. // for bidirectional iterators: ensure that we don't accidently
  566. // compute the length of the whole sequence, as this can be really
  567. // expensive).
  568. //
  569. if(p1->first == l_end)
  570. {
  571. if(p2->first != l_end)
  572. {
  573. // p2 must be better than p1, and no need to calculate
  574. // actual distances:
  575. base1 = 1;
  576. base2 = 0;
  577. break;
  578. }
  579. else
  580. {
  581. // *p1 and *p2 are either unmatched or match end-of sequence,
  582. // either way no need to calculate distances:
  583. if((p1->matched == false) && (p2->matched == true))
  584. break;
  585. if((p1->matched == true) && (p2->matched == false))
  586. return;
  587. continue;
  588. }
  589. }
  590. else if(p2->first == l_end)
  591. {
  592. // p1 better than p2, and no need to calculate distances:
  593. return;
  594. }
  595. base1 = std::distance(l_base, p1->first);
  596. base2 = std::distance(l_base, p2->first);
  597. BOOST_REGEX_ASSERT(base1 >= 0);
  598. BOOST_REGEX_ASSERT(base2 >= 0);
  599. if(base1 < base2) return;
  600. if(base2 < base1) break;
  601. len1 = std::distance((BidiIterator)p1->first, (BidiIterator)p1->second);
  602. len2 = std::distance((BidiIterator)p2->first, (BidiIterator)p2->second);
  603. BOOST_REGEX_ASSERT(len1 >= 0);
  604. BOOST_REGEX_ASSERT(len2 >= 0);
  605. if((len1 != len2) || ((p1->matched == false) && (p2->matched == true)))
  606. break;
  607. if((p1->matched == true) && (p2->matched == false))
  608. return;
  609. }
  610. if(i == size())
  611. return;
  612. if(base2 < base1)
  613. *this = m;
  614. else if((len2 > len1) || ((p1->matched == false) && (p2->matched == true)) )
  615. *this = m;
  616. }
  617. template <class BidiIterator, class Allocator>
  618. void swap(match_results<BidiIterator, Allocator>& a, match_results<BidiIterator, Allocator>& b)
  619. {
  620. a.swap(b);
  621. }
  622. template <class charT, class traits, class BidiIterator, class Allocator>
  623. std::basic_ostream<charT, traits>&
  624. operator << (std::basic_ostream<charT, traits>& os,
  625. const match_results<BidiIterator, Allocator>& s)
  626. {
  627. return (os << s.str());
  628. }
  629. #ifdef BOOST_REGEX_MSVC
  630. #pragma warning(pop)
  631. #endif
  632. } // namespace boost
  633. #endif