I'm trying to create some I/O manipulators to allow a user to modify the output format of a custom type.
Say I have a Foo object: I might want to either output it in a nice, human-readable format (pretty printing), or I might want to print it in a condensed form to save space when serialized.
So, it would be nice to have custom I/O manipulators like condensed and pretty that would modify the internal flags of a facet, so I could say something like:
Foo f;
...
std::cout << pretty << f; // output human-readable format
std::cout << condensed << f; // output condensed format
The problem I always run into is the fact that once a facet object is created, it can only be retrieved later by using std::use_facet, which returns a const reference. This means I can't later modify any of the internal facet flags.
Consider a simple facet:
class my_facet : public std::locale::facet
{
public:
my_facet() : m_pretty(false), m_condensed(false)
{ }
void set_pretty(bool b) { m_pretty = b; }
void set_condensed(bool b) { m_condensed = b; }
static std::locale::id id;
private:
bool m_pretty;
bool m_condensed;
};
I could then create I/O manipulators like:
template <class CharT, class Traits>
inline std::basic_ostream<CharT, Traits>& pretty(std::basic_ostream<CharT, Traits>& os)
{
my_facet<CharT>* facet = new my_facet();
facet->set_pretty(true);
facet->set_condensed(false);
std::locale loc = std::locale(os.getloc(), facet);
os.imbue(loc);
return os;
}
That works nicely... but what if I want to allow the user to specify additional flags or formatting options, like say, an indentation option that allows the user to specify a number of spaces to indent, like this:
std::cout << pretty << indent(4) << f;
The problem is that each I/O manipulator has to recreate the facet object, and so previous flags set are lost. The reason is that there's no way to access a non-const reference to the existing facet.
I want to say:
template <class CharT, class Traits>
inline std::basic_ostream<CharT, Traits>& operator << (std::basic_ostream<CharT, Traits>& os,
const indent& ind)
{
const my_facet<CharT>& facet = std::use_facet<my_facet>(os.getloc());
facet.set_indentation(ind.value()); // Error: facet is const!
return os;
}
...but of course, that won't work, because facet is const. The only way I can see around this is making all internal flags mutable, which is absurd.
So, I'm sensing that I'm just doing this wrong. There doesn't seem to be any way to get a non-const reference to an existing facet, so I think I'm going about this whole thing the wrong way.
So, how is this sort of thing usually achieved? How can I write I/O manipulators that can be chained together to set different flags, like:
std::cout << pretty << indent(3) << etc ...
The accepted way of storing custom formatting state is with memory allocated by std::ios_base::xalloc. For example (abridged, live demo with full code here):
class MyFancyManipulator
{
static int myidx;
int st;
public:
MyFancyManipulator(int st) : st(st) {};
template <typename Ch, typename Tr> friend
std::basic_ostream<Ch, Tr>& operator<< (std::basic_ostream<Ch, Tr>& str,
const MyFancyManipulator& man) {
//
// Transfer state from the manipulator to the stream
//
str.iword(MyFancyManipulator::myidx) = man.st;
return str;
}
};
// Allocate index for the state variable
// This is thread safe per C++14 standard
int MyFancyManipulator::myidx = std::ios_base::xalloc();
// ... In some custom operator<<
int state = str.iword(MyFancyManipulator::myidx);
Related
Imagine a larger project, containing some parameter struct:
struct pars {
int foo;
};
With this struct as parameter, other functionality is implemented, e.g.:
// (de)serialization into different formats
static pars FromString(string const &text);
static string ToString(pars const &data);
static pars FromFile(string const &filename);
// [...]
// comparison / calculation / verification
static bool equals(pars l, pars r);
static pars average(pars a, pars b);
static bool isValid(pars p);
// [...]
// you-name-it
Now imagine a new member needs to be added to that struct:
struct pars {
int foo;
int bar; // new member
};
Is there a design pattern to break the build or issue warnings until all neccessary code places are adapted?
Example:
If I were to change int foo into string foo, I would not miss any code line which needs to be changed.
If int foo would need to change into unsigned int foo, I could rename foo to foo_u and have the compiler point me to where adaptations are neccessary.
One partial solution is to make the members private and settable only from the constructor, which has to be called with all parameters:
pars::pars(int _foo, int _bar)
: foo(_foo), bar(_bar)
{ }
This ensures the correct creation of pars, but not the usage - so this catches missing adaptations in FromString(), but not in ToString().
Unit tests would reveal such problems only during the test (I'm searching for a compile time method), and also only the (de)serialization part, and not that new bar is being considered everywhere (in the comparison / calculation / verification / ... functions as well).
Decouple the streaming operations from the source or destinations of the streams.
A very simple example:
#include <sstream>
#include <fstream>
struct pars
{
int foo;
int bar;
static constexpr auto current_version = 2;
};
std::istream &deserialise(std::istream &is, pars &model)
{
int version;
is >> version;
is >> model.foo;
if (version > 1) {
is >> model.bar;
}
return is;
}
std::ostream &serialise(std::ostream &os, const pars &model)
{
os << model.current_version << " ";
os << model.foo << " ";
// a version 2 addition
os << model.bar<< " ";
return os;
}
static pars FromString(std::string const &text)
{
std::istringstream iss(text);
auto result = pars();
deserialise(iss, result);
return result;
}
static std::string ToString(pars const &data)
{
std::ostringstream oss;
serialise(oss, data);
return oss.str();
}
static pars FromFile(std::string const &filename)
{
auto file = std::ifstream(filename);
auto result = pars();
deserialise(file, result);
return result;
}
Also have a look at:
boost.serialization http://www.boost.org/doc/libs/1_64_0/libs/serialization/doc/index.html
cereal https://github.com/USCiLab/cereal
etc.
A pattern that enforces this would be a for-each-member operation.
Pick a name, like members_of. Using ADL and a tag, make members_of(tag<T>) return a tuple of integral constant member pointers to the members of T.
This has to be written once. Then it can be used many spots.
I will write it in C++17 as in 14 and earlier it is just more verbose.
template<class T>struct tag_t{constexpr tag_t(){}};
template<class T>constexpr tag_t<t> tag{};
template<auto X>using val_t=std::integral_constant<decltype(X), X>;
template<auto X>constexpr val_k<X> val{};
struct pars {
int foo;
friend constexpr auto members_of( tag_t<pars> ){
return std::make_tuple( val<&pars::foo> );
}
};
When you add a member you must also add it to the friend members_of.
template<class...Fs>
struct overload:Fs...{
using Fs::operator()...;
overload(Fs...fs):Fs(std::move(fs))... {}
};
overload lets you overload lambdas.
Finally write a foreach_tuple_element.
static pars FromString(string const &text){
pars retval;
foreach_tuple_element( members_of(tag<pars>), overload{
[&](val_t<&pars::foo>){
// code to handle pars.foo
}
});
return retval;
}
when you add a new member bar to both pars and members_of, the above code breaks as the foreach cannot find an overload for val_t<&pars::bar>.
static pars FromString(string const &text){
pars retval;
foreach_tuple_element( members_of(tag<pars>), overload{
[&](val_t<&pars::foo>){
// code to handle pars.foo
},
[&](val_t<&pars::bar>){
// code to handle pars.bar
}
});
return retval;
}
and now it would compile.
For serialization / deserialization specifically, you want a single method for both (where the type of one arg says if it is in or out), and string to/from is just a special case of serialization/deserialization.
template<class A, class Self,
std::enable_if_t<std::is_same<pars, std::decay_t<Self>>{}, int> =0
>
friend void Archive(A& a, Self& self) {
ArchiveBlock(a, archive_tag("pars"), 3, [&]{
Archive(a, self.foo);
Archive(a, self.bar);
});
}
this is an example of how a unified serialize/deserialize method (without the above member pointers) works. You override Archive on your output stream and on primitive const&, on your input stream and primitive&.
For almost everything else, you use common structure for both reading and writing from the Archive. This keeps the structure of your input and output identical.
ArchiveBlock( Archive&, tag, tag version, lambda ) wraps the lambda in whatever archiving block structure you have. As an example, your archive blocks might have length information in their header, allowing earlier deserializers to skip over added data at the end. It would also both read and write blocks; on writing, it would write out the block header and whatever else before writing the body (maybe keeping track of length and backing up to record length once they know it). On reading, it would ensure the tag exists (and deal with missing tags however you choose; skip?) and fast forward over newer block contents if you want to support older readers reading what newer writers write.
In more general cases where you need to keep code aligned with data this answer might solve things. Serialization and deserialization are very special cases, because unlike most bits of C++ code you have to future-proof the binary layout of everything. It is like writing library interfaces; there is lots more care required.
Let's suppose I want to write my own manipulator for input and output.
cin >> mymanip >> str;
or
cout << mymanip << str;
What I want that mymanip does is toggle case the caracters I read from input and assigns the result to one string.
So, if I type "QwErTy" I get "qWeRtY" in the string.
This is a very basic task with one function, but i want to learn more about manipulators.
Can someone give a clue?
Thank you.
All that a manipulator does is set the corresponding bits in the std::ios_base base class.
For example, the std::setprecision() manipulator simply invokes std::ios_base::precision(), on the manipulated stream.
The implementation of std::setprecision() is almost readable, in gcc's headers (a rarity, for a C++ library template implementation):
inline _Setprecision setprecision(int __n)
{ return { __n }; }
std::setprecision() returns an internal std::_Precision object. Then, a simple template overload for the >> (and the << operator, which is similar) operator, for the std::_Precision object, handles the rest of the magic:
template<typename _CharT, typename _Traits>
inline basic_istream<_CharT, _Traits>&
operator>>(basic_istream<_CharT, _Traits>& __is, _Setprecision __f)
{
__is.precision(__f._M_n);
return __is;
}
In your case, there are no bits in the std::ios_base class that implement your desired input/output transformation. As such, a manipulator, per se, won't work here.
What you're trying to do requires a completely different, more complicated, approach:
A custom subclass of std::[io]stream, that uses a custom subclass of std::streambuf.
The std::streambuf subclass reads or writes from a chained stream, transforming the input or output as you've described.
Reading or writing from the custom subclass ends up reading or writing from the chained stream, transforming the data accordingly.
The way is a little tricky - but it can be done, you can add own manipulator for stream.
First, you need your toggle:
class toggle_t {};
constexpr toggle_t toggle;
Next - the version for ostream (the case for istream is very similar...):
After putting toggle to ostream - you need some special object:
struct toggled_ostream
{
std::ostream& os;
};
inline toggled_ostream operator << (std::ostream& os, toggle_t)
{
return { os };
}
Beware, that someone might put togglein wrong place: cout << toggle << 123 - so it should work for all other types as ordinary stream:
template <typename T>
std::ostream& operator << (toggled_ostream tos, const T& v)
{
return tos.os << v;
}
So - for char types (like char, const char*, std::string) write your toggle overloads. I am giving you version for char - it shouldn't be a problem to write version for "longer" types:
std::ostream& operator << (toggled_ostream tos, char v)
{
char c = std::isupper(v) ? std::tolower(v)
: std::islower(v) ? std::toupper(v) : v;
return tos.os << c;
}
Working demo.
You cannot do that. What you can do instead are manipulators that would take the string as argument, i.e.
std::cout << toggle(str);
std::cin >> toggle(str);
A manipulator is only ever so-called syntactic sugar, i.e. it can do things more conveniently than otherwise. For example,
std::cout << std::setw(5) << x <<;
will do the same as
std::cout.width(5);
std::cout << x;
but is more convenient as it allows to be chained together with other << operations.
Now, there is no formatting support of the thing you want (swap lower and upper case characters), and hence also no way to provide syntactic sugar for that.
However, if the manipulator can take your string as an argument, then of course, you can achieve what you want, implemented in the standard way of manipulator implementation. For example,
struct toggle_output
{ std::string const&str; }
inline toggle_output toggle(std::string const&str)
{ return {str}; }
inline std::ostream& operator<<(std::ostream&out, toggle_output const&t)
{
for(auto c:t.str)
if (std::islower(c)) out<<std::toupper(c);
else if(std::isupper(c)) out<<std::tolower(c);
else out<<c;
return out;
}
struct toggle_input
{ std::string &str; }
inline toggle_input toggle(std::string&str)
{ return {str}; }
inline std::istream& operator>>(std::istream&in, toggle_input &t)
{
in >> t.str;
for(auto&c:t.str)
if (std::islower(c)) c=std::toupper(c);
else if(std::isupper(c)) c=std::tolower(c);
return in;
}
You may also need (to avoid confusion)
inline std::ostream& operator<<(std::ostream&out, toggle_input const&t)
{ return out<<toggle_output(t.str); }
As other answers explain, manipulators simply mimic existing std::ios_base functionality.
There's a simple solution to your problem, though I'm not sure if this can be called a manipulator:
struct toggle_in_helper
{
std::string & res;
};
toggle_in_helper toggle (std::string & res)
{
return {res};
}
std::istream & operator >> (std::istream & in, toggle_in_helper h)
{
in >> h.res;
for (auto & c : h.res)
// toggle the case of 'c'
;
return in;
}
That is, we create a helper class toggle_in_helper with overloaded operator >> which does the job.
I often run into situations (in my C++/C++11 code), where I have a type that basically behaves like a built-in type (or a "basic simple" type like std::string), but that has a meaning
beyond a 32 bit number or a bunch of characters.
I didn't find anything useful on the Internet, because I don't really what terms to search for...
Examples:
I once worked on a system, where items were identified by an ID. And these IDs were std::strings (probably not the best idea in the first place, but that's a different story). What was really bad though was the fact, that these IDs were passed through the system as std::strings or as const char*s. So it was hard (impossible) to tell where in the
code base IDs were used when searching for the type. The variable names were all variations of ID(ID, id, Id) or key or just i or name or whatever. So you could not search by name either. So I'd prefer to pass those variables as type id_t.
Network ports: They are uint16_ts. But I would like to pass them as network_port_ts.
I generally used typedefs to make things a little nicer. This approach has multiple problems though:
You don't have to use the typedef. You can still pass variables around by the "raw" type (e.g. std::string instead of id_t).
If the raw type is a template, you are done with forward declaring the typedef (e.g. with a shared_ptr).
"Forward declaring" the typedef is a maintenance problem. If the raw type changes, you get to change stuff all over the place.
Another thing I tried with the network port example was writing a thin wrapper class sporting a operator uint16_t. This solved the problem with forward declarations. But then I ran
into a trap with some logging macros which used printf internally. The printfs still worked (well, compiled), but didn't print the port number, but (I think) the address of the object.
I figured with dimensions like weights or lengths Boost.Units might be worth a look (even so it appears a little "heavy"). But for the two examples above, it doesn't fit.
What is the best practice to achieve what I want (using Boost is an option)?
In short:
What I want to achieve is to pass "types with higher meaning" as its own type and not as the plain raw/low level/non-abstract type. (Kind of) like having a user defined type. Preferably without the huge overhead of writing a complete class for every type with basically identical implementations, only to be able to do what built-ins already can do.
1. Strong Typedefs
You can use BOOST_STRONG_TYPEDEF to get some convenience.
It does employ macros, and I think you get to do heterogeneous comparisons (e.g. id == "123").
There's two versions, be sure to take the one from Boost Utility.
2. flavoured_string<>
For strings you can cheat the system by using flavoured strings (inventor: R.Martinho Fernandes).
This leverages the fact that you can actually vary the traits on a std::basic_string, and create actually different tagged aliases:
#include <string>
#include <iostream>
namespace dessert {
template <typename Tag>
struct not_quite_the_same_traits : std::char_traits<char> {};
template <typename Tag>
using strong_string_alias = std::basic_string<char, not_quite_the_same_traits<Tag>>;
using vanilla_string = std::string;
using strawberry_string = strong_string_alias<struct strawberry>;
using caramel_string = strong_string_alias<struct caramel>;
using chocolate_string = strong_string_alias<struct chocolate>;
template <typename T>
struct special;
template <typename T>
using special_string = strong_string_alias<special<T>>;
std::ostream& operator<<(std::ostream& os, vanilla_string const& s) {
return os << "vanilla: " << s.data();
}
std::ostream& operator<<(std::ostream& os, strawberry_string const& s) {
return os << "strawberry: " << s.data();
}
std::ostream& operator<<(std::ostream& os, caramel_string const& s) {
return os << "caramel: " << s.data();
}
std::ostream& operator<<(std::ostream& os, chocolate_string const& s) {
return os << "chocolate: " << s.data();
}
template <typename T>
std::ostream& operator<<(std::ostream& os, special_string<T> const& s) {
return os << "special: " << s.data();
}
}
int main() {
dessert::vanilla_string vanilla = "foo";
dessert::strawberry_string strawberry = "foo";
dessert::caramel_string caramel = "foo";
dessert::chocolate_string chocolate = "foo";
std::cout << vanilla << '\n';
std::cout << strawberry << '\n';
std::cout << caramel << '\n';
std::cout << chocolate << '\n';
dessert::special_string<struct nuts> nuts = "foo";
std::cout << nuts << '\n';
}
To create an integer that's not an integer (or a string that's not a string) and cannot promote or demote to it), you can only create a new type, that merely means "write a new class". There is no way -at least on basic type- to inherit behaviour without aliasing. A new_type<int> has no arithmetic (unless you'll define it).
But you can define a
template<class Innertype, class Tag>
class new_type
{
Innertype m;
public:
template<class... A>
explicit new_type(A&&... a) :m(std::forward<A>(a)...) {}
const Innertype& as_native() const { return m; }
};
and do all the workout only once for all.
template<class T, class I>
auto make_new_type(I&& i)
{ return new_type<I,T>(std::forward<I>(i)); }
template<class A, class B, class T>
auto operator+(const new_type<A,T>& a, const new_type<B,T>& b)
{ return make_new_type<T>(a.as_native()+b.as_native()); }
....
and then
struct ID_tag;
typedef new_type<std::string,ID_tag> ID;
struct OtehrID_tag;
typedef new_type<std::string,OtehrID_tag> OtherID;
and ID oand OtherID cannot mix in expressions.
NOTE:
auto -function with unspecifyed return are standard from C++14, but GCC accepts it in C++11 as-well.
template <typename tag_t, typename value_t>
struct meaningful_value
{
typedef value_t value_type;
meaningful_value() : value() {}
explicit meaningful_value(const value_type & val) : value(val) {}
operator const value_type & () const { return value; }
protected:
value_type value;
};
typedef meaningful_value<struct ParamType1_tag, double> ParamType1;
typedef meaningful_value<struct ParamType2_tag, double> ParamType2;
This is basically what boost::quantity does, but allows for default construction; the tag struct can be declared inplace in the typedef, so declaring a new type of parameter is a single-line deal; you get to choose if you want a macro for it
(Edited to fix constructor name)
There is the complex<> template in C++ standard library, and it has an overloaded << operator so that it outputs complex numbers in the (real_part, im_part) format. I need to change the behavior of that operator for complex numbers so that the output format is changed to something completely different. Specifically, I need the output to be in the form real_part\tim_part. How do I do that?
There's no direct way to replace operator <<, but you do have a few options. First, you could just write your own function to print complex numbers:
template <typename T> void PrintComplex(const complex<T>& c) {
/* ... */
}
If you want to still use the nice stream syntax, then one trick you could do would be to make a wrapper class that wraps a complex and then defines its own operator << that prints it out in a different way. For example:
template <typename T> class ComplexPrinter {
public:
/* Conversion constructor allows for implicit conversions from
* complex<T> to ComplexPrinter<T>.
*/
ComplexPrinter(const complex<T>& value) : c(value) {
// Handled in initializer list
}
/* Output the complex in your own format. */
friend ostream& operator<< (ostream& out, const ComplexPrinter& cp) {
/* ... print in your own format ... */
}
private:
complex<T> c;
};
Once you have this, you could write something like
cout << ComplexPrinter<double>(myComplex) << endl;
You can make this even cleaner by writing a function like this one to wrap the object for you:
template <typename T>
ComplexPrinter<T> wrap(const complex<T>& c) {
return ComplexPrinter<T>(c);
}
This then lets you write
cout << wrap(myComplex) << endl;
Which isn't perfect, but is pretty good.
One thing to note about the above wrapper is that it has an implicit conversion constructor set up to let you convert complex<T>s to ComplexPrinter<T>s. This means that if you have a vector< complex<T> >, you can print it out using your custom code by calling
vector< complex<double> > v = /* ... */
copy (v.begin(), v.end(), ostream_iterator< ComplexPrinter<double> >(cout, " "));
On output, the implicit conversion constructor will transform your complex<double>s into the wrappers, and your custom code will do the printing for you.
If you want to be very adventurous and cast caution to the wind, you could even write the class so that it just stores a reference to the original complex, as shown here:
template <typename T> class ComplexPrinter {
public:
/* Conversion constructor allows for implicit conversions from
* complex<T> to ComplexPrinter<T>.
*/
ComplexPrinter(const complex<T>& value) : c(value) {
// Handled in initializer list
}
/* Output the complex in your own format. */
friend ostream& operator<< (ostream& out, const ComplexPrinter& cp) {
/* ... print in your own format ... */
}
private:
const complex<T>& c;
};
This completely eliminates any copying and just makes the wrapper a thin veneer around a real complex. (No pun intended). You'd have to be very careful if you did this not to pass these objects around across scope boundaries where the original objects go out of scope, but if it's what you want it might work out just great.
Hope this helps!
template<class T>
struct my_complex_format_type {
std::complex<T> const &x;
my_complex_format_type(std::complex<T> const &x) : x (x) {}
friend std::ostream& operator<<(std::ostream &out,
my_complex_format_type const &value)
{
out << "format value.x however you like";
return out;
}
};
template<class T>
my_complex_format_type<T> my_complex_format(std::complex<T> const &x) {
return x;
}
void example() {
std::cout << my_complex_format(some_complex);
}
For any specific instantiation of complex<T>, Use a strong typedef (boost has a version) and cast to that type during << calls. Override << for that type.
If you need to override << for any variation of complex<T> then life will be harder.
My answer to the same question here: c++ display complex number with i in imaginary part produces the behavior you want, at the expense of some risk of future incompatibility because it inserts a template specialization into the std:: namespace.
There is no really tidy way to do that. My suggestion would be to just ditch iostreams and write something more C-like instead. It will probably be faster to write, faster to read and faster to execute.
For an API that I am working on, I want to allow the user to insert custom objects into an ostream, but these objects have no meaning on their own, and are too memory constrained to include an additional pointer or reference for context. (Think tens of millions of 16-/32-/48-bit objects in an embedded system with limited memory.)
Suppose the user initializes the underlying context, and looks up one of these objects:
DDB ddb("xc5vlx330t");
Tilewire tw = ddb.lookUpTilewire("DSP_X34Y0", "DSP_IMUX_B5_3");
...
std::cout << complexDataStructure;
In an entirely different scope, possibly nested far away from the user's explicit code, we may need to insert the object into an ostream, with ddb unavailable.
os << tw;
The actual value encapsulated by tw is 97,594,974, but the desired output is this:
DSP_IMUX_B5_3#[263,84] DSP "DSP_X34Y0" (1488#77406)
In order for this to work, the appropriate insertion operator would need access to ddb, but it cannot rely on static or global variables or functions (for multithreading reasons). What I'd like to do is allow the user to request and use a stream wrapper kind of like this:
ostream& wrappedCout = ddb.getWrappedOstream(std::cout);
The returned subclass of ostream would include a reference to ddb for use by special stream inserters that needed it, and a reference to the original stream—std::cout in this case—where it would forward all of its output.
Unfortunately, the inheritance or composition schemes that I have come up with are messy to code up (not an enormous concern), and possibly problematic for the user (a much larger concern). Any suggestions on how to elegantly make ddb available to insertion operators? I am marginally aware of boost.Iostreams, but not sure that it will help me out here.
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;
}
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?
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;
}
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.