Winestone
Winestone

Reputation: 1500

C++ Indenting output class inheriting ofstream

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!

Upvotes: 1

Views: 2246

Answers (2)

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

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.

Upvotes: 3

Winestone
Winestone

Reputation: 1500

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

Upvotes: 2

Related Questions