E-MailRelay
gstringfield.h
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gstringfield.h
19///
20
21#ifndef G_STRING_FIELD_H
22#define G_STRING_FIELD_H
23
24#include "gdef.h"
25#include "gstringview.h"
26#include "gassert.h"
27#include <string>
28#include <algorithm>
29#include <iterator>
30#include <cstddef> // std::nullptr_t
31
32namespace G
33{
34 template <typename T> class StringFieldT ;
35 template <typename T> class StringFieldIteratorT ;
36 using StringField = StringFieldT<std::string> ;
37 using StringFieldView = StringFieldT<std::string_view> ;
38}
39
40//| \class G::StringFieldT
41/// A zero-copy string field iterator where the field separators
42/// are short fixed strings.
43///
44/// \code
45/// for( StringField f(s,"\r\n",2U) ; f ; ++f )
46/// std::cout << std::string_view(f.data(),f.size()) << "\n" ;
47/// \endcode
48///
49/// \see G::Str::splitIntoFields(), G::SubString
50///
51template <typename T>
53{
54public:
55 using char_type = typename T::value_type ;
56
57 StringFieldT( const T & s , const char_type * sep , std::size_t sepn ) noexcept ;
58 ///< Constructor. The parameters must stay valid
59 ///< for the object lifefime.
60 ///<
61 ///< The rvalue overload is deleted to avoid passing a
62 ///< temporary T that has been implicitly constructed from
63 ///< something else. Temporary std::string_views constructed
64 ///< from a string would be safe, but might be unsafe for
65 ///< other types.
66
67 StringFieldT( const T & s , char_type sep ) noexcept ;
68 ///< Constructor. The parameters must stay valid
69 ///< for the object lifefime.
70
71 explicit operator bool() const noexcept ;
72 ///< Returns true if a valid field.
73
74 bool valid() const noexcept ;
75 ///< Returns true if a valid field.
76
77 static constexpr bool deref_operator_noexcept = std::is_same<T,std::string_view>::value ;
78
79 T operator()() const noexcept(deref_operator_noexcept) ;
80 ///< Returns the current field substring. Prefer data()
81 ///< and size() to avoid copying.
82
83 StringFieldT<T> & operator++() noexcept ;
84 ///< Moves to the next field.
85
86 const char_type * data() const noexcept ;
87 ///< Returns the current field pointer.
88
89 std::size_t size() const noexcept ;
90 ///< Returns the current field size.
91
92 bool first() const noexcept ;
93 ///< Returns true if the current field is the first.
94
95 bool last() const noexcept ;
96 ///< Returns true if the current field is the last.
97
98 std::size_t count() const noexcept ;
99 ///< Returns the number of fields.
100
101public:
102 ~StringFieldT() = default ;
103 StringFieldT( T && s , const char * , std::size_t ) = delete ;
104 StringFieldT( T && s , char ) = delete ;
105 StringFieldT( const StringFieldT<T> & ) = delete ;
106 StringFieldT( StringFieldT<T> && ) = delete ;
107 StringFieldT<T> & operator=( const StringFieldT<T> & ) = delete ;
108 StringFieldT<T> & operator=( StringFieldT<T> && ) = delete ;
109
110private:
111 const T & m_s ;
112 char m_c ;
113 const char_type * m_sep ;
114 std::size_t m_sepn ;
115 std::size_t m_fpos ;
116 std::size_t m_fendpos ;
117} ;
118
119//| \class G::StringFieldIteratorT
120/// A standard forward iterator for G::StringFieldT:
121/// \code
122/// StringFieldView f( "foo,bar"_sv , "," , 1U ) ;
123/// std::copy( begin(f) , end(f) , std::back_inserter(list) ) ; // or...
124/// for( std::string_view sv : f ) list.push_back( sv ) ;
125/// \endcode
126///
127template <typename T>
129{
130public:
131 using iterator_category = std::forward_iterator_tag ;
132 using value_type = T ;
133 using difference_type = int ;
134 using pointer = T* ;
135 using reference = T& ;
136 explicit StringFieldIteratorT( StringFieldT<T> & ) noexcept ;
137 explicit StringFieldIteratorT( std::nullptr_t ) noexcept ;
138 StringFieldIteratorT & operator++() noexcept ;
139 T operator*() const noexcept(StringFieldT<T>::deref_operator_noexcept) ;
140 bool operator==( StringFieldIteratorT other ) const noexcept ;
141 bool operator!=( StringFieldIteratorT other ) const noexcept ;
142private:
143 StringFieldT<T> * f ;
144} ;
145
146namespace G
147{
148 namespace StringFieldImp /// An implementation namespace for G::StringField.
149 {
150 template <typename T> inline T substr( const T & s ,
151 std::size_t pos , std::size_t len ) noexcept
152 {
153 try { return s.substr( pos , len ) ; } catch(...) { return {} ; }
154 }
155 template <> std::string_view inline substr<std::string_view>( const std::string_view & s ,
156 std::size_t pos , std::size_t len ) noexcept
157 {
158 return sv_substr_noexcept( s , pos , len ) ;
159 }
160 }
161}
162
163template <typename T>
164G::StringFieldT<T>::StringFieldT( const T & s , const char_type * sep_p , std::size_t sep_n ) noexcept :
165 m_s(s) ,
166 m_c('\0') ,
167 m_sep(sep_p) ,
168 m_sepn(sep_n) ,
169 m_fpos(s.empty()?std::string::npos:0U) ,
170 m_fendpos(s.find(m_sep,0U,m_sepn))
171{
172}
173
174template <typename T>
175G::StringFieldT<T>::StringFieldT( const T & s , char_type sep ) noexcept :
176 m_s(s) ,
177 m_c(sep) ,
178 m_sep(&m_c) ,
179 m_sepn(1U) ,
180 m_fpos(s.empty()?std::string::npos:0U) ,
181 m_fendpos(s.find(m_sep,0U,m_sepn))
182{
183}
184
185template <typename T>
186const typename T::value_type * G::StringFieldT<T>::data() const noexcept
187{
188 G_ASSERT( m_fpos != std::string::npos ) ;
189 return m_s.data() + m_fpos ;
190}
191
192template <typename T>
193std::size_t G::StringFieldT<T>::size() const noexcept
194{
195 G_ASSERT( m_fpos != std::string::npos ) ;
196 return (m_fendpos==std::string::npos?m_s.size():m_fendpos) - m_fpos ;
197}
198
199template <typename T>
200G::StringFieldT<T>::operator bool() const noexcept
201{
202 return m_fpos != std::string::npos ;
203}
204
205template <typename T>
206bool G::StringFieldT<T>::valid() const noexcept
207{
208 return m_fpos != std::string::npos ;
209}
210
211template <typename T>
212T G::StringFieldT<T>::operator()() const noexcept(deref_operator_noexcept)
213{
214 if( m_fpos == std::string::npos ) return {} ;
215 return StringFieldImp::substr<T>( m_s , m_fpos , size() ) ;
216}
217
218template <typename T>
220{
221 m_fpos = m_fendpos ;
222 if( m_fpos != std::string::npos )
223 {
224 m_fpos = std::min( m_s.size() , m_fpos + m_sepn ) ;
225 m_fendpos = m_s.find( m_sep , m_fpos , m_sepn ) ; // documented as non-throwing
226 }
227 return *this ;
228}
229
230template <typename T>
231bool G::StringFieldT<T>::first() const noexcept
232{
233 return m_fpos == 0U ;
234}
235
236template <typename T>
237bool G::StringFieldT<T>::last() const noexcept
238{
239 return m_fendpos == std::string::npos ;
240}
241
242template <typename T>
243std::size_t G::StringFieldT<T>::count() const noexcept
244{
245 std::size_t n = 0U ;
246 for( StringFieldT<T> f( m_s , m_sep , m_sepn ) ; f ; ++f )
247 n++ ;
248 return n ;
249}
250
251// --
252
253template <typename T>
255 f(&f_in)
256{
257}
258
259template <typename T>
260G::StringFieldIteratorT<T>::StringFieldIteratorT( std::nullptr_t ) noexcept :
261 f(nullptr)
262{
263}
264
265template <typename T>
267{
268 if( f ) ++(*f) ;
269 return *this ;
270}
271
272template <typename T>
273T G::StringFieldIteratorT<T>::operator*() const noexcept(StringFieldT<T>::deref_operator_noexcept)
274{
275 return (*f)() ;
276}
277
278template <typename T>
279bool G::StringFieldIteratorT<T>::operator==( StringFieldIteratorT other ) const noexcept
280{
281 return (f&&f->valid()?f->data():nullptr) == (other.f&&other.f->valid()?other.f->data():nullptr) ;
282}
283
284template <typename T>
285bool G::StringFieldIteratorT<T>::operator!=( StringFieldIteratorT other ) const noexcept
286{
287 return !(*this == other) ;
288}
289
290namespace G
291{
292 template <typename T> StringFieldIteratorT<T> begin( StringFieldT<T> & f ) noexcept
293 {
294 return StringFieldIteratorT<T>( f ) ;
295 }
296}
297
298namespace G
299{
300 template <typename T> StringFieldIteratorT<T> end( StringFieldT<T> & ) noexcept
301 {
302 return StringFieldIteratorT<T>( nullptr ) ;
303 }
304}
305
306#endif
A standard forward iterator for G::StringFieldT:
Definition: gstringfield.h:129
A zero-copy string field iterator where the field separators are short fixed strings.
Definition: gstringfield.h:53
bool first() const noexcept
Returns true if the current field is the first.
Definition: gstringfield.h:231
bool valid() const noexcept
Returns true if a valid field.
Definition: gstringfield.h:206
bool last() const noexcept
Returns true if the current field is the last.
Definition: gstringfield.h:237
StringFieldT< T > & operator++() noexcept
Moves to the next field.
Definition: gstringfield.h:219
StringFieldT(const T &s, const char_type *sep, std::size_t sepn) noexcept
Constructor.
Definition: gstringfield.h:164
T operator()() const noexcept(deref_operator_noexcept)
Returns the current field substring.
Definition: gstringfield.h:212
const char_type * data() const noexcept
Returns the current field pointer.
Definition: gstringfield.h:186
std::size_t size() const noexcept
Returns the current field size.
Definition: gstringfield.h:193
std::size_t count() const noexcept
Returns the number of fields.
Definition: gstringfield.h:243
Low-level classes.
Definition: garg.h:36