]>
Commit | Line | Data |
---|---|---|
6cb784b6 TS |
1 | // class template regex -*- C++ -*- |
2 | ||
aa118a03 | 3 | // Copyright (C) 2013-2014 Free Software Foundation, Inc. |
6cb784b6 TS |
4 | // |
5 | // This file is part of the GNU ISO C++ Library. This library is free | |
6 | // software; you can redistribute it and/or modify it under the | |
7 | // terms of the GNU General Public License as published by the | |
8 | // Free Software Foundation; either version 3, or (at your option) | |
9 | // any later version. | |
10 | ||
11 | // This library is distributed in the hope that it will be useful, | |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | // GNU General Public License for more details. | |
15 | ||
16 | // Under Section 7 of GPL version 3, you are granted additional | |
17 | // permissions described in the GCC Runtime Library Exception, version | |
18 | // 3.1, as published by the Free Software Foundation. | |
19 | ||
20 | // You should have received a copy of the GNU General Public License and | |
21 | // a copy of the GCC Runtime Library Exception along with this program; | |
22 | // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
23 | // <http://www.gnu.org/licenses/>. | |
24 | ||
25 | /** | |
26 | * @file bits/regex_compiler.tcc | |
27 | * This is an internal header file, included by other library headers. | |
28 | * Do not attempt to use it directly. @headername{regex} | |
29 | */ | |
30 | ||
b21abcee | 31 | // FIXME make comments doxygen format. |
7c812a2a TS |
32 | |
33 | // This compiler refers to "Regular Expression Matching Can Be Simple And Fast" | |
34 | // (http://swtch.com/~rsc/regexp/regexp1.html"), | |
35 | // but doesn't strictly follow it. | |
36 | // | |
37 | // When compiling, states are *chained* instead of tree- or graph-constructed. | |
38 | // It's more like structured programs: there's if statement and loop statement. | |
39 | // | |
40 | // For alternative structure(say "a|b"), aka "if statement", two branchs should | |
41 | // be constructed. However, these two shall merge to an "end_tag" at the end of | |
42 | // this operator: | |
43 | // | |
44 | // branch1 | |
45 | // / \ | |
46 | // => begin_tag end_tag => | |
47 | // \ / | |
48 | // branch2 | |
49 | // | |
50 | // This is the difference between this implementation and that in Russ's | |
51 | // article. | |
52 | // | |
53 | // That's why we introduced dummy node here ------ "end_tag" is a dummy node. | |
54 | // All dummy node will be eliminated at the end of compiling process. | |
55 | ||
6cb784b6 TS |
56 | namespace std _GLIBCXX_VISIBILITY(default) |
57 | { | |
58 | namespace __detail | |
59 | { | |
60 | _GLIBCXX_BEGIN_NAMESPACE_VERSION | |
61 | ||
68e69ce2 JW |
62 | template<typename _FwdIter, typename _TraitsT> |
63 | _Compiler<_FwdIter, _TraitsT>:: | |
33fbbb76 | 64 | _Compiler(_FwdIter __b, _FwdIter __e, |
e280b6ff | 65 | const _TraitsT& __traits, _FlagT __flags) |
c2669da9 TS |
66 | : _M_flags((__flags |
67 | & (regex_constants::ECMAScript | |
68 | | regex_constants::basic | |
69 | | regex_constants::extended | |
70 | | regex_constants::grep | |
71 | | regex_constants::egrep | |
72 | | regex_constants::awk)) | |
73 | ? __flags | |
74 | : __flags | regex_constants::ECMAScript), | |
75 | _M_traits(__traits), | |
68e69ce2 | 76 | _M_ctype(std::use_facet<_CtypeT>(_M_traits.getloc())), |
ab1c993b | 77 | _M_scanner(__b, __e, _M_flags, _M_traits.getloc()), |
c2669da9 | 78 | _M_nfa(_M_flags) |
6cb784b6 | 79 | { |
7c812a2a TS |
80 | _StateSeqT __r(_M_nfa, _M_nfa._M_start()); |
81 | __r._M_append(_M_nfa._M_insert_subexpr_begin()); | |
82 | this->_M_disjunction(); | |
83 | if (!_M_match_token(_ScannerT::_S_token_eof)) | |
84 | __throw_regex_error(regex_constants::error_paren); | |
85 | __r._M_append(_M_pop()); | |
86 | _GLIBCXX_DEBUG_ASSERT(_M_stack.empty()); | |
87 | __r._M_append(_M_nfa._M_insert_subexpr_end()); | |
88 | __r._M_append(_M_nfa._M_insert_accept()); | |
89 | _M_nfa._M_eliminate_dummy(); | |
6cb784b6 TS |
90 | } |
91 | ||
68e69ce2 | 92 | template<typename _FwdIter, typename _TraitsT> |
6cb784b6 | 93 | void |
68e69ce2 | 94 | _Compiler<_FwdIter, _TraitsT>:: |
6cb784b6 TS |
95 | _M_disjunction() |
96 | { | |
97 | this->_M_alternative(); | |
7c812a2a | 98 | while (_M_match_token(_ScannerT::_S_token_or)) |
6cb784b6 | 99 | { |
7c812a2a TS |
100 | _StateSeqT __alt1 = _M_pop(); |
101 | this->_M_alternative(); | |
102 | _StateSeqT __alt2 = _M_pop(); | |
103 | auto __end = _M_nfa._M_insert_dummy(); | |
104 | __alt1._M_append(__end); | |
105 | __alt2._M_append(__end); | |
106 | _M_stack.push(_StateSeqT(_M_nfa, | |
107 | _M_nfa._M_insert_alt(__alt1._M_start, | |
7b86458e | 108 | __alt2._M_start, false), |
7c812a2a | 109 | __end)); |
6cb784b6 TS |
110 | } |
111 | } | |
112 | ||
68e69ce2 | 113 | template<typename _FwdIter, typename _TraitsT> |
6cb784b6 | 114 | void |
68e69ce2 | 115 | _Compiler<_FwdIter, _TraitsT>:: |
6cb784b6 TS |
116 | _M_alternative() |
117 | { | |
118 | if (this->_M_term()) | |
119 | { | |
7c812a2a | 120 | _StateSeqT __re = _M_pop(); |
6cb784b6 | 121 | this->_M_alternative(); |
7c812a2a | 122 | __re._M_append(_M_pop()); |
6cb784b6 TS |
123 | _M_stack.push(__re); |
124 | } | |
7c812a2a TS |
125 | else |
126 | _M_stack.push(_StateSeqT(_M_nfa, _M_nfa._M_insert_dummy())); | |
6cb784b6 TS |
127 | } |
128 | ||
68e69ce2 | 129 | template<typename _FwdIter, typename _TraitsT> |
6cb784b6 | 130 | bool |
68e69ce2 | 131 | _Compiler<_FwdIter, _TraitsT>:: |
6cb784b6 TS |
132 | _M_term() |
133 | { | |
134 | if (this->_M_assertion()) | |
135 | return true; | |
136 | if (this->_M_atom()) | |
137 | { | |
138 | this->_M_quantifier(); | |
139 | return true; | |
140 | } | |
141 | return false; | |
142 | } | |
143 | ||
68e69ce2 | 144 | template<typename _FwdIter, typename _TraitsT> |
6cb784b6 | 145 | bool |
68e69ce2 | 146 | _Compiler<_FwdIter, _TraitsT>:: |
6cb784b6 TS |
147 | _M_assertion() |
148 | { | |
7c812a2a | 149 | if (_M_match_token(_ScannerT::_S_token_line_begin)) |
7d9d2185 | 150 | _M_stack.push(_StateSeqT(_M_nfa, _M_nfa._M_insert_line_begin())); |
7c812a2a | 151 | else if (_M_match_token(_ScannerT::_S_token_line_end)) |
7d9d2185 | 152 | _M_stack.push(_StateSeqT(_M_nfa, _M_nfa._M_insert_line_end())); |
7c812a2a | 153 | else if (_M_match_token(_ScannerT::_S_token_word_bound)) |
7b86458e TS |
154 | // _M_value[0] == 'n' means it's negtive, say "not word boundary". |
155 | _M_stack.push(_StateSeqT(_M_nfa, _M_nfa. | |
156 | _M_insert_word_bound(_M_value[0] == 'n'))); | |
7c812a2a | 157 | else if (_M_match_token(_ScannerT::_S_token_subexpr_lookahead_begin)) |
7b86458e TS |
158 | { |
159 | auto __neg = _M_value[0] == 'n'; | |
160 | this->_M_disjunction(); | |
161 | if (!_M_match_token(_ScannerT::_S_token_subexpr_end)) | |
162 | __throw_regex_error(regex_constants::error_paren); | |
163 | auto __tmp = _M_pop(); | |
164 | __tmp._M_append(_M_nfa._M_insert_accept()); | |
165 | _M_stack.push( | |
166 | _StateSeqT( | |
167 | _M_nfa, | |
168 | _M_nfa._M_insert_lookahead(__tmp._M_start, __neg))); | |
169 | } | |
7c812a2a TS |
170 | else |
171 | return false; | |
172 | return true; | |
6cb784b6 TS |
173 | } |
174 | ||
68e69ce2 | 175 | template<typename _FwdIter, typename _TraitsT> |
6cb784b6 | 176 | void |
68e69ce2 | 177 | _Compiler<_FwdIter, _TraitsT>:: |
6cb784b6 TS |
178 | _M_quantifier() |
179 | { | |
c2669da9 | 180 | bool __neg = (_M_flags & regex_constants::ECMAScript); |
7b86458e | 181 | auto __init = [this, &__neg]() |
6cb784b6 TS |
182 | { |
183 | if (_M_stack.empty()) | |
184 | __throw_regex_error(regex_constants::error_badrepeat); | |
7b86458e TS |
185 | __neg = __neg && _M_match_token(_ScannerT::_S_token_opt); |
186 | }; | |
187 | if (_M_match_token(_ScannerT::_S_token_closure0)) | |
188 | { | |
189 | __init(); | |
7c812a2a TS |
190 | auto __e = _M_pop(); |
191 | _StateSeqT __r(_M_nfa, _M_nfa._M_insert_alt(_S_invalid_state_id, | |
7b86458e | 192 | __e._M_start, __neg)); |
7c812a2a | 193 | __e._M_append(__r); |
6cb784b6 | 194 | _M_stack.push(__r); |
6cb784b6 | 195 | } |
7c812a2a | 196 | else if (_M_match_token(_ScannerT::_S_token_closure1)) |
6cb784b6 | 197 | { |
7b86458e | 198 | __init(); |
7c812a2a | 199 | auto __e = _M_pop(); |
7b86458e TS |
200 | __e._M_append(_M_nfa._M_insert_alt(_S_invalid_state_id, __e._M_start, |
201 | __neg)); | |
7c812a2a | 202 | _M_stack.push(__e); |
6cb784b6 | 203 | } |
7c812a2a | 204 | else if (_M_match_token(_ScannerT::_S_token_opt)) |
6cb784b6 | 205 | { |
7b86458e | 206 | __init(); |
7c812a2a TS |
207 | auto __e = _M_pop(); |
208 | auto __end = _M_nfa._M_insert_dummy(); | |
209 | _StateSeqT __r(_M_nfa, _M_nfa._M_insert_alt(_S_invalid_state_id, | |
7b86458e | 210 | __e._M_start, __neg)); |
7c812a2a TS |
211 | __e._M_append(__end); |
212 | __r._M_append(__end); | |
6cb784b6 | 213 | _M_stack.push(__r); |
6cb784b6 | 214 | } |
7c812a2a | 215 | else if (_M_match_token(_ScannerT::_S_token_interval_begin)) |
6cb784b6 | 216 | { |
c2669da9 TS |
217 | if (_M_stack.empty()) |
218 | __throw_regex_error(regex_constants::error_badrepeat); | |
6cb784b6 TS |
219 | if (!_M_match_token(_ScannerT::_S_token_dup_count)) |
220 | __throw_regex_error(regex_constants::error_badbrace); | |
7c812a2a TS |
221 | _StateSeqT __r(_M_pop()); |
222 | _StateSeqT __e(_M_nfa, _M_nfa._M_insert_dummy()); | |
6cb43087 | 223 | long __min_rep = _M_cur_int_value(10); |
c2669da9 | 224 | bool __infi = false; |
6cb43087 | 225 | long __n; |
c2669da9 | 226 | |
7c812a2a | 227 | // {3 |
6cb784b6 | 228 | if (_M_match_token(_ScannerT::_S_token_comma)) |
7c812a2a | 229 | if (_M_match_token(_ScannerT::_S_token_dup_count)) // {3,7} |
c2669da9 TS |
230 | __n = _M_cur_int_value(10) - __min_rep; |
231 | else | |
232 | __infi = true; | |
233 | else | |
234 | __n = 0; | |
6cb784b6 TS |
235 | if (!_M_match_token(_ScannerT::_S_token_interval_end)) |
236 | __throw_regex_error(regex_constants::error_brace); | |
c2669da9 TS |
237 | |
238 | __neg = __neg && _M_match_token(_ScannerT::_S_token_opt); | |
239 | ||
6cb43087 | 240 | for (long __i = 0; __i < __min_rep; ++__i) |
c2669da9 TS |
241 | __e._M_append(__r._M_clone()); |
242 | ||
243 | if (__infi) | |
244 | { | |
245 | auto __tmp = __r._M_clone(); | |
246 | _StateSeqT __s(_M_nfa, | |
247 | _M_nfa._M_insert_alt(_S_invalid_state_id, | |
248 | __tmp._M_start, __neg)); | |
249 | __tmp._M_append(__s); | |
250 | __e._M_append(__s); | |
251 | } | |
252 | else | |
253 | { | |
254 | if (__n < 0) | |
255 | __throw_regex_error(regex_constants::error_badbrace); | |
256 | auto __end = _M_nfa._M_insert_dummy(); | |
257 | // _M_alt is the "match more" branch, and _M_next is the | |
258 | // "match less" one. Switch _M_alt and _M_next of all created | |
259 | // nodes. This is a hacking but IMO works well. | |
260 | std::stack<_StateIdT> __stack; | |
6cb43087 | 261 | for (long __i = 0; __i < __n; ++__i) |
c2669da9 TS |
262 | { |
263 | auto __tmp = __r._M_clone(); | |
264 | auto __alt = _M_nfa._M_insert_alt(__tmp._M_start, | |
265 | __end, __neg); | |
266 | __stack.push(__alt); | |
267 | __e._M_append(_StateSeqT(_M_nfa, __alt, __tmp._M_end)); | |
268 | } | |
269 | __e._M_append(__end); | |
270 | while (!__stack.empty()) | |
271 | { | |
272 | auto& __tmp = _M_nfa[__stack.top()]; | |
273 | __stack.pop(); | |
274 | swap(__tmp._M_next, __tmp._M_alt); | |
275 | } | |
276 | } | |
7c812a2a | 277 | _M_stack.push(__e); |
6cb784b6 TS |
278 | } |
279 | } | |
280 | ||
68e69ce2 | 281 | template<typename _FwdIter, typename _TraitsT> |
6cb784b6 | 282 | bool |
68e69ce2 | 283 | _Compiler<_FwdIter, _TraitsT>:: |
6cb784b6 TS |
284 | _M_atom() |
285 | { | |
286 | if (_M_match_token(_ScannerT::_S_token_anychar)) | |
7c812a2a TS |
287 | _M_stack.push(_StateSeqT(_M_nfa, |
288 | _M_nfa._M_insert_matcher | |
bb038ece | 289 | (_AnyMatcher<_TraitsT>(_M_traits)))); |
7c812a2a TS |
290 | else if (_M_try_char()) |
291 | _M_stack.push(_StateSeqT(_M_nfa, | |
292 | _M_nfa._M_insert_matcher | |
bb038ece | 293 | (_CharMatcher<_TraitsT>(_M_value[0], |
7c812a2a TS |
294 | _M_traits, |
295 | _M_flags)))); | |
296 | else if (_M_match_token(_ScannerT::_S_token_backref)) | |
297 | _M_stack.push(_StateSeqT(_M_nfa, _M_nfa. | |
298 | _M_insert_backref(_M_cur_int_value(10)))); | |
299 | else if (_M_match_token(_ScannerT::_S_token_quoted_class)) | |
6cb784b6 | 300 | { |
7c812a2a TS |
301 | _GLIBCXX_DEBUG_ASSERT(_M_value.size() == 1); |
302 | _BMatcherT __matcher(_M_ctype.is(_CtypeT::upper, _M_value[0]), | |
303 | _M_traits, _M_flags); | |
304 | __matcher._M_add_character_class(_M_value); | |
305 | _M_stack.push(_StateSeqT(_M_nfa, | |
7d9d2185 | 306 | _M_nfa._M_insert_matcher(std::move(__matcher)))); |
6cb784b6 | 307 | } |
7c812a2a | 308 | else if (_M_match_token(_ScannerT::_S_token_subexpr_no_group_begin)) |
6cb784b6 | 309 | { |
7c812a2a TS |
310 | _StateSeqT __r(_M_nfa, _M_nfa._M_insert_dummy()); |
311 | this->_M_disjunction(); | |
312 | if (!_M_match_token(_ScannerT::_S_token_subexpr_end)) | |
313 | __throw_regex_error(regex_constants::error_paren); | |
314 | __r._M_append(_M_pop()); | |
315 | _M_stack.push(__r); | |
6cb784b6 | 316 | } |
7c812a2a | 317 | else if (_M_match_token(_ScannerT::_S_token_subexpr_begin)) |
6cb784b6 | 318 | { |
7c812a2a | 319 | _StateSeqT __r(_M_nfa, _M_nfa._M_insert_subexpr_begin()); |
6cb784b6 TS |
320 | this->_M_disjunction(); |
321 | if (!_M_match_token(_ScannerT::_S_token_subexpr_end)) | |
322 | __throw_regex_error(regex_constants::error_paren); | |
7c812a2a TS |
323 | __r._M_append(_M_pop()); |
324 | __r._M_append(_M_nfa._M_insert_subexpr_end()); | |
6cb784b6 | 325 | _M_stack.push(__r); |
6cb784b6 | 326 | } |
7c812a2a TS |
327 | else if (!_M_bracket_expression()) |
328 | return false; | |
329 | return true; | |
6cb784b6 TS |
330 | } |
331 | ||
68e69ce2 | 332 | template<typename _FwdIter, typename _TraitsT> |
6cb784b6 | 333 | bool |
68e69ce2 | 334 | _Compiler<_FwdIter, _TraitsT>:: |
6cb784b6 TS |
335 | _M_bracket_expression() |
336 | { | |
33fbbb76 TS |
337 | bool __neg = |
338 | _M_match_token(_ScannerT::_S_token_bracket_neg_begin); | |
339 | if (!(__neg || _M_match_token(_ScannerT::_S_token_bracket_begin))) | |
e280b6ff | 340 | return false; |
33fbbb76 | 341 | _BMatcherT __matcher(__neg, _M_traits, _M_flags); |
7c812a2a TS |
342 | while (!_M_match_token(_ScannerT::_S_token_bracket_end)) |
343 | _M_expression_term(__matcher); | |
7d9d2185 JW |
344 | _M_stack.push(_StateSeqT(_M_nfa, |
345 | _M_nfa._M_insert_matcher(std::move(__matcher)))); | |
6cb784b6 TS |
346 | return true; |
347 | } | |
348 | ||
68e69ce2 | 349 | template<typename _FwdIter, typename _TraitsT> |
6cb784b6 | 350 | void |
68e69ce2 | 351 | _Compiler<_FwdIter, _TraitsT>:: |
6cb784b6 TS |
352 | _M_expression_term(_BMatcherT& __matcher) |
353 | { | |
354 | if (_M_match_token(_ScannerT::_S_token_collsymbol)) | |
7c812a2a TS |
355 | __matcher._M_add_collating_element(_M_value); |
356 | else if (_M_match_token(_ScannerT::_S_token_equiv_class_name)) | |
357 | __matcher._M_add_equivalence_class(_M_value); | |
358 | else if (_M_match_token(_ScannerT::_S_token_char_class_name)) | |
359 | __matcher._M_add_character_class(_M_value); | |
360 | else if (_M_try_char()) // [a | |
e280b6ff | 361 | { |
33fbbb76 TS |
362 | auto __ch = _M_value[0]; |
363 | if (_M_try_char()) | |
e280b6ff | 364 | { |
7c812a2a | 365 | if (_M_value[0] == '-') // [a- |
e280b6ff | 366 | { |
33fbbb76 TS |
367 | if (_M_try_char()) // [a-z] |
368 | { | |
369 | __matcher._M_make_range(__ch, _M_value[0]); | |
370 | return; | |
371 | } | |
372 | // If the dash is the last character in the bracket | |
373 | // expression, it is not special. | |
374 | if (_M_scanner._M_get_token() | |
375 | != _ScannerT::_S_token_bracket_end) | |
e280b6ff | 376 | __throw_regex_error(regex_constants::error_range); |
e280b6ff | 377 | } |
33fbbb76 | 378 | __matcher._M_add_char(_M_value[0]); |
e280b6ff | 379 | } |
33fbbb76 | 380 | __matcher._M_add_char(__ch); |
e280b6ff | 381 | } |
7c812a2a TS |
382 | else |
383 | __throw_regex_error(regex_constants::error_brack); | |
6cb784b6 TS |
384 | } |
385 | ||
68e69ce2 | 386 | template<typename _FwdIter, typename _TraitsT> |
33fbbb76 | 387 | bool |
68e69ce2 | 388 | _Compiler<_FwdIter, _TraitsT>:: |
33fbbb76 TS |
389 | _M_try_char() |
390 | { | |
391 | bool __is_char = false; | |
392 | if (_M_match_token(_ScannerT::_S_token_oct_num)) | |
393 | { | |
394 | __is_char = true; | |
395 | _M_value.assign(1, _M_cur_int_value(8)); | |
396 | } | |
397 | else if (_M_match_token(_ScannerT::_S_token_hex_num)) | |
398 | { | |
399 | __is_char = true; | |
400 | _M_value.assign(1, _M_cur_int_value(16)); | |
401 | } | |
402 | else if (_M_match_token(_ScannerT::_S_token_ord_char)) | |
403 | __is_char = true; | |
404 | return __is_char; | |
405 | } | |
406 | ||
68e69ce2 | 407 | template<typename _FwdIter, typename _TraitsT> |
7c812a2a | 408 | bool |
68e69ce2 | 409 | _Compiler<_FwdIter, _TraitsT>:: |
7c812a2a TS |
410 | _M_match_token(_TokenT token) |
411 | { | |
412 | if (token == _M_scanner._M_get_token()) | |
413 | { | |
414 | _M_value = _M_scanner._M_get_value(); | |
415 | _M_scanner._M_advance(); | |
416 | return true; | |
417 | } | |
418 | return false; | |
419 | } | |
420 | ||
68e69ce2 | 421 | template<typename _FwdIter, typename _TraitsT> |
6cb784b6 | 422 | int |
68e69ce2 | 423 | _Compiler<_FwdIter, _TraitsT>:: |
6cb784b6 TS |
424 | _M_cur_int_value(int __radix) |
425 | { | |
6cb43087 | 426 | long __v = 0; |
6cb784b6 | 427 | for (typename _StringT::size_type __i = 0; |
33fbbb76 TS |
428 | __i < _M_value.length(); ++__i) |
429 | __v =__v * __radix + _M_traits.value(_M_value[__i], __radix); | |
6cb784b6 TS |
430 | return __v; |
431 | } | |
432 | ||
bb038ece | 433 | template<typename _TraitsT> |
7d9d2185 | 434 | bool |
bb038ece | 435 | _BracketMatcher<_TraitsT>::operator()(_CharT __ch) const |
6cb784b6 | 436 | { |
6cb784b6 | 437 | bool __ret = false; |
756aa0c3 TS |
438 | if (_M_traits.isctype(__ch, _M_class_set) |
439 | || _M_char_set.count(_M_translate(__ch)) | |
440 | || _M_equiv_set.count(_M_traits.transform_primary(&__ch, &__ch+1))) | |
e3509691 | 441 | __ret = true; |
6cb784b6 | 442 | else |
e280b6ff | 443 | { |
e3509691 TS |
444 | _StringT __s = _M_get_str(_M_flags & regex_constants::collate |
445 | ? _M_translate(__ch) : __ch); | |
446 | for (auto& __it : _M_range_set) | |
447 | if (__it.first <= __s && __s <= __it.second) | |
e280b6ff TS |
448 | { |
449 | __ret = true; | |
450 | break; | |
451 | } | |
452 | } | |
6cb784b6 | 453 | if (_M_is_non_matching) |
33fbbb76 TS |
454 | return !__ret; |
455 | else | |
456 | return __ret; | |
6cb784b6 TS |
457 | } |
458 | ||
459 | _GLIBCXX_END_NAMESPACE_VERSION | |
460 | } // namespace __detail | |
461 | } // namespace |