| Did you know ... | Search Documentation: |
| 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 matching PL_release_stream() is done, and
also handles some subtle problems with C++ exceptions.
The methods are:
PlStream
object to an invalid stream (see PlStream::check_stream()).IOSTREAM*, PlStream
is implicitly converted to IOSTREAM*.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(). PlStream::seek() and PlStream::tell()
call
Sseek64() and Stell64() instead of long (they
are also deprecated: PlStream::seek64() and PlStream::tell64()
are preferred).
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();
}