Component of the Week #35: bdls_fdstreambuf
- Summary:
Provides a
bsl::streambufthat can be initialized with a file descriptor.Supports both text and binary modes, with platform-appropriate line ending handling.
Can optionally take ownership of the file descriptor for automatic closing on destruction.
The bdls_fdstreambuf
component provides a class, bdls::FdStreamBuf, derived from the C++
standard library’s bsl::streambuf that can be associated with a file
descriptor, and then a bsl::stream can be created from the `bsl::streambuf, which allows the client to use C++ stream I/O operations with file
descriptors obtained from system calls or from bdls::FilesystemUtil. The file descriptor can represent a file, pipe, or other device.
Key Features
File descriptor integration: Works with file descriptors from
bdls::FilesystemUtilor system callsStream interface: Provides familiar C++ stream operations through
bsl::streambufinterfaceOwnership management: Optional ownership of file descriptors for automatic closing on destruction
Platform-aware text mode: Handles line ending translation on Windows (
\r\n) vs Unix (\n)Binary mode support: Raw byte access without line ending translation
Seeking operations: Supports random access within the file
Efficient I/O: Direct operations like
sputnandsgetnfor bulk data transfer
Common Use Cases
bdls::FdStreamBuf is particularly useful for:
Allowing a file, socket, or pipe to be dealt with as a C++ stream by creating a bdls::FdStreamBuf out of a file descriptor, then creating an std::stream initialized with the FdStreamBuf.
Cross-platform file I/O with appropriate text/binary handling
Performance-critical applications requiring direct stream buffer operations
Basic Usage
The most common usage is to initialize a stream with a bdls::FdStreamBuf
for either input or output:
#include <bdls_fdstreambuf.h>
#include <bdls_filesystemutil.h>
#include <bsl_iostream.h>
#include <bsl_fstream.h>
using namespace BloombergLP;
using bsl::cout;
using bsl::cerr;
using bsl::endl;
int main() {
const char* filename = "/tmp/example.txt";
// Create and open a file descriptor
typedef bdls::FilesystemUtil::FileDescriptor FdType;
FdType fd = bdls::FilesystemUtil::open(
filename,
bdls::FilesystemUtil::e_CREATE,
bdls::FilesystemUtil::e_READ_WRITE);
if (bdls::FilesystemUtil::k_INVALID_FD == fd) {
cerr << "Failed to open file" << endl;
return -1;
}
// Write to the file using a stream buffer
{
bdls::FdStreamBuf streamBuffer(fd,
true, // writable
false); // don't close fd on
// destruction
bsl::ostream os(&streamBuffer);
os << "Hello, World!" << endl;
os << "Line 2: " << 42 << endl;
}
// Read back from the file
{
bdls::FdStreamBuf streamBuffer(fd,
false, // not writable
true); // close fd on
// destruction
streamBuffer.pubseekpos(0); // seek to beginning
bsl::istream is(&streamBuffer);
bsl::string line;
while (bsl::getline(is, line)) {
cout << "Read: " << line << endl;
}
}
// fd is automatically closed when the last streamBuffer is destroyed
bdls::FilesystemUtil::remove(filename);
return 0;
}
Direct Stream Buffer Operations
You can also use the stream buffer’s public methods directly for more precise control over I/O operations:
#include <bdls_fdstreambuf.h>
#include <bdls_filesystemutil.h>
#include <bsl_iostream.h>
#include <bsl_string_view.h>
#include <assert.h>
using namespace BloombergLP;
using bsl::cout;
using bsl::endl;
int main() {
const char* filename = "/tmp/direct_io.txt";
const bsl::string_view line1("First line of text\n");
const bsl::string_view line2("Second line of text\n");
const bsl::string_view line3("Third line of text\n");
// Open file descriptor
typedef bdls::FilesystemUtil::FileDescriptor FdType;
FdType fd = bdls::FilesystemUtil::open(
filename,
bdls::FilesystemUtil::e_CREATE,
bdls::FilesystemUtil::e_READ_WRITE);
bdls::FdStreamBuf streamBuffer(fd, true); // writable, owns fd
// Write data using sputn
streamBuffer.sputn(line1.data(), line1.length());
streamBuffer.sputn(line2.data(), line2.length());
streamBuffer.sputn(line3.data(), line3.length());
// Seek back to the beginning
bsl::streamoff pos = streamBuffer.pubseekpos(0);
assert(0 == pos);
// Read specific amounts of data
char buffer[100];
bsl::streamsize bytesRead = streamBuffer.sgetn(buffer,
line1.length());
const bsl::string_view read1(buffer, bytesRead);
assert(read1 == line1);
cout << "Read: " << read1;
// Read the rest of the file
bsl::memset(buffer, 0, sizeof(buffer));
bytesRead = streamBuffer.sgetn(buffer, sizeof(buffer) - 1);
const bsl::string_view remaining(buffer, bytesRead);
cout << "Remaining: " << remaining;
streamBuffer.clear(); // closes fd and disconnects
bdls::FilesystemUtil::remove(filename);
return 0;
}
The above example produces the output:
Read: First line of text
Remaining: Second line of text
Third line of text
Text vs Binary Mode
The stream buffer supports both text and binary modes, which affects how line endings are handled on different platforms:
#include <bdls_fdstreambuf.h>
#include <bdls_filesystemutil.h>
#include <bsl_algorithm.h>
#include <bsl_iostream.h>
#include <assert.h>
using namespace BloombergLP;
using bsl::cout;
using bsl::endl;
int main() {
const char* filename = "/tmp/mode_test.txt";
typedef bdls::FilesystemUtil::FileDescriptor FdType;
FdType fd = bdls::FilesystemUtil::open(
filename,
bdls::FilesystemUtil::e_CREATE,
bdls::FilesystemUtil::e_READ_WRITE);
// Write in text mode (default on Windows, binary on Unix)
{
bdls::FdStreamBuf streamBuffer(fd,
true, // writable
false, // don't close fd
false); // non-binary CRLF mode
// on Windows, this
// argument ignored on
// Unix
bsl::ostream os(&streamBuffer);
os << "Line with newline" << endl;
}
// Read back in binary mode to see exact bytes written, end of line
// will be "\r\n" on Windows, just "\n" on Unix.
{
bdls::FdStreamBuf streamBuffer(fd,
false, // not writable
false, // don't close fd
true); // binary mode
streamBuffer.pubseekpos(0);
char buffer[100]; char *end = buffer + sizeof(buffer);
bsl::istream is(&streamBuffer);
char *pc = buffer;
while (pc < end && is.get(*pc) && *pc != '\n') {
++pc;
}
assert(pc < end && '\n' == *pc);
cout << "Binary read: ";
for (char *p = buffer; p <= pc; ++p) {
if (*p == '\r') {
cout << "\\r";
} else if (*p == '\n') {
cout << "\\n";
} else {
cout << *p;
}
}
cout << endl;
}
bdls::FilesystemUtil::close(fd);
bdls::FilesystemUtil::remove(filename);
return 0;
}
// Output on Unix: "Line with newline\n"
// Output on Windows: "Line with newline\r\n"
- For more details, see: