Here is minimal sample code for creating a blob that owns a
connection to a database. It has a single field (connection
)
and defines compare_fields() and write_fields().
A second sample code shows how to wrap a system pointer - section 1.6.8.7
struct MyConnection { std::string name; explicit MyConnection(); explicit MyConnection(const std::string& _name); bool open(); bool close() noexcept; void portray(PlStream& strm) const; }; struct MyBlob; static PL_blob_t my_blob = PL_BLOB_DEFINITION(MyBlob, "my_blob"); struct MyBlob : public PlBlob { std::unique_ptr<MyConnection> connection; explicit MyBlob() : PlBlob(&my_blob) { } explicit MyBlob(const std::string& connection_name) : PlBlob(&my_blob), connection(std::make_unique<MyConnection>(connection_name)) { if ( !connection->open() ) throw MyBlobError("my_blob_open_error"); } PL_BLOB_SIZE ~MyBlob() noexcept { if ( !close() ) Sdprintf("***ERROR: Close MyBlob failed: %s\n", name().c_str()); // Can't use PL_warning() } inline std::string name() const { return connection ? connection->name : ""; } bool close() noexcept { if ( !connection ) return true; bool rc = connection->close(); connection.reset(); // Can be omitted, leaving deletion to ~MyBlob() return rc; } PlException MyBlobError(const char* error) const { return PlGeneralError(PlCompound(error, PlTermv(symbol_term()))); } int compare_fields(const PlBlob* _b_data) const override { auto b_data = static_cast<const MyBlob*>(_b_data); // See note about cast return name().compare(b_data->name()); } bool write_fields(IOSTREAM *s, int flags) const override { PlStream strm(s); strm.printf(","); return write_fields_only(strm); } bool write_fields_only(PlStream& strm) const { if ( connection ) connection->portray(strm); else strm.printf("closed"); return true; } bool portray(PlStream& strm) const { strm.printf("MyBlob("); write_fields_only(strm); strm.printf(")"); return true; } }; // %! create_my_blob(+Name: atom, -MyBlob) is semidet. PREDICATE(create_my_blob, 2) { // Allocating the blob uses std::unique_ptr<MyBlob> so that it'll be // deleted if an error happens - the auto-deletion is disabled by // ref.release() inside unify_blob() before returning success. auto ref = std::unique_ptr<PlBlob>(new MyBlob(A1.as_atom().as_string())); return A2.unify_blob(&ref); } // %! close_my_blob(+MyBlob) is det. // % Close the connection, silently succeeding if is already // % closed; throw an exception if something goes wrong. PREDICATE(close_my_blob, 1) { auto ref = PlBlobV<MyBlob>::cast_ex(A1, my_blob); if ( !ref->close() ) throw ref->MyBlobError("my_blob_close_error"); return true; } // %! portray_my_blob(+Stream, +MyBlob) is det. // % Hook predicate for // % user:portray(MyBlob) :- // % blob(MyBlob, my_blob), !, // % portray_my_blob(current_output, MyBlob). PREDICATE(portray_my_blob, 2) { auto ref = PlBlobV<MyBlob>::cast_ex(A2, my_blob); PlStream strm(A1, 0); return ref->portray(strm); }