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