Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
OutputStream.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Streams_OutputStream_h_
5#define _Stroika_Foundation_Streams_OutputStream_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <memory>
10
12#include "Stroika/Foundation/Common/Common.h"
13#include "Stroika/Foundation/Memory/Common.h"
15
16/**
17 *
18 * \file
19 *
20 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
21 *
22 * TODO:
23 * @todo http://stroika-bugs.sophists.com/browse/STK-998 - support EWouldBlock on output stream writes (like we do for InputStream)
24 *
25 * @todo Consider if Seek () past end of stream on writable stream should be
26 * allowed. Often - like in UNIX - this works - and you can then write there,
27 * and this creates a hole in the file read back as zeros.
28 *
29 * Its easier to DISALLOW this now, and then lift the restriction, and later allow it,
30 * so since I'm unsure, disallow for now. This can always be simulated with an extra
31 * zero write, and it assuming no seek past EOF makes implementations simpler, and
32 * definition more consistent (read).
33 *
34 * @todo Add ability to SetEOF (); You can SEEK, but if you seek backwards, and start writing - that doesn't change EOF. EOF
35 * remains fixed as max written to. DODUCMNET THIS (for text and binary) - and provide a SetEOF() method
36 * (maybe just for seekable streams)? Maybe add rule that SetEOF () can only go backwards (shorten). Then call
37 * PullBackEOF() or RestrictEOF() or RemovePast(); OR ResetEOFToCurrentPosiiton(). Later maybe best API.
38 *
39 * @todo Consider/document approaches to timeouts. We COULD have a stream class where
40 * it was a PROPERTY OF THE CLASS (or alternate API) where writes timeout after
41 * a certain point.
42 *
43 */
44
46 class String;
47}
48namespace Stroika::Foundation::Memory {
49 class BLOB;
50}
51
52namespace Stroika::Foundation::Streams::OutputStream {
53
54 using Characters::Character;
55 using Characters::String;
56
57 template <typename ELEMENT_TYPE>
58 class IRep;
59
60 /**
61 * \em Design Overview
62 *
63 * o @See Stream
64 * o @See OutputStream::Ptr<ELEMENT_TYPE>
65 *
66 * o InputStream::Ptr and OutputStream::Ptr may logically be mixed together to make an
67 * input/output stream: @see InputOutputStream::Ptr<ELEMENT_TYPE>
68 *
69 * o One (potential) slight design flaw with this API, is that its not possible to have legal partial writes.
70 * But not supporting partial writes makes use much simpler (since callers don't need
71 * to worry about that case), and its practically never useful. In principle - this API could be
72 * extended so that an exception (or extra method to ask about last write) could include information
73 * about partial writes, but for now - I don't see any reason.
74 *
75 * Note - when you Seek() away from the end of an output stream, and then write, you automatically
76 * extend the stream to the point seeked to, and if you seek back (less) than the end and write, this overwrites
77 * instead of inserting.
78 *
79 * Note - Write is sufficient to guarantee the data is written, but it may be buffered until you call
80 * the destructor on the OutputStream (last reference goes away) or until you call Flush ().
81 *
82 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety-For-Envelope-But-Ambiguous-Thread-Safety-For-Letter">C++-Standard-Thread-Safety-For-Envelope-But-Ambiguous-Thread-Safety-For-Letter</a>
83 *
84 * \brief OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
85 *
86 * \note Since OutputStream::Ptr<ELEMENT_TYPE> is a smart pointer, the constness of the methods depends on whether they modify the smart pointer itself, not
87 * the underlying thread object.
88 *
89 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety-For-Envelope-But-Ambiguous-Thread-Safety-For-Letter">C++-Standard-Thread-Safety-For-Envelope-But-Ambiguous-Thread-Safety-For-Letter/a>
90 */
91 template <typename ELEMENT_TYPE>
92 class Ptr : public Streams::Ptr<ELEMENT_TYPE> {
93 private:
94 using inherited = typename Streams::Ptr<ELEMENT_TYPE>;
95
96 public:
97 /**
98 * defaults to null (empty ())
99 */
100 Ptr () = default;
101 Ptr (nullptr_t);
102 Ptr (const Ptr&) = default;
103 Ptr (Ptr&&) = default;
104 Ptr (const shared_ptr<IRep<ELEMENT_TYPE>>& rep);
105
106 public:
107 /**
108 */
109 nonvirtual Ptr& operator= (const Ptr&) = default;
110 nonvirtual Ptr& operator= (Ptr&&) = default;
111
112 public:
113 /**
114 * GetOffset () returns the currently seeked offset. This is the same as Seek (eFromCurrent, 0), except that the later
115 * requires IsSeekable ().
116 *
117 * \pre IsOpen ()
118 */
119 nonvirtual SeekOffsetType GetOffset () const;
120
121 public:
122 /**
123 * The new position, measured in bytes, is obtained by adding offset bytes to the position
124 * specified by whence.
125 *
126 * Seek () past the end of stream is NOT legal (may reconsider).
127 *
128 * Seek () returns the new resulting position (measured from the start of the stream - same as GetOffset).
129 *
130 * \pre IsOpen ()
131 */
132 nonvirtual SeekOffsetType Seek (SeekOffsetType offset) const;
133 nonvirtual SeekOffsetType Seek (Whence whence, SignedSeekOffsetType offset) const;
134
135 public:
136 /**
137 * Write the given span of elements to the output stream.
138 *
139 * If ELEMENT_TYPE==byte
140 * then the argument may also be of type uint8_t
141 * if ELEMENT_TYPE=Character
142 * then the argument may also be of type String or something convertible to String (char8_t*, string_view, etc)
143 *
144 * Writes always succeed fully or throw (no partial writes).
145 *
146 * \note The meaning of Write () depends on the exact type of Stream you are referencing. The data
147 * may still be buffered. Call @Flush () to get it pushed out.
148 *
149 * \note Span argument is really a span<const ELEMENT_TYPE> but must declare this way to it matches non-const
150 * arguments as well - one area where either spans suck, or I'm not using/understanding properly.
151 *
152 * \pre IsOpen ()
153 */
154 template <typename ELEMENT_TYPE2, size_t EXTENT_2>
155 nonvirtual void Write (span<ELEMENT_TYPE2, EXTENT_2> elts) const
156 requires (same_as<ELEMENT_TYPE, remove_cvref_t<ELEMENT_TYPE2>> or
157 (same_as<ELEMENT_TYPE, byte> and (same_as<remove_cvref_t<ELEMENT_TYPE2>, uint8_t>)) or
158 (same_as<ELEMENT_TYPE, Character> and (Characters::IUNICODECanUnambiguouslyConvertFrom<remove_cvref_t<ELEMENT_TYPE2>>)));
159 nonvirtual void Write (const ELEMENT_TYPE& e) const;
160 nonvirtual void Write (const Memory::BLOB& blob) const
161 requires (same_as<ELEMENT_TYPE, byte>);
162 nonvirtual void Write (const String& s) const
163 requires (same_as<ELEMENT_TYPE, Character>);
164 template <Characters::IUNICODECanUnambiguouslyConvertFrom CHAR_T>
165 nonvirtual void Write (const CHAR_T* cStr) const
166 requires (same_as<ELEMENT_TYPE, Character>);
167
168 public:
169 /**
170 * For Character output streams only - do a Write () with the given argument, followed by a Characters::kEOL.
171 */
172 template <typename ELT_2_WRITE>
173 nonvirtual void WriteLn (ELT_2_WRITE&& arg) const
174 requires (same_as<ELEMENT_TYPE, Character>);
175
176 public:
177 /**
178 * \pre IsOpen ()
179 */
180 nonvirtual void PrintF (const wchar_t* format, ...)
181 requires (same_as<ELEMENT_TYPE, Character>);
182
183 public:
184 /**
185 * Put the output stream in a state where it cannot be written to anymore.
186 * If argument 'reset' is true, this also clears the smart pointer (calls Stream<>::reset()).
187 *
188 * It is generally unneeded to ever call Close () - as streams are closed automatically when the final
189 * reference to them is released (shared_ptr).
190 *
191 * But - this can be handy - in that it allows for exception handling. Exceptions closing out an output stream - doing
192 * final writes - cannot be reported if done by destroying objects (cannot throw from dtor) - so Close () assures
193 * a clean shutdown with exceptions being propagated during the cleanup.
194 *
195 * \note Most calls on an OutputStream after it is closed are illegal, and result in Require () errors. It is up
196 * to the caller/user of the shared output streams to assure they don't use them after being closed. Note - if that
197 * sounds hard its not: it naturally falls out of normal usage.
198 *
199 * The rationale is just that I can think of no useful case where a caller might want to allow writing after close, and
200 * have that translated into an exception, and this choice is more performant (and could be reversed more easily
201 * than the opposite policy if I change my mind).
202 *
203 * \note Close () - and IsOpen () are intentionally duplicated in InputStream () and OutputStream () classes. This is so
204 * you can close down the OutputStream side of an InputOutputStream, and leave open the InputStream side - so it sees EOF.
205 *
206 * \note When a subtype stream (like BufferedOutputStream) aggregates another stream, it is left to that subclass
207 * whether or not closing the top level stream also Closes the sub-stream. Typically, if the class designer intends
208 * you to think of the ownership as 'aggregation' - close of this stream will close any aggregated streams, but
209 * if this class is designed to 'reference' other streams, it will not close them.
210 *
211 * See the stream documentation for that stream class to see how it handles Close.
212 *
213 * \pre IsOpen ()
214 */
215 nonvirtual void Close () const;
216 nonvirtual void Close (bool reset);
217
218 public:
219 /**
220 * Return true, unless a call to Close () has been done on the underlying stream (not just Ptr).
221 *
222 * \note InputStream and OutputStream (when mixed in InputOutputStream) have separate IsOpen/IsClosed flags, so you
223 * can call Close on the write side of the stream and still read from the InputStream side.
224 *
225 * @see Close ()
226 */
227 nonvirtual bool IsOpen () const;
228
229 public:
230 /**
231 * \brief forces any data contained in this stream to be written.
232 *
233 * Forces any data contained in this stream to be written.
234 *
235 * For some streams (such as buffered streams, sockets, and OpenSSLCryptStream) - they may not
236 * finish their writes until they are destroyed. The trouble then - is that they cannot
237 * propagate exceptions! Calling Flush() before destroying the output stream allows exceptions
238 * to be propagated properly.
239 *
240 * \pre IsOpen ()
241 */
242 nonvirtual void Flush () const;
243
244 public:
245 /**
246 * EXPERIEMNTAL API
247 * done as template so third parties can externally extend, and have overloading work right..
248 * @todo need overloads for basic types, std::string, int, float, etc...
249 * But don't do except for string for now. Don't make same mistake as iostream - with formatting. Not clear how todo
250 * right so don't dig a hole and do it wrong (yet).
251 *
252 * \pre IsOpen ()
253 */
254 template <typename T>
255 const typename OutputStream::Ptr<ELEMENT_TYPE>& operator<< (const T& write2TextStream) const
256 requires (same_as<ELEMENT_TYPE, Characters::Character>);
257
258 public:
259 /**
260 * \brief protected access to underlying stream smart pointer
261 */
262 nonvirtual shared_ptr<IRep<ELEMENT_TYPE>> GetSharedRep () const;
263
264 public:
265 /**
266 * \pre *this != nullptr
267 */
268 nonvirtual const IRep<ELEMENT_TYPE>& GetRepConstRef () const;
269
270 public:
271 /**
272 * \pre *this != nullptr
273 */
274 nonvirtual IRep<ELEMENT_TYPE>& GetRepRWRef () const;
275
276 public:
277 [[deprecated ("Since Strokka v3.0d5 deprecated since not widely used and very specific purpose and directly implementingable given "
278 "apis")]] SeekOffsetType
279 GetOffsetToEndOfStream () const
280 {
281 SeekOffsetType savedReadFrom = GetOffset ();
282 SeekOffsetType size = Seek (eFromEnd, 0);
283 Seek (Whence::eFromStart, savedReadFrom);
284 size -= savedReadFrom;
285 return size;
286 }
287 [[deprecated ("Since Stroika v3.0d5 use span overload of Write")]] void Write (const ELEMENT_TYPE* start, const ELEMENT_TYPE* end) const
288 {
289 Write (span{start, end});
290 }
291 [[deprecated ("Since Stroika v3.0d5 use span overload")]] void Write (const uint8_t* start, const uint8_t* end) const
292 requires (same_as<ELEMENT_TYPE, byte>)
293 {
294 this->Write (span{start, end});
295 }
296 template <typename POD_TYPE>
297 [[deprecated ("Since Stroika v3.0d5 use span overload of WriteRaw")]] void WriteRaw (const POD_TYPE* start, const POD_TYPE* end) const
298 requires (same_as<ELEMENT_TYPE, byte> and is_standard_layout_v<POD_TYPE>)
299 {
300 WriteRaw (span{start, end});
301 }
302 [[deprecated ("Since Stroika v3.0d5 use span overload of Write")]] void Write (const wchar_t* start, const wchar_t* end) const
303 requires (same_as<ELEMENT_TYPE, Characters::Character>)
304 {
305 this->Write (span{start, end});
306 }
307 template <typename POD_TYPE>
308 [[deprecated ("Since Stroika v3.0d8 - use Write (as_bytes (span{&p, 1}));")]] void WriteRaw (const POD_TYPE& p) const
309 requires (same_as<ELEMENT_TYPE, byte> and is_trivial_v<POD_TYPE> and not Memory::ISpan<POD_TYPE>)
310 {
311 this->Write (as_bytes (span{&p, 1}));
312 }
313 template <typename POD_TYPE, size_t SPAN_LENGTH>
314 [[deprecated ("Since Stroika v3.0d8 - use Write(as_bytes(p))")]] void WriteRaw (span<POD_TYPE, SPAN_LENGTH> elts) const
315 requires (same_as<ELEMENT_TYPE, byte> and is_trivial_v<POD_TYPE>)
316 {
317 this->Write (as_bytes (elts));
318 }
319 };
320
321 /**
322 * \brief Abstract interface for output stream object. Don't call directly (use Ptr usually) - but use directly mostly to implement new output stream types.
323 *
324 * \note \em Thread-Safety <a href="Thread-Safety.md#Thread-Safety-Rules-Depends-On-Subtype">Thread-Safety-Rules-Depends-On-Subtype/a>
325 */
326 template <typename ELEMENT_TYPE>
327 class IRep : public Streams::IRep<ELEMENT_TYPE> {
328 public:
329 using ElementType = ELEMENT_TYPE;
330
331 public:
332 IRep () = default;
333 IRep (const IRep&) = delete;
334
335 public:
336 virtual ~IRep () = default;
337
338 public:
339 nonvirtual IRep& operator= (const IRep&) = delete;
340
341 public:
342 /**
343 * May (but typically not) called before destruction. If called, \pre no other write or seek etc operations.
344 *
345 * \note - 'Require (IsOpen()) automatically checked in Ptr wrappers for things like Write, so subclassers don't need to
346 * do that in implementing reps, but probably good docs/style todo in both places.'
347 *
348 * \note - CloseWrite must implicitly Flush ()
349 */
350 virtual void CloseWrite () = 0;
351
352 public:
353 /**
354 * return true iff CloseWrite () has not been called (cannot construct closed stream)
355 */
356 virtual bool IsOpenWrite () const = 0;
357
358 public:
359 virtual SeekOffsetType GetWriteOffset () const = 0;
360
361 public:
362 virtual SeekOffsetType SeekWrite (Whence whence, SignedSeekOffsetType offset) = 0;
363
364 public:
365 /**
366 * Pointer must refer to valid memory at least bufSize long, and cannot be nullptr.
367 * BufSize must always be >= 1.
368 * Writes always succeed fully or throw (no partial writes so no return value of amount written).
369 *
370 * \note The meaning of Write () depends on the exact type of Stream you are referencing. The data
371 * may still be buffered. Call @Flush () to ensure it pushed out.
372 *
373 * \note This can block indefinitely (for example in writing to a UNIX Pipe) - when output buffers fill.
374 */
375 virtual void Write (span<const ELEMENT_TYPE> elts) = 0;
376
377 public:
378 /**
379 *
380 */
381 virtual void Flush () = 0;
382 };
383
384}
385
386/*
387 ********************************************************************************
388 ***************************** Implementation Details ***************************
389 ********************************************************************************
390 */
391#include "OutputStream.inl"
392
393#endif /*_Stroika_Foundation_Streams_OutputStream_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
Abstract interface for output stream object. Don't call directly (use Ptr usually) - but use directly...
virtual void Write(span< const ELEMENT_TYPE > elts)=0
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
nonvirtual shared_ptr< IRep< ELEMENT_TYPE > > GetSharedRep() const
protected access to underlying stream smart pointer
nonvirtual IRep< ELEMENT_TYPE > & GetRepRWRef() const
nonvirtual SeekOffsetType Seek(SeekOffsetType offset) const
nonvirtual SeekOffsetType GetOffset() const
nonvirtual void Write(span< ELEMENT_TYPE2, EXTENT_2 > elts) const
nonvirtual const IRep< ELEMENT_TYPE > & GetRepConstRef() const
nonvirtual void WriteLn(ELT_2_WRITE &&arg) const
nonvirtual void PrintF(const wchar_t *format,...)
nonvirtual void Flush() const
forces any data contained in this stream to be written.
A Streams::Ptr<ELEMENT_TYPE> is a smart-pointer to a stream of elements of type T.
Definition Stream.h:170
nonvirtual void reset() noexcept
Definition Stream.inl:50