mercredi 3 février 2016

How can I make an output stream that encloses unspecified types within brackets in C++?

First timer on StackOverflow, please don't eat me alive.

Here is my problem: I use classes that define the operator << to output their member values. Unfortunately, these classes forgot that member values can be other classes and they do not enclose themselves between (let's say) curly brackets to keep the structural information intact. I cannot modify these classes, nor have I information on their structure. This means I can't just brutally browse myself their contents.

For the sake of the example, below is a chunk of code that has two classes, A and B (where B contains a class A) which output their member variables ("<variable_name>=<value>, "). Here is the result of using the << operator on cout for b:

cout << b; // yields: m_a=m_foo=43586, m_bar=43604, m_foo=47938

One can see that it is impossible to tell whether A has one or two member variables (does m_bar belong to A or B?).

I have spent some time looking into a wrapper around ostream that would add curly brackets around things that are neither C-style strings nor ints. Unfortunately, the recursion is far from as trivial as I initially thought.

produced result: B= { m_a=m_foo=43586, m_bar=43604, m_foo=47938 } 
expected result: B= { m_a= { m_foo=43586, m_bar=43604 } , m_foo=47938 }

(but it works great for wrapper << 42 << "cstring" << variable_with_other_type;)

And here are my questions: Am I doing this completely wrong? Did I miss something obvious? Is the solution to my problem significantly more complicated than my approach?

This is the full code (it requires C++11 rvalues, but I am stuck with an outdated version of gcc that has only -std=c++0x, so it needs to be as little C++11 as possible).

#include <sstream>
#include <typeinfo>
using namespace std;

/* THIS BELOW CANNOT BE MODIFIED */

class A
{
    public:
        A(){
            m_foo=0xAA42;
            m_bar=0xAA54;
        };
        virtual ~A(){};
        friend ostream& operator<<(ostream& os, const A& a);
        int m_foo;
        int m_bar;
};

class B
{
    public:
        B(A a) {
            m_foo = 0xBB42;
            m_a = a;
        }
        virtual ~B(){};
        friend ostream& operator<<(ostream& os, const B& b);
        A m_a;
        int m_foo;
};

ostream& operator<<(ostream& os, const A& a)
{
    // os << "{"; // woops forgot to uncomment this
    os << "m_foo=" << a.m_foo;
    os << ", ";
    os << "m_bar=" << a.m_bar;
    // os << "}"; // woops forgot to uncomment this
    return os;
}

ostream& operator<<(ostream& os, const B& b)
{
    // os << "{"; // woops forgot to uncomment this
    os << "m_a=" << b.m_a;
    os << ", ";
    os << "m_foo=" << b.m_foo;
    // os << "}"; // woops forgot to uncomment this
    return os;
}

/* THIS BELOW CAN BE MODIFIED */

class WrappingOstream
{
    public:
        WrappingOstream(ostream& stream);
        virtual ~WrappingOstream();

        template <class T> WrappingOstream& operator<<(T&& x) {
            m_stream << " { " << forward<T>(x) << " } ";
            //*this << " { " << forward<T>(x) << " } "; //segfaults when replacing m_stream with *this
            return *this;
        }

        WrappingOstream& operator<<(int i)
        {
            m_stream << i;
            return *this;
        }

        WrappingOstream& operator<<(const char* cstr)
        {
            m_stream << cstr;
            return *this;
        }

    private:
        ostream& m_stream;
};

WrappingOstream::WrappingOstream(ostream& stream) :
    m_stream(stream)
{
    //ctor
}

WrappingOstream::~WrappingOstream()
{
    //dtor
}



int main() {

    A a;
    B b(a);

    ostringstream ss;
    WrappingOstream wos(ss);
    wos << b << endl;
    cout << "produced result: B=" << ss.str();
   cout << "expected result: B=" << " { m_a= { m_foo=43586, m_bar=43604 } , m_foo=47938 }";

}

Thanks in advance,

Aucun commentaire:

Enregistrer un commentaire