• Places
    • Home
    • Graphs
    • Prefixes
  • Admin
    • Users
    • Settings
    • Plugins
    • Statistics
  • CPACK
    • Home
    • List packs
    • Submit pack
  • Repository
    • Load local file
    • Load from HTTP
    • Load from library
    • Remove triples
    • Clear repository
  • Query
    • YASGUI SPARQL Editor
    • Simple Form
    • SWISH Prolog shell
  • Help
    • Documentation
    • Tutorial
    • Roadmap
    • HTTP Services
  • Login

1.6.9 Limitations of the interface
All Application Manual Name SummaryHelp

  • Documentation
    • Reference manual
    • Packages
      • A C++ interface to SWI-Prolog
        • A C++ interface to SWI-Prolog
          • Overview
            • Limitations of the interface
              • Strings
              • Stream I/O
              • Object handles

1.6.9.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 to IOSTREAM*.
void check_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(). 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();
}

ClioPatria (version V3.1.1-51-ga0b30a5)