struct MyFileBlob;
static PL_blob_t my_file_blob = PL_BLOB_DEFINITION(MyFileBlob, "my_file_blob");
static const PlOptionsFlag<int>
MyFileBlob_options("MyFileBlob-options",
{ {"absolute", PL_FILE_ABSOLUTE},
{"ospath", PL_FILE_OSPATH},
{"search", PL_FILE_SEARCH},
{"exist", PL_FILE_EXIST},
{"read", PL_FILE_READ},
{"write", PL_FILE_WRITE},
{"execute", PL_FILE_EXECUTE},
{"noerrors", PL_FILE_NOERRORS} });
struct MyFileBlob : public PlBlob
{ std::FILE* file_;
std::string mode_;
int flags_;
std::string filename_;
std::vector<char> buffer_; // used by read(), to avoid re-allocation
explicit MyFileBlob()
: PlBlob(&my_file_blob) { }
explicit MyFileBlob(PlTerm filename, PlTerm mode, PlTerm flags)
: PlBlob(&my_file_blob),
mode_(mode.as_string())
{ flags_ = MyFileBlob_options.lookup_list(flags);
filename_ = filename.get_file_name(flags_);
file_ = fopen(filename_.c_str(), mode_.c_str());
if ( !file_ ) // TODO: get error code (might not be existence error)
throw PlExistenceError("my_file_blob_open", PlTerm_string(filename_));
// for debugging:
// PlTerm_string(filename.as_string() + "\" => \"" +
// filename_ + "\", \"" + mode_ +
// ", flags=" + MyFileBlob_options.as_string(flags_) + "\")")
}
PL_BLOB_SIZE
std::string read(size_t count)
{ assert(sizeof buffer_[0] == sizeof (char));
assert(sizeof (char) == 1);
buffer_.reserve(count);
return std::string(buffer_.data(),
std::fread(buffer_.data(), sizeof buffer_[0], count, file_));
}
bool eof() const
{ return std::feof(file_);
}
bool error() const
{ return std::ferror(file_);
}
virtual ~MyFileBlob() noexcept
{ if ( !close() )
// Can't use PL_warning()
Sdprintf("***ERROR: Close MyFileBlob failed: (%s)\n", filename_.c_str());
}
bool close() noexcept
{ if ( !file_ )
return true;
int rc = std::fclose(file_);
file_ = nullptr;
return rc == 0;
}
PlException MyFileBlobError(const std::string error) const
{ return PlGeneralError(PlCompound(error, PlTermv(symbol_term())));
}
int compare_fields(const PlBlob* _b_data) const override
{ // dynamic_cast is safer than static_cast, but slower (see documentation)
// It's used here for testing (the documentation has static_cast)
auto b_data = dynamic_cast<const MyFileBlob*>(_b_data);
return filename_.compare(b_data->filename_);
}
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
{ // For debugging:
// strm.printf("%s mode=%s flags=%s", filename_.c_str(), mode_.c_str(),
// MyFileBlob_options.as_string(flags_).c_str());
strm.printf("%s", filename_.c_str());
if ( !file_ )
strm.printf("-CLOSED");
return true;
}
bool portray(PlStream& strm) const
{ strm.printf("MyFileBlob(");
write_fields_only(strm);
strm.printf(")");
return true;
}
};
PREDICATE(my_file_open, 4)
{ auto ref = std::unique_ptr<PlBlob>(new MyFileBlob(A2, A3, A4));
return A1.unify_blob(&ref);
}
PREDICATE(my_file_close, 1)
{ auto ref = PlBlobV<MyFileBlob>::cast_ex(A1, my_file_blob);
if ( !ref->close() ) // TODO: get the error code
throw ref->MyFileBlobError("my_file_blob_close_error");
return true;
}