2.5.8.2 Stream I/O
PlStream
can be used to get a stream from a Prolog term,
or to lock the stream so that other threads cannot interleave their
output. With either usage, PlStream
is a RAII
class that ensure the matchin PL_release_stream() is done, and
also handles some subtle problems with C++ exceptions.
The methods are:
-
- PlStream :: PlStream(term_t t, int flags)
- - see PL_get_stream() for documentation of the flags. Throws a C++ exception on error.
- PlStream :: PlStream(IOSTREAM *s)
- - calls PL_acquire_stream() to lock the stream. Throws a C++ exception on error.
- ~ PlStream()
- - calls PlStream::release(). See below for caveats if there are exceptions.
- void PlStream::release()
- calls PL_release_stream(), throwing an exception if there has been an I/O error on the stream, and sets the
PlStream
object to an invalid stream (see PlStream::check_stream()).- IOSTREAM* ::operator PlStream(void)
- - when used in a context that requires an
IOSTREAM*
,PlStream
is implicitly converted toIOSTREAM*
.- cfunctionvoidcheck_stream checks that the
PlStream
object contains a valid stream and throws an exception if it doesn't. This is used to ensure that PlStream::release() hasn't been called.- Most of the stream I/O functions have corresponding methods in
PlStream
. For example, Sfprintf() corresponds to PlStream::printf().
The C interface to stream I/O doesn't raise a Prolog error when
there's a stream error (typically indicated by a -1 return code).
Instead, the error sets a flag on the stream and
PL_release_stream() creates the error term. The
PlStream
destructor calls PL_release_stream(); but
it's a fatal error in C++ to raise an exception in a destructor if the
destructor is invoked by stack-unwinding due to another exception,
including the pseudo-exceptions PlFail
and
PlExceptionFail
.
To get around this, the various stream I/O functions have wrapper
methods in the PlStream
class that check for an error and
call PlStream::release()
to create the Prolog error, which is thrown as a C++ error.
The destructor calls PlStream::release(), which throws a C++ exception if there is a stream error. This is outside the destructor, so it is safe - the destructor checks if the stream has been released and does nothing in that situation.
The following two code examples do essentially the same thing:
PREDICATE(name_arity, 1) { PlStream strm(Scurrent_output); strm.printf("name = %s, arity = %zd\n", A1.name().as_string().c_str(), A1.arity()); return true; }
PREDICATE(name_arity, 1) { PlStream strm(Scurrent_output); try { strm.printf("name = %s, arity = %zd\n", A1.name().as_string().c_str(), A1.arity()); } PREDICATE_CATCH({strm.release(); return false;}) return true; }
If you write the code as follows, using Sfprintf() directly, it is possible that a fatal exception will be raised on an I/O error:
PREDICATE(name_arity, 1) { PlStream strm(Scurrent_output); Sfprintf(strm, "name = %s, arity = %zd\n", A1.name().as_string().c_str(), A1.arity()); return true; // WARNING: the PlStream destructor might throw a C++ // exception on stack unwinding, giving a fatal // fatal runtime exception. }
If you don't use these, and want to throw an exception if there's an
error, the following code works because PlStream
(and the
underlying PL_acquire_stream()) can be called recursively:
{ PlStream strm(...); strm.release(); }