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);
}