E-MailRelay

gclientptr.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 gclientptr.h
19///
20
21#ifndef G_NET_CLIENT_PTR_H
22#define G_NET_CLIENT_PTR_H
23
24#include "gdef.h"
25#include "gclient.h"
26#include "gnetdone.h"
27#include "gexception.h"
28#include "geventlogging.h"
29#include "gexceptionsource.h"
30#include "geventhandler.h"
31#include "gscope.h"
32#include "gslot.h"
33#include "glog.h"
34#include <memory>
35#include <type_traits>
36
37 namespace GNet
38{
39 class ClientPtrBase ;
40 template <typename T> class ClientPtr ;
41}
42
43 namespace GNet
44{
45 namespace ClientPtrImp /// An implementation namespace for GNet::ClientPtr.
46 {
47 template <typename T>
48 bool hasConnected( T * p ,
49 typename std::enable_if<std::is_convertible<T*,Client*>::value>::type * = nullptr )
50 {
51 return p ? static_cast<Client*>(p)->hasConnected() : false ;
52 }
53 template <typename T>
54 bool hasConnected( T * ,
55 typename std::enable_if<!std::is_convertible<T*,Client*>::value>::type * = nullptr )
56 {
57 return false ;
58 }
59 }
60}
61
62//| \class GNet::ClientPtrBase
63/// The non-template part of GNet::ClientPtr.
64///
65 class GNet::ClientPtrBase
66{
67 public:
68 G::Slot::Signal<const std::string&,const std::string&,const std::string&> & eventSignal() noexcept ;
69 ///< A signal that is linked to the contained client's
70 ///< eventSignal().
71
72 G::Slot::Signal<const std::string&> & deleteSignal() noexcept ;
73 ///< A signal that is triggered as the client is deleted
74 ///< following an exception handled by this class.
75 ///< The parameter is normally the exception string, but it
76 ///< is the empty string for GNet::Done exceptions or if the
77 ///< client was finished().
78
79 G::Slot::Signal<const std::string&> & deletedSignal() noexcept ;
80 ///< A signal that is triggered after deleteSignal() once
81 ///< the client has been deleted and the ClientPtr is empty.
82
83 protected:
84 ClientPtrBase() ;
85 ///< Default constructor.
86
87 void eventSlot( const std::string & , const std::string & , const std::string & ) ;
88 ///< Emits an eventSignal().
89
90 public:
91 virtual ~ClientPtrBase() = default ;
92 ClientPtrBase( const ClientPtrBase & ) = delete ;
93 ClientPtrBase( ClientPtrBase && ) = delete ;
94 ClientPtrBase & operator=( const ClientPtrBase & ) = delete ;
95 ClientPtrBase & operator=( ClientPtrBase && ) = delete ;
96
97 private:
98 G::Slot::Signal<const std::string&> m_deleted_signal ;
99 G::Slot::Signal<const std::string&,const std::string&,const std::string&> m_event_signal ;
100 G::Slot::Signal<const std::string&> m_delete_signal ;
101} ;
102
103//| \class GNet::ClientPtr
104/// A smart pointer class for GNet::Client or similar.
105///
106/// The ClientPtr is-a ExceptionHandler, so it should be the ExceptionHandler
107/// part of the Client's EventState:
108/// \code
109/// m_client_ptr.reset( new Client( m_es.eh(m_client_ptr) , ... ) ) ;
110/// \endcode
111///
112/// If that is done then the contained Client object will get deleted as
113/// the result of an exception thrown out of a network event handler
114/// (including GNet::Done) with internal notification to the Client's
115/// onDelete() method and external notification via the smart pointer's
116/// deleteSignal(). If the Client is deleted from the smart pointer's
117/// destructor then there are no notifications.
118///
119/// If the Client is given some higher-level object as its ExceptionHandler
120/// then the ClientPtr will not do any notification and the higher-level
121/// object must ensure that the Client object is deleted or disconnected
122/// when an exception is thrown:
123/// \code
124/// void Foo::fn()
125/// {
126/// m_client_ptr.reset( new Client( m_es.eh(this,&m_client_ptr) , ... ) ) ;
127/// }
128/// void Foo::onException( ExceptionSource * esrc , std::exception & e , bool done )
129/// {
130/// if( esrc == &m_client_ptr )
131/// {
132/// m_client_ptr->doOnDelete( e.what() , done ) ;
133/// m_client_ptr.reset() ; // or m_client_ptr->disconnect() ;
134/// }
135/// }
136/// \endcode
137///
138/// Failure to delete the client from within the higher-level object's
139/// exception handler will result in bad event handling, with the event
140/// loop raising events that are never cleared.
141///
142 template <typename T>
143 class GNet::ClientPtr : public ClientPtrBase , public ExceptionHandler , public ExceptionSource
144{
145 public:
146 G_EXCEPTION( InvalidState , tx("invalid state of network client holder") )
147
148 explicit ClientPtr( T * p = nullptr ) ;
149 ///< Constructor. Takes ownership of the new-ed client.
150
151 ~ClientPtr() override ;
152 ///< Destructor.
153
154 bool busy() const ;
155 ///< Returns true if the pointer is not nullptr.
156
157 void reset( T * p ) ;
158 ///< Resets the pointer. There is no call to onDelete()
159 ///< and no emitted signals.
160
161 void reset( std::unique_ptr<T> p ) ;
162 ///< Resets the pointer. There is no call to onDelete()
163 ///< and no emitted signals.
164
165 void reset() noexcept ;
166 ///< Resets the pointer. There is no call to onDelete()
167 ///< and no emitted signals.
168
169 T * get() noexcept ;
170 ///< Returns the pointer, or nullptr if deleted.
171
172 const T * get() const noexcept ;
173 ///< Returns the pointer, or nullptr if deleted.
174
175 T * operator->() ;
176 ///< Returns the pointer. Throws if deleted.
177
178 const T * operator->() const ;
179 ///< Returns the pointer. Throws if deleted.
180
181 bool hasConnected() const noexcept ;
182 ///< Returns true if any Client owned by this smart pointer
183 ///< has ever successfully connected. Returns false if T
184 ///< is-not-a Client.
185
186private: // overrides
187 void onException( ExceptionSource * , std::exception & , bool ) override ; // GNet::ExceptionHandler
188
189public:
190 ClientPtr( const ClientPtr & ) = delete ;
191 ClientPtr( ClientPtr && ) noexcept ;
192 ClientPtr & operator=( const ClientPtr & ) = delete ;
193 ClientPtr & operator=( ClientPtr && ) noexcept ;
194
195private:
196 T * set( T * ) ;
197 T * set( std::nullptr_t ) noexcept ;
198 T * release() noexcept ;
199 void connectSignals( T & ) ;
200 void disconnectSignals( T & ) ;
201
202private:
203 T * m_p ;
204 bool m_has_connected {false} ;
205} ;
206
207 template <typename T>
208 GNet::ClientPtr<T>::ClientPtr( T * p ) :
209 m_p(p)
210{
211 if( m_p != nullptr )
212 connectSignals( *m_p ) ;
213}
214
215 namespace GNet
216{
217 template <typename T>
218 ClientPtr<T>::~ClientPtr()
219 {
220 delete release() ;
221 }
222}
223
224 template <typename T>
225 void GNet::ClientPtr<T>::onException( ExceptionSource * , std::exception & e , bool done )
226{
227 if( m_p == nullptr )
228 {
229 G_WARNING( "GNet::ClientPtr::onException: unhandled exception: " << e.what() ) ;
230 throw ; // should never get here -- rethrow just in case
231 }
232 else
233 {
234 std::string reason = ( done || m_p->finished() ) ? std::string() : std::string(e.what()) ;
235 {
236 G::ScopeExit _( [this](){this->reset();} ) ; // (was release())
237 m_p->doOnDelete( e.what() , done ) ; // first
238 deleteSignal().emit( reason ) ; // second -- m_p still set
239 ///< T client deleted here
240 }
241 deletedSignal().emit( reason ) ;
242 }
243}
244
245 template <typename T>
246T * GNet::ClientPtr<T>::set( T * p )
247{
248 if( m_p != nullptr )
249 {
250 if( ClientPtrImp::hasConnected(m_p) ) m_has_connected = true ;
251 disconnectSignals( *m_p ) ;
252 }
253 if( p != nullptr )
254 {
255 connectSignals( *p ) ; // may throw AlreadyConnected
256 }
257 std::swap( p , m_p ) ;
258 return p ; // return old m_p for deletion
259}
260
261 template <typename T>
262T * GNet::ClientPtr<T>::set( std::nullptr_t ) noexcept
263{
264 if( m_p != nullptr )
265 {
266 if( ClientPtrImp::hasConnected(m_p) ) m_has_connected = true ;
267 disconnectSignals( *m_p ) ;
268 }
269 T * old_p = m_p ;
270 m_p = nullptr ;
271 return old_p ;
272}
273
274 template <typename T>
275T * GNet::ClientPtr<T>::release() noexcept
276{
277 return set( nullptr ) ;
278}
279
280 template <typename T>
281 void GNet::ClientPtr<T>::reset( T * p )
282{
283 delete set( p ) ;
284}
285
286 template <typename T>
287 void GNet::ClientPtr<T>::reset( std::unique_ptr<T> p )
288{
289 delete set( p.release() ) ;
290}
291
292 template <typename T>
293 void GNet::ClientPtr<T>::reset() noexcept
294{
295 delete set( nullptr ) ;
296}
297
298 template <typename T>
299T * GNet::ClientPtr<T>::get() noexcept
300{
301 return m_p ;
302}
303
304 template <typename T>
305 const T * GNet::ClientPtr<T>::get() const noexcept
306{
307 return m_p ;
308}
309
310 template <typename T>
311 bool GNet::ClientPtr<T>::busy() const
312{
313 return m_p != nullptr ;
314}
315
316 template <typename T>
317 bool GNet::ClientPtr<T>::hasConnected() const noexcept
318{
319 return m_has_connected ;
320}
321
322 template <typename T>
323T * GNet::ClientPtr<T>::operator->()
324{
325 if( m_p == nullptr )
326 throw InvalidState() ;
327 return m_p ;
328}
329
330 template <typename T>
331 const T * GNet::ClientPtr<T>::operator->() const
332{
333 if( m_p == nullptr )
334 throw InvalidState() ;
335 return m_p ;
336}
337
338 template <typename T>
339 void GNet::ClientPtr<T>::connectSignals( T & t )
340{
341 t.eventSignal().connect( G::Slot::slot(*static_cast<ClientPtrBase*>(this),&ClientPtr<T>::eventSlot) ) ;
342}
343
344 template <typename T>
345 void GNet::ClientPtr<T>::disconnectSignals( T & t )
346{
347 t.eventSignal().disconnect() ;
348}
349
350#endif
GNet::ClientPtrBase
The non-template part of GNet::ClientPtr.
Definition: gclientptr.h:66
GNet::ClientPtrBase::deletedSignal
G::Slot::Signal< const std::string & > & deletedSignal() noexcept
A signal that is triggered after deleteSignal() once the client has been deleted and the ClientPtr is...
Definition: gclientptr.cpp:27
GNet::ClientPtrBase::deleteSignal
G::Slot::Signal< const std::string & > & deleteSignal() noexcept
A signal that is triggered as the client is deleted following an exception handled by this class.
Definition: gclientptr.cpp:37
GNet::ClientPtrBase::ClientPtrBase
ClientPtrBase()
Default constructor.
GNet::ClientPtrBase::eventSlot
void eventSlot(const std::string &, const std::string &, const std::string &)
Emits an eventSignal().
Definition: gclientptr.cpp:42
GNet::ClientPtrBase::eventSignal
G::Slot::Signal< const std::string &, const std::string &, const std::string & > & eventSignal() noexcept
A signal that is linked to the contained client's eventSignal().
Definition: gclientptr.cpp:32
GNet::ClientPtr
A smart pointer class for GNet::Client or similar.
Definition: gclientptr.h:144
GNet::ClientPtr::busy
bool busy() const
Returns true if the pointer is not nullptr.
Definition: gclientptr.h:311
GNet::ClientPtr::operator->
T * operator->()
Returns the pointer. Throws if deleted.
Definition: gclientptr.h:323
GNet::ClientPtr::ClientPtr
ClientPtr(T *p=nullptr)
Constructor. Takes ownership of the new-ed client.
Definition: gclientptr.h:208
GNet::ClientPtr::get
T * get() noexcept
Returns the pointer, or nullptr if deleted.
Definition: gclientptr.h:299
GNet::ClientPtr::hasConnected
bool hasConnected() const noexcept
Returns true if any Client owned by this smart pointer has ever successfully connected.
Definition: gclientptr.h:317
GNet::ClientPtr::~ClientPtr
~ClientPtr() override
Destructor.
Definition: gclientptr.h:218
GNet::ClientPtr::reset
void reset() noexcept
Resets the pointer.
Definition: gclientptr.h:293
GNet::ExceptionHandler
An abstract interface for handling exceptions thrown out of event-loop callbacks (socket/future event...
Definition: gexceptionhandler.h:44
GNet::ExceptionSource
A mixin base class that identifies the source of an exception when delivered to GNet::ExceptionHandle...
Definition: gexceptionsource.h:39
G::ScopeExit
A class that calls an exit function at the end of its scope.
Definition: gscope.h:47
gclient.h
geventhandler.h
geventlogging.h
gexception.h
gexceptionsource.h
glog.h
gnetdone.h
gscope.h
gslot.h
GNet
Network classes.
Definition: gdef.h:1243
G::Slot::slot
Slot< Args... > slot(TSink &sink, void(TSink::*method)(Args...))
A factory function for Slot objects.
Definition: gslot.h:240
G::tx
constexpr const char * tx(const char *p) noexcept
A briefer alternative to G::gettext_noop().
Definition: ggettext.h:84
std
STL namespace.
G::Slot::Signal< const std::string &, const std::string &, const std::string & >