E-MailRelay

gclientptr.h
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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 "gexceptionsource.h"
29#include "geventhandler.h"
30#include "gslot.h"
31#include <memory>
32
33 namespace GNet
34{
35 class ClientPtrBase ;
36 template <typename T> class ClientPtr ;
37}
38
39//| \class GNet::ClientPtrBase
40/// The non-template part of GNet::ClientPtr. It is an ExcptionHandler
41/// so that exceptions thrown by the Client out to the event loop can
42/// be delivered back to reset the ClientPtr with the expected Client
43/// onDelete() semantics (like GNet::ServerPeer).
44///
45 class GNet::ClientPtrBase : public ExceptionHandler , public ExceptionSource
46{
47 public:
48 G::Slot::Signal<const std::string&,const std::string&,const std::string&> & eventSignal() ;
49 ///< See GNet::Client::eventSignal().
50
51 G::Slot::Signal<const std::string&> & deleteSignal() ;
52 ///< A signal that is triggered as the client is deleted
53 ///< following an exception handled by this class.
54 ///< The parameter is normally the exception string, but it
55 ///< is the empty string for GNet::Done exceptions or if the
56 ///< client was finished().
57
58 G::Slot::Signal<const std::string&> & deletedSignal() ;
59 ///< A signal that is triggered after deleteSignal() once
60 ///< the client has been deleted and the ClientPtr is empty.
61
62 protected:
63 ClientPtrBase() ;
64 ///< Default constructor.
65
66 void connectSignals( Client & ) ;
67 ///< Connects the given client's signals to this object's
68 ///< slots.
69
70 void disconnectSignals( Client & ) noexcept ;
71 ///< Disconnects the given client's signals from this
72 ///< object's slots.
73
74 public:
75 ~ClientPtrBase() override = default ;
76 ClientPtrBase( const ClientPtrBase & ) = delete ;
77 ClientPtrBase( ClientPtrBase && ) = delete ;
78 void operator=( const ClientPtrBase & ) = delete ;
79 void operator=( ClientPtrBase && ) = delete ;
80
81 private:
82 void eventSlot( const std::string & , const std::string & , const std::string & ) ;
83
84 private:
85 G::Slot::Signal<const std::string&> m_deleted_signal ;
86 G::Slot::Signal<const std::string&,const std::string&,const std::string&> m_event_signal ;
87 G::Slot::Signal<const std::string&> m_delete_signal ;
88} ;
89
90//| \class GNet::ClientPtr
91/// A smart pointer class for GNet::Client.
92///
93/// The ClientPtr is-a ExceptionHandler, so it should normally be used
94/// as the Client's ExceptionSink:
95/// \code
96/// m_client_ptr.reset( new Client( ExceptionSink(m_client_ptr,m_es.esrc()) , ... ) ) ;
97/// \endcode
98///
99/// If that is done then the contained Client object will get deleted as
100/// the result of an exception thrown out of a network event handler
101/// (including GNet::Done) with internal notification to the Client's
102/// onDelete() method and external notification via the smart pointer's
103/// deleteSignal(). If the Client is deleted from the smart pointer's
104/// destructor then there are no notifications.
105///
106/// If the Client is given some higher-level object as its ExceptionSink
107/// then the ClientPtr will not do any notification and the higher-level
108/// object must ensure that the Client object is deleted or disconnected
109/// when an exception is thrown:
110/// \code
111/// void Foo::fn()
112/// {
113/// m_client_ptr.reset( new Client( ExceptionSink(*this,&m_client_ptr) , ... ) ) ;
114/// }
115/// void Foo::onException( ExceptionSource * esrc , std::exception & e , bool done )
116/// {
117/// if( esrc == m_client_ptr.get() )
118/// {
119/// m_client_ptr->doOnDelete( e.what() , done ) ;
120/// m_client_ptr.reset() ; // or m_client_ptr->disconnect() ;
121/// }
122/// }
123/// \endcode
124///
125/// Failure to delete the client from within the higher-level object's
126/// exception handler will result in bad event handling, with the event
127/// loop raising events that are never cleared.
128///
129/// Note that the ClientPtr exception handler ignores the ExceptionSource
130/// pointer, so it can be set to be the same as the higher-level object,
131/// for better event logging (as shown above).
132///
133 template <typename T>
134 class GNet::ClientPtr : public ClientPtrBase
135{
136 public:
137 G_EXCEPTION( InvalidState , "invalid state of network client holder" ) ;
138
139 explicit ClientPtr( T * p = nullptr ) ;
140 ///< Constructor. Takes ownership of the new-ed client.
141
142 ~ClientPtr() override ;
143 ///< Destructor.
144
145 bool busy() const ;
146 ///< Returns true if the pointer is not nullptr.
147
148 void reset( T * p ) noexcept ;
149 ///< Resets the pointer. There is no call to onDelete()
150 ///< and no emitted signals.
151
152 void reset( std::unique_ptr<T> p ) noexcept ;
153 ///< Resets the pointer. There is no call to onDelete()
154 ///< and no emitted signals.
155
156 void reset() noexcept ;
157 ///< Resets the pointer. There is no call to onDelete()
158 ///< and no emitted signals.
159
160 T * get() noexcept ;
161 ///< Returns the pointer, or nullptr if deleted.
162
163 T * operator->() ;
164 ///< Returns the pointer. Throws if deleted.
165
166 const T * operator->() const ;
167 ///< Returns the pointer. Throws if deleted.
168
169 bool hasConnected() const noexcept ;
170 ///< Returns true if any Client owned by this smart pointer
171 ///< has ever successfully connected.
172
173private: // overrides
174 void onException( ExceptionSource * , std::exception & , bool ) override ; // Override from GNet::ExceptionHandler.
175 std::string exceptionSourceId() const override ; // Override from GNet::ExceptionSource.
176
177public:
178 ClientPtr( const ClientPtr & ) = delete ;
179 ClientPtr( ClientPtr && ) = delete ;
180 void operator=( const ClientPtr & ) = delete ;
181 void operator=( ClientPtr && ) = delete ;
182
183private:
184 T * set( T * ) ;
185 T * set( std::nullptr_t ) noexcept ;
186 T * release() noexcept ;
187
188private:
189 T * m_p ;
190 bool m_has_connected{false} ;
191} ;
192
193 template <typename T>
194 GNet::ClientPtr<T>::ClientPtr( T * p ) :
195 m_p(p)
196{
197 if( m_p != nullptr )
198 connectSignals( *m_p ) ;
199}
200
201 template <typename T>
202 GNet::ClientPtr<T>::~ClientPtr()
203{
204 delete release() ;
205}
206
207 template <typename T>
208 void GNet::ClientPtr<T>::onException( ExceptionSource * , std::exception & e , bool done )
209{
210 if( m_p == nullptr )
211 {
212 G_WARNING( "GNet::ClientPtr::onException: unhandled exception: " << e.what() ) ;
213 throw ; // should never get here -- rethrow just in case
214 }
215 else
216 {
217 std::string reason = ( done || m_p->finished() ) ? std::string() : std::string(e.what()) ;
218 {
219 std::unique_ptr<T> ptr( release() ) ;
220 ptr->doOnDelete( e.what() , done ) ; // first
221 deleteSignal().emit( reason ) ; // second
222 ///< T client deleted here
223 }
224 deletedSignal().emit( reason ) ;
225 }
226}
227
228 template <typename T>
229T * GNet::ClientPtr<T>::set( T * p )
230{
231 if( m_p != nullptr )
232 {
233 if( m_p->hasConnected() ) m_has_connected = true ;
234 disconnectSignals( *m_p ) ;
235 }
236 if( p != nullptr )
237 {
238 connectSignals( *p ) ; // may throw AlreadyConnected
239 }
240 std::swap( p , m_p ) ;
241 return p ; // return old m_p for deletion
242}
243
244 template <typename T>
245T * GNet::ClientPtr<T>::set( std::nullptr_t ) noexcept
246{
247 if( m_p != nullptr )
248 {
249 if( m_p->hasConnected() ) m_has_connected = true ;
250 disconnectSignals( *m_p ) ;
251 }
252 T * old_p = m_p ;
253 m_p = nullptr ;
254 return old_p ;
255}
256
257 template <typename T>
258T * GNet::ClientPtr<T>::release() noexcept
259{
260 return set( nullptr ) ;
261}
262
263 template <typename T>
264 void GNet::ClientPtr<T>::reset( T * p ) noexcept
265{
266 delete set( p ) ;
267}
268
269 template <typename T>
270 void GNet::ClientPtr<T>::reset( std::unique_ptr<T> p ) noexcept
271{
272 delete set( p.release() ) ;
273}
274
275 template <typename T>
276 void GNet::ClientPtr<T>::reset() noexcept
277{
278 delete set( nullptr ) ;
279}
280
281 template <typename T>
282T * GNet::ClientPtr<T>::get() noexcept
283{
284 return m_p ;
285}
286
287 template <typename T>
288 bool GNet::ClientPtr<T>::busy() const
289{
290 return m_p != nullptr ;
291}
292
293 template <typename T>
294 bool GNet::ClientPtr<T>::hasConnected() const noexcept
295{
296 return m_has_connected ;
297}
298
299 template <typename T>
300T * GNet::ClientPtr<T>::operator->()
301{
302 if( m_p == nullptr )
303 throw InvalidState() ;
304 return m_p ;
305}
306
307 template <typename T>
308 const T * GNet::ClientPtr<T>::operator->() const
309{
310 if( m_p == nullptr )
311 throw InvalidState() ;
312 return m_p ;
313}
314
315 template <typename T>
316std::string GNet::ClientPtr<T>::exceptionSourceId() const
317{
318 const ExceptionSource * base_p = m_p ;
319 return base_p ? base_p->exceptionSourceId() : std::string() ;
320}
321
322#endif
GNet::ClientPtrBase
The non-template part of GNet::ClientPtr.
Definition: gclientptr.h:46
GNet::ClientPtrBase::connectSignals
void connectSignals(Client &)
Connects the given client's signals to this object's slots.
Definition: gclientptr.cpp:27
GNet::ClientPtrBase::disconnectSignals
void disconnectSignals(Client &) noexcept
Disconnects the given client's signals from this object's slots.
Definition: gclientptr.cpp:47
GNet::ClientPtrBase::deletedSignal
G::Slot::Signal< const std::string & > & deletedSignal()
A signal that is triggered after deleteSignal() once the client has been deleted and the ClientPtr is...
Definition: gclientptr.cpp:32
GNet::ClientPtrBase::ClientPtrBase
ClientPtrBase()
Default constructor.
GNet::ClientPtrBase::deleteSignal
G::Slot::Signal< const std::string & > & deleteSignal()
A signal that is triggered as the client is deleted following an exception handled by this class.
Definition: gclientptr.cpp:42
GNet::ClientPtrBase::eventSignal
G::Slot::Signal< const std::string &, const std::string &, const std::string & > & eventSignal()
See GNet::Client::eventSignal().
Definition: gclientptr.cpp:37
GNet::ClientPtr
A smart pointer class for GNet::Client.
Definition: gclientptr.h:135
GNet::ClientPtr::busy
bool busy() const
Returns true if the pointer is not nullptr.
Definition: gclientptr.h:288
GNet::ClientPtr::operator->
T * operator->()
Returns the pointer. Throws if deleted.
Definition: gclientptr.h:300
GNet::ClientPtr::reset
void reset(T *p) noexcept
Resets the pointer.
Definition: gclientptr.h:264
GNet::ClientPtr::ClientPtr
ClientPtr(T *p=nullptr)
Constructor. Takes ownership of the new-ed client.
Definition: gclientptr.h:194
GNet::ClientPtr::get
T * get() noexcept
Returns the pointer, or nullptr if deleted.
Definition: gclientptr.h:282
GNet::ClientPtr::hasConnected
bool hasConnected() const noexcept
Returns true if any Client owned by this smart pointer has ever successfully connected.
Definition: gclientptr.h:294
GNet::ClientPtr::reset
void reset(std::unique_ptr< T > p) noexcept
Resets the pointer.
Definition: gclientptr.h:270
GNet::ClientPtr::~ClientPtr
~ClientPtr() override
Destructor.
Definition: gclientptr.h:202
GNet::ClientPtr::reset
void reset() noexcept
Resets the pointer.
Definition: gclientptr.h:276
GNet::Client
A class for making an outgoing connection to a remote server, with support for socket-level protocols...
Definition: gclient.h:75
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:40
GNet::ExceptionSource::exceptionSourceId
virtual std::string exceptionSourceId() const
Returns an identifying string for logging purposes, or the empty string.
Definition: gexceptionsource.cpp:25
gclient.h
geventhandler.h
gexception.h
gexceptionsource.h
gnetdone.h
gslot.h
GNet
Network classes.
Definition: gdef.h:1115
G::Slot::Signal< const std::string &, const std::string &, const std::string & >