FBB::SharedStream(3bobcat)
π FBB::SharedStream β I/O on shared memory π
okay bestie, welcome to the bobcat manual, section 3 π this whole page is about a little class called FBB::SharedStream, and it's literally giving "iostream but the file lives in shared memory" energy. no cap.
β¨ NAME β¨
FBB::SharedStream β I/O operations on shared memory π«Ά
πΈ SYNOPSIS πΈ
#include <bobcat/sharedstream>
Linking option: -lpthread -lbobcat
yes bestie, you link both of those, don't skip -lpthread, iykyk π
π¦ DESCRIPTION π¦
This class is the ultimate mashup: it combines the features of the std::istream and std::ostream classes, and it operates on shared memory. And just like with std::fstream objects, you need a seekp or seekg member call to switch from writing to reading, or the other way around. it's giving "one lane road, no overtaking" π
Also, same as with std::fstream objects, FBB::SharedStream objects do not keep separate offsets for reading and writing. The seek-members always refer to the (single) offset maintained by the FBB::SharedMemory object that the SharedStream object interfaces to. one offset to rule them all π
So even though tellg and tellp return identical values, you should not call tellg after writing to a SharedStream object, and you should not call tellp after reading from one. Because calling reading-related members (tellg) after writing and vice versa puts the stream in its fail state. bestie this is not a vibe, please respect the rule π
π NAMESPACE π
FBB
Every single constructor, member, operator and manipulator mentioned in this man-page lives in the namespace FBB. periodt.
β INHERITS FROM β
FBB::SharedBuf(private inheritance),std::istream,std::ostream,FBB::SharedEnum__(cf.sharedmemory(3bobcat)for a description of this base class).
look at all those ancestors, main character energy fr fr π
π SIZEUNIT ENUMERATION π
The enum SizeUnit defines these symbolic constants:
kB, representing 1024 (2**10) bytes of memory;MB, representing 1048576 (2**20bytes of memory;GB, representing 1073741824 (2**30) bytes of memory )
ngl the original page is a little unhinged with those parentheses and I'm keeping them exactly as they are, because the facts do not change π
π CONSTRUCTORS π
SharedStream()β The default constructor defines a stubSharedStreamobject that cannot immediately be used to access shared memory. To use it, its memberopenmust first be called. it's giving "placeholder bestie, come back later" π¦SharedStream(size_t maxSize, SizeUnit sizeUnit, std::ios::openmode openMode = std::ios::in | std::ios::out, size_t access = 0600)β This constructor creates a stream inheriting the facilities of anstd::istreamandstd::ostreamthat interfaces to a shared memory segment having a capacity of at leastmaxSize * sizeUnitbytes. πΈ
By default, the shared memory segment is opened for reading and writing. Different from the open modes used for file streams, creating a shared memory stream with open modes ios::in | ios::out is OK. In this case the shared memory segment is created and once information has been written to the shared memory it can also be read again. so yes, you can have both, slay β¨
The shared memory's access rights are defined by the access parameter, interpreted as an octal value, using the well-known (chmod(1)) way to define the access rights for owner, group and others.
If construction fails, an FBB::Exception is thrown. π
SharedStream(int id, std::ios::openmode openMode = std::ios::in | std::ios::out)β This constructor creates a stream inheriting the facilities of anstd::istreamandstd::ostreamthat connects to a shared memory segment having IDid.
Specifying the ios::trunc flag immediately clears the content of the shared memory. π§Ή
An FBB::Exception is thrown if construction fails (e.g., no shared memory segment having ID id exists),
Copy and move constructors (and assignment operators) are not available. not me trying to copy it, nope, blocked π
π MEMBER FUNCTIONS π
All members of std::istream and std::ostream and the enum values kB, MB, and GB, defined by FBB::SharedEnum__ are available. yes bestie, you get the whole inheritance package π
FBB::SharedCondition attachSharedCondition(std::ios::off_type offset, std::ios::seekdir origin)β Returns anFBB::SharedCondition(3)object, interfacing to a shared condition variable located atoffsetoffset(relative toorigin) in theSharedMemoryobject to which theSharedStreamobject interfaces. This member does not alter the value returned by the stream'stellgandtellpmembers. π βοΈ
An FBB::Exception is thrown if the FBB::SharedCondition object could not be constructed. π
FBB::SharedCondition createSharedCondition()β Returns anFBB::SharedCondition(3)object, interfacing to a newly created shared condition variable which is created at the current offset of theSharedMemoryobject to which theSharedStreamobject interfaces (or at the first offset of the next physical shared memory data block, cf.sharedcondition(3bobcat)). Creating aSharedConditionobject does not alter the value returned by the stream'stellgandtellpmembers. π
An FBB::Exception is thrown if the FBB::SharedCondition object could not be constructed. π
int id() constβ The ID of the shared memory segment is returned. πͺͺvoid kill()β Without locking the shared memory first, all shared memory is returned to the operating system. TheFBB::SharedStreamobject is unusable after returning fromkill. Other processes that were using the shared memory can continue to do so. π
bestie this one does NOT lock first, that's literally in the description, so be careful with it π₯
void memInfo(std::ostream &out, char const *end = "\n")β Information about theSharedMemoryobject is inserted into the providedostreamobject. The IDs of the shared segments, their sizes, the maximum number of shared memory segments, the number of bytes that can be read from the shared memory, and its actual storage capacity, etc., are displayed. Following the information about the shared memory,endis inserted intoout. πvoid open(size_t maxSize, SizeUnit sizeUnit, std::ios::openmode openMode = std::ios::in | std::ios::out, size_t access = 0600)β This member creates a shared memory segment having a capacity of at leastmaxSize * sizeUnitbytes, and connects the shared memory segment to theFBB::SharedStream. A matchingclosemember does not exist and is not required. π
By default, the shared memory segment is opened for reading and writing. Different from the open modes used for file streams, creating a shared memory stream with open modes ios::in | ios::out is OK. In this case the shared memory segment is created and once information has been written to the shared memory it can also be read again. β¨
The shared memory's access rights are defined by the access parameter, interpreted as an octal value, using the well-known (chmod(1)) way to define the access rights for owner, group and others.
If opening fails, an FBB::Exception is thrown. π
void open(int id, std::ios::openmode openMode = std::ios::in | std::ios::out)β This member connects theFBB::SharedStreamobject to a shared memory segment having IDid. A matchingclosemember does not exist and is not required. If opening succeeds the shared memory is ready for use. π«Ά
Specifying the ios::trunc flag immediately clears the content of the shared memory. π§Ή
An FBB::Exception is thrown if opening fails (e.g., no shared memory segment having ID id exists). π
void remove()β The shared memory is first locked. Next, all shared memory is returned to the operating system. TheFBB::SharedStreamobject is unusable after returning fromremove. Other processes that were using the shared memory can continue to do so. π
see how this one locks first and kill doesn't? yeah. that's the whole difference, don't mix them up π
bool truncate(std::streamsize offset)β Ifoffsetis not exceeding the value returned byseekg(0, std::ios::end), then this latter value is changed tooffsetandtrueis returned. Otherwisefalseis returned, and the value returned byseekgis not altered. If the value returned bytellgexceededoffset,tellg's return value it is reduced tooffsetas well. Subsequent read operations on the shared memory can only succeed as long astellg's return value hasn't reached the valueoffset. βοΈ
okay that's part 1 bestie, hold on, there's more coming π
π EXAMPLE π
okay bestie, THIS is the example program, and it is highkey a whole interactive playground for shared segments π it's giving "type a letter, get a segment" energy. here it is, exactly as it lives in the source:
#include <iostream>
#include <string>
#include <ostream>
#include <istream>
#include <bobcat/exception>
#include <bobcat/sharedstream>
#include <bobcat/sharedcondition>
using namespace std;
using namespace FBB;
int main()
{
SharedStream sharedStream;
int id = -1;
while (true)
{
cout <<
"\n"
" K kill (no lock) existing shared segment\n"
" S show stats of current shared segment\n"
" L <id> Load segment <id>\n"
" C Install a SharedCondition at offset 0\n"
" c create new shared memory (sets id)\n"
// " l lock segment id until key pressed\n"
// " p <x> c put char c at offset x\n"
" q quit\n"
// " r <x> <n> read n chars from offset x\n"
// " w <x> args write all args at offset x\n"
" i insert lines (until empty) at the current "
"offset\n"
" x extract lines (until EOF) from the current "
"offset\n"
" X extract the next line from the current "
"offset\n"
" when nodified via βNβ\n"
" N notify a waiting X\n"
" s <x> seek (abs) offset x\n"
"? ";
char ch;
cin >> ch;
ios::off_type offset;
cout << "Requested: " << ch << β\nβ;
sharedStream.clear();
switch (ch)
{
case βcβ:
{
sharedStream.open(1, SharedStream::kB);
id = sharedStream.id();
cout << "id = " << id << β\nβ;
sharedStream.memInfo(cout);
cout << β\nβ;
}
break;
case βCβ:
{
sharedStream.seekp(0);
SharedCondition cond(sharedStream.createSharedCondition());
sharedStream.seekg(cond.width());
break;
}
case βKβ: // delete segment
{
if (id == -1)
{
cout << "No segment loaded\n";
continue;
}
cout << "Removing segment id = " << id << β\nβ;
if (ch == βRβ)
sharedStream.remove();
else
sharedStream.kill();
id = -1;
}
break;
case βLβ:
cin >> id;
cout << "Loading segment " << id << β\nβ;
sharedStream.open(id);
sharedStream.memInfo(cout);
cout << β\nβ;
break;
case βSβ:
if (id == -1)
{
cout << "No segment loaded\n";
continue;
}
sharedStream.memInfo(cout);
cout << β\nβ;
break;
case βsβ:
{
size_t offset;
if (id == -1)
{
cout << "No segment loaded\n";
continue;
}
cin >> offset;
sharedStream.seekg(offset);
sharedStream.seekp(offset);
cout << "tellg: " << sharedStream.tellg() << ", "
"tellp: " << sharedStream.tellp() << β\nβ;
}
break;
case βiβ:
{
string line;
getline(cin, line);
sharedStream.seekp(0, ios::cur);
while (true)
{
cout << "? ";
if (not getline(cin, line) || line.empty())
break;
sharedStream << line << endl;
cout <<
" tellp: " << sharedStream.tellp() << β\nβ;
}
cout << // " tellg: " << sharedStream.tellg() << ", "
" tellp: " << sharedStream.tellp() << β\nβ;
}
break;
case βxβ:
{
string line;
sharedStream.seekg(0, ios::cur);
while (true)
{
cout << ": ";
if (not getline(sharedStream, line))
break;
cout << line << "\n"
" tellg: " << sharedStream.tellg() << ", "
" tellp: " << sharedStream.tellp() << β\nβ;
}
}
break;
case βXβ:
{
SharedCondition cond(sharedStream.attachSharedCondition(0));
string line;
sharedStream.seekg(cond.width());
cond.lock();
while (true)
{
cond.wait();
cout << ": ";
if (not getline(sharedStream, line))
break;
cout << line << "\n"
" tellg: " << sharedStream.tellg() << ", "
" tellp: " << sharedStream.tellp() << β\nβ;
}
cout << "All done\n";
cond.unlock();
}
break;
case βNβ:
{
string line;
getline(cin, line);
SharedCondition cond(sharedStream.attachSharedCondition(0));
while (true)
{
cout << "βenterβ or βqβ? ";
getline(cin, line);
if (line == "q")
break;
cond.lock();
cond.notify();
cond.unlock();
}
}
break;
case βpβ: // put a char behind the last written
{
if (id == -1)
{
cout << "No segment loaded\n";
continue;
}
cin >> offset >> ch;
if (!cin)
throw Exception() << "cmd specification error";
sharedStream.seekp(offset);
cout << "Segment id = " << id << " at write offset " <<
sharedStream.tellp() << β\nβ;
sharedStream.put(ch);
}
break;
case βrβ: // put a char behind the last written
{
if (id == -1)
{
cout << "No segment loaded\n";
continue;
}
int n;
cin >> offset >> n;
if (!cin)
throw Exception() << "cmd specification error";
char buf[n];
sharedStream.seekg(offset);
cout << "Segment id = " << id << " at offset " <<
sharedStream.tellg() << ", to read " << n << " bytes\n";
n = sharedStream.read(buf, n).gcount();
if (n < 0)
cout << "No data at " << offset << β\nβ;
else
{
cout << "Retrieved " << n << " bytes, containing `";
cout.write(buf, n);
cout << "β\n";
for (auto ch: buf)
cout << static_cast<int>(ch) << β β;
cout << β\nβ;
}
}
break;
case βwβ: // write chars at offset
{
if (id == -1)
{
cout << "No segment loaded\n";
continue;
}
string line;
cin >> offset;
getline(cin, line);
if (!cin)
throw Exception() << "cmd specification error";
streampos pos = sharedStream.seekp(offset).tellp();
cout << "Segment id = " << id << " at offset " <<
pos << ", to write " << line.length() << " bytes\n";
sharedStream.write(line.data(), line.length());
if (!sharedStream)
cout << "No room left to write any bytes\n";
else
cout << "Wrote " << (sharedStream.tellp() - pos) << " bytes\n";
}
break;
case βqβ:
return 0;
default:
cout << "request not implemented: " << ch << β\nβ;
break;
}
}
}
a few things you're gonna clock while you're poking around in there πΈ
- the menu string only advertises
K,S,L <id>,C,c,q,i,x,X,N,s <x>and?β the other keys (l,p <x> c,r <x> <n>,w <x> args) are sitting right there in the source but commented out of the menu. - and yet
case βpβ:,case βrβ:andcase βwβ:are very much alive down at the bottom of theswitch, so they'll still fire if you type them π case βXβ:andcase βNβ:are the duo that talk to each other through aSharedConditionβ one waits, the otherlock()s,notify()s andunlock()s πcase βqβ:is your escape hatch β it justreturn 0;s and you're out the door πββοΈ- not me noticing that
case βpβ:andcase βrβ:carry the exact same comment (// put a char behind the last written) even thoughβrβis reading, not putting π
π it's giving "demo program that doubles as a stress test", and honestly? slay.
π FILES π
bobcat/sharedstreamβ defines the class interface
π SEE ALSO π
bobcat(7)chmod(1)isharedstream(3bobcat)osharedstream(3bobcat)sharedblock(3bobcat)sharedcondition(3bobcat)sharedmemory(3bobcat)sharedmutex(3bobcat)sharedpos(3bobcat)sharedsegment(3bobcat)sharedbuf(3bobcat)
π BUGS π
ok bestie, this one's important, do not skip it π¨
by default, exceptions thrown from inside astd::streamobject get caught by the stream object itself, and that just quietly sets itsios::failbitflag. sneaky little guy.
if you want exceptions to actually leave a stream object, you call its exceptions member, like this:
myStream.exceptions(ios::failbit | ios::badbit | ios::eofbit);
that's the move. πβ¨
...and bestie, if you wanna go straight to the source, here's where the Bobcat lore lives π
- https://fbb-git.gitlab.io/bobcat/: the gitlab project page, it's giving main character energy fr fr;
And the Debian Bobcat project files situation:
libbobcat6: the debian package holding the shared library, the changelog and the copyright note, iconic;libbobcat-dev: the debian package with the static library, the headers, the manual pages, and all the developer info. literally everything you need π«Ά
π BOBCAT
Okay so fun fact, Bobcat is an acronym of Brokkenβs Own Base Classes And Templates. Not me thinking it was just a cute lil wildcat this whole time π no cap, that's the actual name, periodt.
π COPYRIGHT
This is free software, distributed under the terms of the GNU General Public License (GPL). Free as in freedom bestie, slay π
π AUTHOR
Frank B. Brokken (f.b.brokken@rug.nl). The literal father of Bobcat, we stan π
And that's a wrap on this one, bestie!! π¦β¨ This whole thing shipped as part of libbobcat-dev, version 6.07.01, dated 2005-2025 β yeah, twenty whole years of Bobcat, that's longevity, that's legacy, that's ate and left no crumbs π₯π Go forth and stream your shared objects responsibly, ily, bye!! ππΈ
π¦ where this page comes from
- debian trixie: libbobcat-dev 6.07.01-2 Β· GPL-3.0-or-later WITH OpenSSL exception
- ubuntu questing: libbobcat-dev 6.07.01-2
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms