Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qstringtokenizer.h
Go to the documentation of this file.
1// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#ifndef QSTRINGTOKENIZER_H
4#define QSTRINGTOKENIZER_H
5
6#include <QtCore/qnamespace.h>
7#include <QtCore/qcontainerfwd.h>
8#include <iterator>
9
10QT_BEGIN_NAMESPACE
11
12template <typename, typename> class QStringBuilder;
13
15{
16protected:
18 constexpr QStringTokenizerBaseBase(Qt::SplitBehavior sb, Qt::CaseSensitivity cs) noexcept
19 : m_sb{sb}, m_cs{cs} {}
20
23 friend constexpr bool operator==(tokenizer_state lhs, tokenizer_state rhs) noexcept
24 { return lhs.start == rhs.start && lhs.end == rhs.end && lhs.extra == rhs.extra; }
25 friend constexpr bool operator!=(tokenizer_state lhs, tokenizer_state rhs) noexcept
26 { return !operator==(lhs, rhs); }
27 };
28
31};
32
33template <typename Haystack, typename Needle>
35{
36 struct next_result {
37 Haystack value;
38 bool ok;
39 tokenizer_state state;
40 };
41 inline next_result next(tokenizer_state state) const noexcept;
42 inline next_result toFront() const noexcept { return next({}); }
43public:
44 constexpr explicit QStringTokenizerBase(Haystack haystack, Needle needle, Qt::SplitBehavior sb, Qt::CaseSensitivity cs) noexcept
45 : QStringTokenizerBaseBase{sb, cs}, m_haystack{haystack}, m_needle{needle} {}
46
47 class iterator;
48 friend class iterator;
49 class sentinel {
50 friend constexpr bool operator==(sentinel, sentinel) noexcept { return true; }
51 friend constexpr bool operator!=(sentinel, sentinel) noexcept { return false; }
52 };
53 class iterator {
54 const QStringTokenizerBase *tokenizer;
55 next_result current;
57 explicit iterator(const QStringTokenizerBase &t) noexcept
58 : tokenizer{&t}, current{t.toFront()} {}
59 public:
61 using value_type = Haystack;
62 using pointer = const value_type*;
63 using reference = const value_type&;
64 using iterator_category = std::forward_iterator_tag;
65
66 iterator() noexcept = default;
67
68 // violates std::forward_iterator (returns a reference into the iterator)
69 [[nodiscard]] constexpr const Haystack* operator->() const { return Q_ASSERT(current.ok), &current.value; }
70 [[nodiscard]] constexpr const Haystack& operator*() const { return *operator->(); }
71
72 iterator& operator++() { advance(); return *this; }
73 iterator operator++(int) { auto tmp = *this; advance(); return tmp; }
74
75 friend constexpr bool operator==(const iterator &lhs, sentinel) noexcept
76 { return !lhs.current.ok; }
77 friend constexpr bool operator!=(const iterator &lhs, sentinel) noexcept
78 { return !operator==(lhs, sentinel{}); }
79 friend constexpr bool operator==(sentinel, const iterator &rhs) noexcept
80 { return !rhs.current.ok; }
81 friend constexpr bool operator!=(sentinel, const iterator &rhs) noexcept
82 { return !operator==(sentinel{}, rhs); }
83 private:
84 void advance() {
85 Q_ASSERT(current.ok);
86 current = tokenizer->next(current.state);
87 }
88 };
89 using const_iterator = iterator;
90
91 using size_type = std::size_t;
92 using difference_type = typename iterator::difference_type;
93 using value_type = typename iterator::value_type;
94 using pointer = typename iterator::pointer;
95 using const_pointer = pointer;
96 using reference = typename iterator::reference;
97 using const_reference = reference;
98
99 [[nodiscard]] iterator begin() const noexcept { return iterator{*this}; }
100 [[nodiscard]] iterator cbegin() const noexcept { return begin(); }
101 [[nodiscard]] constexpr sentinel end() const noexcept { return {}; }
102 [[nodiscard]] constexpr sentinel cend() const noexcept { return {}; }
103
104private:
105 Haystack m_haystack;
106 Needle m_needle;
107};
108
109QT_BEGIN_INCLUDE_NAMESPACE
110#include <QtCore/qstringview.h>
111QT_END_INCLUDE_NAMESPACE
112
113namespace QtPrivate {
114namespace Tok {
115
116 constexpr qsizetype size(QChar) noexcept { return 1; }
117 template <typename String>
118 constexpr qsizetype size(const String &s) noexcept { return static_cast<qsizetype>(s.size()); }
119
120 template <typename String> struct ViewForImpl {};
121 template <> struct ViewForImpl<QStringView> { using type = QStringView; };
122 template <> struct ViewForImpl<QLatin1StringView> { using type = QLatin1StringView; };
123 template <> struct ViewForImpl<QChar> { using type = QChar; };
124 template <> struct ViewForImpl<QString> : ViewForImpl<QStringView> {};
125 template <> struct ViewForImpl<QLatin1Char> : ViewForImpl<QChar> {};
126 template <> struct ViewForImpl<char16_t> : ViewForImpl<QChar> {};
127 template <> struct ViewForImpl<char16_t*> : ViewForImpl<QStringView> {};
128 template <> struct ViewForImpl<const char16_t*> : ViewForImpl<QStringView> {};
129 template <typename LHS, typename RHS>
131 template <typename Char, typename...Args>
132 struct ViewForImpl<std::basic_string<Char, Args...>> : ViewForImpl<Char*> {};
133#ifdef __cpp_lib_string_view
134 template <typename Char, typename...Args>
135 struct ViewForImpl<std::basic_string_view<Char, Args...>> : ViewForImpl<Char*> {};
136#endif
137
138 // This metafunction maps a StringLike to a View (currently, QChar,
139 // QStringView, QLatin1StringView). This is what QStringTokenizerBase
140 // operates on. QStringTokenizer adds pinning to keep rvalues alive
141 // for the duration of the algorithm.
142 template <typename String>
143 using ViewFor = typename ViewForImpl<typename std::decay<String>::type>::type;
144
145 // Pinning:
146 // rvalues of owning string types need to be moved into QStringTokenizer
147 // to keep them alive for the lifetime of the tokenizer. For lvalues, we
148 // assume the user takes care of that.
149
150 // default: don't pin anything (characters are pinned implicitly)
151 template <typename String>
152 struct PinForImpl { using type = ViewFor<String>; };
153
154 // rvalue QString -> QString
155 template <>
156 struct PinForImpl<QString> { using type = QString; };
157
158 // rvalue std::basic_string -> basic_string
159 template <typename Char, typename...Args>
160 struct PinForImpl<std::basic_string<Char, Args...>>
161 { using type = std::basic_string<Char, Args...>; };
162
163 // rvalue QStringBuilder -> pin as the nested ConvertTo type
164 template <typename LHS, typename RHS>
167
168 template <typename StringLike>
169 using PinFor = typename PinForImpl<typename std::remove_cv<StringLike>::type>::type;
170
171 template <typename T> struct is_owning_string_type : std::false_type {};
172 template <> struct is_owning_string_type<QString> : std::true_type {};
173 template <typename...Args> struct is_owning_string_type<std::basic_string<Args...>> : std::true_type {};
174
175 // unpinned
176 template <typename T, bool pinned = is_owning_string_type<T>::value>
177 struct Pinning
178 {
179 // this is the storage for non-pinned types - no storage
180 constexpr Pinning(const T&) noexcept {}
181 // Since we don't store something, the view() method needs to be
182 // given something it can return.
183 constexpr T view(T t) const noexcept { return t; }
184 };
185
186 // pinned
187 template <typename T>
188 struct Pinning<T, true>
189 {
191 // specialisation for owning string types (QString, std::u16string):
192 // stores the string:
193 constexpr Pinning(T &&s) noexcept : m_string{std::move(s)} {}
194 // ... and thus view() uses that instead of the argument passed in:
195 constexpr QStringView view(const T&) const noexcept { return m_string; }
196 };
197
198 // NeedlePinning and HaystackPinning are there to distinguish them as
199 // base classes of QStringTokenizer. We use inheritance to reap the
200 // empty base class optimization.
201 template <typename T>
203 {
204 using Pinning<T>::Pinning;
205 template <typename Arg>
206 constexpr auto needleView(Arg &&a) noexcept
207 -> decltype(this->view(std::forward<Arg>(a)))
208 { return this->view(std::forward<Arg>(a)); }
209 };
210
211 template <typename T>
213 {
214 using Pinning<T>::Pinning;
215 template <typename Arg>
216 constexpr auto haystackView(Arg &&a) noexcept
217 -> decltype(this->view(std::forward<Arg>(a)))
218 { return this->view(std::forward<Arg>(a)); }
219 };
220
221 // The Base of a QStringTokenizer is QStringTokenizerBase for the views
222 // corresponding to the Haystack and Needle template arguments
223 //
224 // ie. QStringTokenizer<QString, QString>
225 // : QStringTokenizerBase<QStringView, QStringView> (+ pinning)
226 template <typename Haystack, typename Needle>
228} // namespace Tok
229} // namespace QtPrivate
230
231template <typename Haystack, typename Needle>
233 : private QtPrivate::Tok::HaystackPinning<Haystack>,
234 private QtPrivate::Tok::NeedlePinning<Needle>,
236{
237 using HPin = QtPrivate::Tok::HaystackPinning<Haystack>;
238 using NPin = QtPrivate::Tok::NeedlePinning<Needle>;
240 template <typename Container, typename HPin>
241 struct if_haystack_not_pinned_impl : std::enable_if<std::is_empty<HPin>::value, bool> {};
242 template <typename Container>
243 using if_haystack_not_pinned = typename if_haystack_not_pinned_impl<Container, HPin>::type;
244 template <typename Container, typename Iterator = decltype(std::begin(std::declval<Container>()))>
245 using if_compatible_container = typename std::enable_if<
247 typename Base::value_type,
249 >::value,
250 bool
251 >::type;
252public:
253 using value_type = typename Base::value_type;
255 using size_type = typename Base::size_type;
256 using reference = typename Base::reference;
258 using pointer = typename Base::pointer;
260 using iterator = typename Base::iterator;
262 using sentinel = typename Base::sentinel;
263
264#ifdef Q_QDOC
265 [[nodiscard]] iterator begin() const noexcept { return Base::begin(); }
266 [[nodiscard]] iterator cbegin() const noexcept { return begin(); }
267 [[nodiscard]] constexpr sentinel end() const noexcept { return {}; }
268 [[nodiscard]] constexpr sentinel cend() const noexcept { return {}; }
269#endif
270
271 constexpr explicit QStringTokenizer(Haystack haystack, Needle needle,
272 Qt::CaseSensitivity cs,
273 Qt::SplitBehavior sb = Qt::KeepEmptyParts)
275 // here, we present the haystack to Pinning<>, for optional storing.
276 // If it did store, haystack is moved-from and mustn't be touched
277 // any longer, which is why view() for these Pinning<>s ignores the
278 // argument.
279 : HPin{std::forward<Haystack>(haystack)},
280 NPin{std::forward<Needle>(needle)},
281 // If Pinning<> didn't store, we pass the haystack (ditto needle)
282 // to view() again, so it can be copied from there.
284 this->needleView(needle), sb, cs}
285 {}
286 constexpr explicit QStringTokenizer(Haystack haystack, Needle needle,
287 Qt::SplitBehavior sb = Qt::KeepEmptyParts,
288 Qt::CaseSensitivity cs = Qt::CaseSensitive)
290 : HPin{std::forward<Haystack>(haystack)},
291 NPin{std::forward<Needle>(needle)},
293 this->needleView(needle), sb, cs}
294 {}
295
296#ifdef Q_QDOC
297 template<typename LContainer> LContainer toContainer(LContainer &&c = {}) const & {}
298 template<typename RContainer> RContainer toContainer(RContainer &&c = {}) const && {}
299#else
300 template<typename Container = QList<value_type>, if_compatible_container<Container> = true>
301 Container toContainer(Container &&c = {}) const &
302 {
303 for (auto e : *this)
304 c.emplace_back(e);
305 return std::forward<Container>(c);
306 }
307 template<typename Container = QList<value_type>, if_compatible_container<Container> = true,
308 if_haystack_not_pinned<Container> = true>
309 Container toContainer(Container &&c = {}) const &&
310 {
311 for (auto e : *this)
312 c.emplace_back(e);
313 return std::forward<Container>(c);
314 }
315#endif
316};
317
318namespace QtPrivate {
319namespace Tok {
320// This meta function just calculated the template arguments for the
321// QStringTokenizer (not -Base), based on the actual arguments passed
322// to qTokenize() (or the ctor, with CTAD). It basically detects rvalue
323// QString and std::basic_string and otherwise decays the arguments to
324// the respective view type.
325//
326// #define works around a C++ restriction: [temp.deduct.guide]/3 seems
327// to ask for the simple-template-id following the `->` of a deduction
328// guide to be identical to the class name for which we guide deduction.
329// In particular, Clang rejects a template alias there, while GCC accepts
330// it.
331#define Q_TOK_RESULT
332 QStringTokenizer<
333 QtPrivate::Tok::PinFor<Haystack>,
334 QtPrivate::Tok::PinFor<Needle>
335 >
336 /*end*/
337template <typename Haystack, typename Needle>
339template <typename Haystack, typename Needle>
341}
342}
343
344#ifdef __cpp_deduction_guides
345// these tell the compiler how to determine the QStringTokenizer
346// template arguments based on the constructor arguments (CTAD):
347template <typename Haystack, typename Needle>
348QStringTokenizer(Haystack&&, Needle&&)
349 -> Q_TOK_RESULT;
350template <typename Haystack, typename Needle>
351QStringTokenizer(Haystack&&, Needle&&, Qt::SplitBehavior)
352 -> Q_TOK_RESULT;
353template <typename Haystack, typename Needle>
354QStringTokenizer(Haystack&&, Needle&&, Qt::SplitBehavior, Qt::CaseSensitivity)
355 -> Q_TOK_RESULT;
356template <typename Haystack, typename Needle>
357QStringTokenizer(Haystack&&, Needle&&, Qt::CaseSensitivity)
358 -> Q_TOK_RESULT;
359template <typename Haystack, typename Needle>
360QStringTokenizer(Haystack&&, Needle&&, Qt::CaseSensitivity, Qt::SplitBehavior)
361 -> Q_TOK_RESULT;
362#endif
363
364#undef Q_TOK_RESULT
365
366template <typename Haystack, typename Needle, typename...Flags>
367[[nodiscard]] constexpr auto
368qTokenize(Haystack &&h, Needle &&n, Flags...flags)
371 std::forward<Needle>(n), flags...})
372{ return QtPrivate::Tok::TokenizerResult<Haystack, Needle>{std::forward<Haystack>(h),
373 std::forward<Needle>(n),
374 flags...}; }
375
376template <typename Haystack, typename Needle>
377auto QStringTokenizerBase<Haystack, Needle>::next(tokenizer_state state) const noexcept -> next_result
378{
379 while (true) {
380 if (state.end < 0) {
381 // already at end:
382 return {{}, false, state};
383 }
384 state.end = m_haystack.indexOf(m_needle, state.start + state.extra, m_cs);
385 Haystack result;
386 if (state.end >= 0) {
387 // token separator found => return intermediate element:
388 result = m_haystack.sliced(state.start, state.end - state.start);
389 const auto ns = QtPrivate::Tok::size(m_needle);
390 state.start = state.end + ns;
391 state.extra = (ns == 0 ? 1 : 0);
392 } else {
393 // token separator not found => return final element:
394 result = m_haystack.sliced(state.start);
395 }
396 if ((m_sb & Qt::SkipEmptyParts) && result.isEmpty())
397 continue;
398 return {result, true, state};
399 }
400}
401
402QT_END_NAMESPACE
403
404#endif /* QSTRINGTOKENIZER_H */
Qt::CaseSensitivity m_cs
~QStringTokenizerBaseBase()=default
constexpr QStringTokenizerBaseBase(Qt::SplitBehavior sb, Qt::CaseSensitivity cs) noexcept
friend constexpr bool operator!=(const iterator &lhs, sentinel) noexcept
constexpr const Haystack * operator->() const
friend constexpr bool operator==(const iterator &lhs, sentinel) noexcept
iterator() noexcept=default
constexpr const Haystack & operator*() const
friend constexpr bool operator!=(sentinel, const iterator &rhs) noexcept
friend constexpr bool operator==(sentinel, const iterator &rhs) noexcept
friend constexpr bool operator==(sentinel, sentinel) noexcept
friend constexpr bool operator!=(sentinel, sentinel) noexcept
constexpr sentinel end() const noexcept
iterator begin() const noexcept
constexpr QStringTokenizerBase(Haystack haystack, Needle needle, Qt::SplitBehavior sb, Qt::CaseSensitivity cs) noexcept
iterator cbegin() const noexcept
constexpr sentinel cend() const noexcept
\inmodule QtCore
Container toContainer(Container &&c={}) const &&
Container toContainer(Container &&c={}) const &
constexpr QStringTokenizer(Haystack haystack, Needle needle, Qt::CaseSensitivity cs, Qt::SplitBehavior sb=Qt::KeepEmptyParts) noexcept(std::is_nothrow_copy_constructible< QStringTokenizer >::value)
\typealias QStringTokenizer::sentinel
Combined button and popup list for selecting options.
constexpr qsizetype size(QChar) noexcept
constexpr qsizetype size(const String &s) noexcept
\macro QT_NO_KEYWORDS >
Definition qcompare.h:24
#define Q_TOK_RESULT
constexpr auto qTokenize(Haystack &&h, Needle &&n, Flags...flags) noexcept(QtPrivate::Tok::is_nothrow_constructible_from< Haystack, Needle >::value) -> decltype(QtPrivate::Tok::TokenizerResult< Haystack, Needle >{std::forward< Haystack >(h), std::forward< Needle >(n), flags...})
friend constexpr bool operator!=(tokenizer_state lhs, tokenizer_state rhs) noexcept
friend constexpr bool operator==(tokenizer_state lhs, tokenizer_state rhs) noexcept
constexpr auto haystackView(Arg &&a) noexcept -> decltype(this->view(std::forward< Arg >(a)))
constexpr auto needleView(Arg &&a) noexcept -> decltype(this->view(std::forward< Arg >(a)))
constexpr QStringView view(const T &) const noexcept
constexpr Pinning(T &&s) noexcept
constexpr Pinning(const T &) noexcept
constexpr T view(T t) const noexcept