+1  A: 

Write a custom stream manipulator that stores a reference to ddb using the iword/pword mechanism. Here is an example, you'd need to add locking around the iwork_indexes map in a multithreaded program.

class dbb
{
public:
    explicit dbb(int value) : m_value(value) {}
    int value() const { return m_value; }
private:
    int m_value;
};

class dbb_reliant_type
{
public:
    dbb_reliant_type(const std::string& value) : m_value(value) {}
    const std::string& value() const { return m_value; }
private:
    std::string m_value;
};

typedef std::map<std::ostream*, int> iword_map;
iword_map iword_indexes;

inline int get_iword_index(std::ostream& os)
{
    iword_map::const_iterator index = iword_indexes.find(&os);

    if(index == iword_indexes.end())
    {
        std::pair<iword_map::iterator, bool> inserted = iword_indexes.insert(std::make_pair(&os, os.xalloc()));
        index = inserted.first;
    }

    return index->second;
}


inline std::ostream& operator<<(std::ostream& os, const dbb& value)
{
    const int index = get_iword_index(os);

    if(os.pword(index) == 0)
        os.pword(index) = &const_cast<dbb&>(value);

    return os;
}

std::ostream& operator<<(std::ostream& os, const dbb_reliant_type& value)
{
    const int index = get_iword_index(os);
    dbb* deebeebee = reinterpret_cast<dbb*>(os.pword(index));
    os << value.value() << "(" << deebeebee->value() << ")";
    return os;
}

int main(int, char**)
{
    dbb deebeebee(5);
    dbb_reliant_type variable("blah");
    std::cout << deebeebee << variable << std::endl;
    return 0;
}
Gary
@Gary: Can you give us a code example? I'd like to see how that's done....
Billy ONeal
Here's an example I googled up, I haven't look at it in detail but it should give you enough to go on. http://www.unc.edu/depts/case/pgi/pgC++_lib/stdlibug/str_5412.htm
Gary
@Gary, in the future, you can edit your own reply to include more expansive answers (e.g. the URL), example code, etc.
Joe
@Gary You're right. I just found that same link, and verified that it'll work. I'll post the code in a little bit and also give you credit for the answer.
Neil Steiner
@Gary Sorry, Gary: I'm a newbie, so I can't vote you up, but I did accept your answer.
Neil Steiner
+1  A: 

I'm not entirely sure if I understand what can be accessed at what time and what can and can't change, but....can you do something like this

struct TilewireFormatter {
    DDB *ddb;
    TilewireFormatter(DDB* d) : ddb(d) {}

    print(std::ostream& out, const Tilewire& obj) {
        // some formatting dependent on ddb
        out << obj;
    }
};

and replace out << tw; with formatter.print(out, tw);

then not provide any sort of << operator overload for Tilewire and pass an instance of TilewireFormatter around that's used to format them based on what ddb is?

choobablue
+1 -- just keep in mind that ddb must be kept alive somehow with this code.
Billy ONeal
Neil Steiner
@Billy ONeal The lifespan of the DDB object can safely be assumed to encompass any of these calls.
Neil Steiner
I didn't have any idea as to the scope or copy semantics of DDB so I just did a 'wrong way' as it was simple. If it was me, I would probably make copies of the relevant members of DDB and store those in the formatter instead of relying on DDB. And if you want it to have regular stream semantics you could have TilewireFormatter return a temporary object that does define operator<< then you get more like out << formatter(tw); if that's still too much, you could look into using .imbue(), I don't really know much about it, boost::date_time uses it, I think it decorates the stream somehow
choobablue
Neil Steiner
@Neil Steiner: Well you'd have to pass around a reference to your stream in the first place, so you're really not saving anything by eschewing that reference.
Billy ONeal
@Billy ONeal You're right that the user would still be passing something around, but it's something that they're very familiar and comfortable with, and it follows normal stream semantics. Again, since this is an API, I really don't want to discourage new users from trying it out. As you can see from Gary's answer and my answer, I believe we found a cleaner way.
Neil Steiner
A: 

Rather than fudging around and trying to find a way to pass contextual information while using the insertion operator, I suggest you make something like a print method like choobablue suggests. It's a nice and simple solution and anything fancier is probably more trouble than it's worth.

I also find it odd that you choose iostreams for an embedded system. They're one of the most bloated parts of the C++ standard library (not just by implementation, but by design) and if you are working on an embedded system, you could just as well roll your own alternative of this (still based on the basic design of iostreams) and can probably do it just as quickly as trying to use iostream effectively and across multiple threads.

Why the IOStreams FUD? I have yet to see a case where any speed limitation was an issue because any I/O device you're referencing with it is usually the bottleneck. Use the various streambuf classes if you need to be closer to the metal.
Billy ONeal
@stinky472 I think you may agree that the answer above is actually quite elegant from the user's perspective. But I'm with you on iostreams and embedded systems: The more complete story is that some users may need to run this embedded, and for those, I'm going to try to separate out the iostreams dependencies. In fact, the solution above will go a long way towards helping me segregate things. And for people running on regular systems, simplicity for the user is highly valued.
Neil Steiner
@Billy not speed so much, but for embedded systems with limited resources, I'm talking more about bloat. Even Sutter discusses this quite in depth and the mistakes the standard committee made by simply adopting legacy I/O streams with little changes. The combination of virtual methods and templates, for instance, leads to a lot of bloat. It's not uncommon to increase executable size dramatically just by having a hello world program that uses cout.
The user already mentioned he's on an embedded system with limited memory.
A: 

I'm new at this, so in case providing my own answer gets in the way of me sharing the credit with Gary, well, Gary pointed out what I had just stumbled upon moments before through the same reference: Stream Storage for Private Use: iword, pword, and xalloc

#include <iostream>

// statically request a storage spot that can be associated with any stream
const int iosDdbIndex = std::ios_base::xalloc();

class DDB {
public:
    // give the stream a pointer to ourselves
    void bless(std::ostream& os) { os.pword(iosDdbIndex) = this; }
    // provide a function that the insertion operator can access
    int getSomething(void) { return 50; }
};

class Tilewire {
    friend std::ostream& operator<< (std::ostream& os, Tilewire tilewire);
    // encapsulate a dummy value
    int m;
public:
    // construct the Tilewire
    Tilewire(int m) : m(m) {}
};

std::ostream& operator<< (std::ostream& os, Tilewire tilewire) {
    // look up the pointer to the DDB object
    DDB* ddbPtr = (DDB*) os.pword(iosDdbIndex);
    // insert normally, and prove that we can access the DDB object's methods
    return os << "Tilewire(" << tilewire.m << ") with DDB param " << ddbPtr->getSomething();
}

int main (int argc, char * const argv[]) {
    DDB ddb;
    ddb.bless(std::cout);
    std::cout << Tilewire(0) << std::endl;
    return 0;
}
Neil Steiner