1

So I want to write an indent output class that can be used like this:

Debug f;
f.open("test.txt");
f << Debug::IndS << "Start" << std::endl;
f << Debug::Ind << "test" << std::endl;
f << Debug::IndE << "End" << std::endl;

which would output:

Start
    test
End

So IndS would print out current indent and increment indent, Ind would print out the current indent and IndE would decrement indent and print out the current indent. I have tried to create it like so:

class Debug : public std::ofstream {
    public:
        Debug();
        ~Debug();

    private:
        std::string IndentText;
        int _Indent;

    public:
        void SetIndentText(const char* Text);
        inline void Indent(int Amount);
        inline void SetIndent(int Amount);

        inline std::ofstream& Ind (std::ofstream& ofs);
        inline std::ofstream& IndS(std::ofstream& ofs);
        inline std::ofstream& IndE(std::ofstream& ofs);
};

Debug::Debug () : std::ofstream() {
    IndentText = "    ";
}

Debug::~Debug () {
}

void Debug::SetIndentText (const char* Text) {
    IndentText = Text;
}

void Debug::Indent (int Amount) {
    _Indent += Amount;
}

void Debug::SetIndent(int Amount) {
    _Indent = Amount;
}

std::ofstream& Debug::Ind (std::ofstream& ofs) {
    for (int i = 0;i < _Indent;i++) {
        ofs << IndentText;
    }
    return ofs;
}

std::ofstream& Debug::IndS (std::ofstream& ofs) {
    ofs << Ind;
    _Indent++;
    return ofs;
}

std::ofstream& Debug::IndE (std::ofstream& ofs) {
    _Indent--;
    ofs << Ind;
    return ofs;
}

So I think there are a few problems with this:

  1. It does not compile. Errors with no match for 'operator<<' (operand types are 'std::ofstream {aka std::basic_ofstream<char>}' and '<unresolved overloaded function type>') ofs << Ind; candidates are: blah blah

  2. I don't override all constructors. Is there a way to do this? I think I just have to rewrite all the constructors to do IndentText = " "; and delegate the overloaded constructor

Could someone help me with this? Thanks!

Winestone
  • 1,500
  • 9
  • 19
  • 1
    You shouldn't inherit `std::ofstream`, provide an [I/O manipulator](http://en.cppreference.com/w/cpp/io/manip) instead. – πάντα ῥεῖ Aug 09 '14 at 09:13
  • I have tried to provide the manipulator's ``Ind`` ``IndS`` and ``IndE`` but this requires storing extra information which so I think creating a special version of an ofstream would work – Winestone Aug 09 '14 at 09:15
  • 2
    Heve a look [at this question](http://stackoverflow.com/questions/9599807/how-to-add-indention-to-the-stream-operator) it's a cleaner solution, should work with any stream (`cout`, `stringstream`, `fstream`) – Antoine Aug 09 '14 at 09:15
  • 1
    Here are some more hekpful links about how to write an I/O manipulator: http://stackoverflow.com/questions/535444/custom-manipulator-for-c-iostream http://stackoverflow.com/questions/799599/c-custom-stream-manipulator-that-changes-next-item-on-stream – πάντα ῥεῖ Aug 09 '14 at 09:16
  • Also, for redirecting constructors, use something like `template Child(T... && v) : Parent(std::forward(v)...) {}` – Antoine Aug 09 '14 at 09:18
  • Also you may extern the information about the actual indentation level into another class used by your I/O manipulators commonly. – πάντα ῥεῖ Aug 09 '14 at 09:19
  • @πάνταῥεῖ haha, visited all your links already :L – Winestone Aug 09 '14 at 09:25
  • @Antoine turns out my C++ is worse than I thought, not sure how to use your 2nd suggestion, your first suggestion looks interesting, though it seems to be based on reading characters while they are outputted and indents every newline, (which is probably what I want) but to use it in the way described, do you know what could be done? Manipulators might be useful – Winestone Aug 09 '14 at 09:29
  • @Winestone Well, may be a simple I/O manipulator isn't the right way to go. Though inheriting `std::ofstream` certainly isn't either. What you could do is having kind of a `Logger` class, that manages a `std::ostream` internally and exposes the `< – πάντα ῥεῖ Aug 09 '14 at 09:29
  • @πάνταῥεῖ sounds interesting, I'll try that, do you know anyway to expose all of ``std::ofstream``'s constructors? – Winestone Aug 09 '14 at 09:31
  • @πάνταῥεῖ how would I expose the ``operator<<``? Do I make a copy of the ``std::ofstream``'s ``operator<<``s and just redirect the arguements? or would I just public the ``std::ofstream``? – Winestone Aug 09 '14 at 09:36
  • @Winestone You have free friend functions for `Logger` like this `template Logger& operator<<(Logger& log, T op) { log.os << op; return log; }` – πάντα ῥεῖ Aug 09 '14 at 09:39
  • For the constructor thing, google `C++ perfect forwarding` and `variadic templates`, both new to C++11. But [apparently](http://stackoverflow.com/questions/3119929/forwarding-all-constructors-in-c0x) you could just simply do `using Parent::Parent` (where `Parent` is `std::ostream` or `std::ofstream` in your `Debug` child class, and the compiler will take care of the rest) – Antoine Aug 09 '14 at 10:20
  • @πάνταῥεῖ do you think this would be ok? http://ideone.com/6Xcl2c – Winestone Aug 09 '14 at 11:59
  • @Antoine do you think this would be ok? http://ideone.com/6Xcl2c – Winestone Aug 09 '14 at 11:59
  • @Winestone For using something like a plain manipulator, yes. I'd prefer something in the way I've given in my answer though. – πάντα ῥεῖ Aug 09 '14 at 12:01
  • @πάνταῥεῖ I would except for the slightly decreased flexibility :S – Winestone Aug 09 '14 at 12:03
  • @Winestone You can still write I/O manipulator style manipulators for `Logger` as well, as long these appear as the first argument in the `operator<<()` chain. It's flexible as can be IMHO. – πάντα ῥεῖ Aug 09 '14 at 12:06
  • @πάνταῥεῖ I guess you could, I'll mark yours as the answer and add my other solution :) – Winestone Aug 09 '14 at 12:11

2 Answers2

3

You usually shouldn't inherit std::ostream or implementations of it like std::ofstream. Wrap them into another class instead.

Here's a short sketch of my ideas mentioned in the comments

#include <iostream>
#include <fstream>

using namespace std;

class Logger {
public:
    Logger(ostream& os) : os_(os), curIndentLevel_(0) {}
    void increaseLevel() { ++curIndentLevel_; }
    void decreaseLevel() { --curIndentLevel_; }

private:
    template<typename T> friend ostream& operator<<(Logger&, T);

    ostream& os_;
    int curIndentLevel_;
};

template<typename T> 
ostream& operator<<(Logger& log, T op) {
    for(int i = 0; i < log.curIndentLevel_ * 4; ++i) {
        log.os_ << ' ';
    }
    log.os_ << op;
    return log.os_;
}

int main() {
    Logger log(cout);
    log.increaseLevel();
    log << "Hello World!" << endl;
    log.decreaseLevel();
    log << "Hello World!" << endl;
    return 0;
}

Output

    Hello World!
Hello World!

Live Sample


Here's a little variant, showing how you can shortcut the coding with operator<<() overloads:

class Logger {
public:
    Logger(ostream& os) : os_(os), curIndentLevel_(0) {}
    Logger& increaseLevel() { ++curIndentLevel_; return *this; }
    Logger& decreaseLevel() { --curIndentLevel_; return *this; }

    // ... as before ...        
};

int main() {
    Logger log(cout);

    log.increaseLevel() << "Hello World!" << endl;
    log.decreaseLevel() << "Hello World!" << endl;
    return 0;
}

Live Sample

The same way you can go to provide additional I/O manipulator style free functions.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
2

Alternative solution:

#include <iostream>
#include <fstream>

class IndentClass {
    public:
        IndentClass();
        ~IndentClass();

    private:
        std::string IndentText;
        int _Indent;

    public:
        inline void SetIndentText(const char* Text);
        inline void Indent(int Amount);
        inline void SetIndent(int Amount);

        inline void ind (std::ostream& ofs);

        class Ind_t {
            public:
                IndentClass& state;
                Ind_t (IndentClass& _state) : state(_state) {}
                friend inline std::ostream& operator<< (std::ostream& ofs, Ind_t& ind);
        };
        class IndS_t {
            public:
                IndentClass& state;
                IndS_t (IndentClass& _state) : state(_state) {}
                friend inline std::ostream& operator<< (std::ostream& ofs, IndS_t& ind);
        };
        class IndE_t {
            public:
                IndentClass& state;
                IndE_t (IndentClass& _state) : state(_state) {}
                friend inline std::ostream& operator<< (std::ostream& ofs, IndE_t& ind);
        };
        Ind_t Ind;
        IndS_t IndS;
        IndE_t IndE;
};

IndentClass::IndentClass () : IndentText("    "), _Indent(0), Ind(*this), IndS(*this), IndE(*this) {

}

IndentClass::~IndentClass () {
}

void IndentClass::SetIndentText (const char* Text) {
    IndentText = Text;
}

void IndentClass::Indent (int Amount) {
    _Indent += Amount;
}

void IndentClass::SetIndent(int Amount) {
    _Indent = Amount;
}

void IndentClass::ind (std::ostream& ofs) {
    for (int i = 0;i < _Indent;i++) {
        ofs << IndentText;
    }
}

std::ostream& operator<< (std::ostream& ofs, IndentClass::Ind_t& ind) {
    ind.state.ind(ofs);
    return ofs;
}

std::ostream& operator<< (std::ostream& ofs, IndentClass::IndS_t& inds) {
    inds.state.ind(ofs);
    inds.state.Indent(1);
    return ofs;
}

std::ostream& operator<< (std::ostream& ofs, IndentClass::IndE_t& inde) {
    inde.state.Indent(-1);
    inde.state.ind(ofs);
    return ofs;
}

int main () {
    IndentClass i;

    std::cout << i.IndS << "test" << std::endl;
    std::cout << i.Ind << "test" << std::endl;
    std::cout << i.IndE << "test" << std::endl;

    return 0;
}

Ideone Example

Winestone
  • 1,500
  • 9
  • 19