-2

I'm having a hard time trying to overload my << output operator for this class. The class has private members - enum class and string. The code I made compiles and runs, but the << operator is overloaded only for the enum class. From what I understand and read about it, I need to make something like this:

friend ostream& operator<<(ostream& os, CDirectory const& dir)

but I seem to fail at implementing it and at this point I really have no ideas. I want to overload the operator for the whole class CDirectory and not only the enum class Filetype, that is one of the members. Is this even possible to do?

class CDirectory {
    string m_strDirectory;
    enum class Filetype {
        Archive, Hidden, ReadOnly, System, FileNotSupported
    };
    multimap <CFile, Filetype> m_DirectoryMap;
public:
    /* overloading operator<< for the enum class Filetype */
    friend ostream& operator<<(ostream& os, Filetype const type)
    {
        switch (type)
        {
        case Filetype::Archive:
            os << "archive";
            break;
        case Filetype::Hidden:
            os << "hidden";
            break;
        case Filetype::ReadOnly:
            os << "read-only";
            break;
        case Filetype::System:
            os << "system";
            break;
        case Filetype::FileNotSupported:
            os << "not-supported";
            break;
        }
        return os;
    }
    /* explicit constructor - reads data from a file and inserts pairs
      of types pair <CFile, enum Filetype> in a multimap */
    CDirectory (const string& n) {
        fp.open (n, ios::in);
        string dirName, fileName,  fType;
        int fileSize;
        Filetype filetype;
        fp >> dirName;
        m_strDirectory = dirName;
        while (fp >> fileName >> fileSize >> fType) {
            CFile obj (fileName, fileSize);
            if (fType == "Archive")
                filetype = Filetype::Archive;
            else if (fType == "Hidden")
                filetype = Filetype::Hidden;
            else if (fType == "ReadOnly")
                filetype = Filetype::ReadOnly;
            else if (fType == "System")
                filetype = Filetype::System;
            else
                filetype = Filetype::FileNotSupported;
            m_DirectoryMap.insert(pair<CFile, Filetype>(CFile(obj.getFileName(), obj.getFileSize()), Filetype(filetype)));
        }
        auto p = m_DirectoryMap.begin();
        cout << m_strDirectory << endl;
        while ( p != m_DirectoryMap.end()) {
            cout << endl << p->first.getFileName() << '\t' << p->first.getFileSize() << '\t' << p->second << endl;
            ++p;
        }
    }
    void printMap () {
         auto p = m_DirectoryMap.begin();
         cout << m_strDirectory << endl;
         while ( p != m_DirectoryMap.end()) {
                cout << endl << p->first.getFileName() << '\t' << p->first.getFileSize() << '\t' << p->second << endl;
                ++p;
        }
    }
};

UPDATE:

I tried the following code, for implementing the overload operator << for my entire class, but it produces an error - error: cannot bind 'std::basic_ostream<char>' lvalue to 'std::basic_ostream<char>&&'|on these lines (1 of them is in my constructor and 1 in my printMap function) - cout << endl << p->first.getFileName() << '\t' << p->first.getFileSize() << '\t' << p->second << endl; I'm guessing that it has something to do with the p->second part, but I don't know what exactly is going on. Any thoughts on it?

Update: yep, the problem is when I try to print out the enum with p->second. If I remove that part it compiles..

std::ostream& operator<<(std::ostream &os, const CDirectory &dir)
    {
        const Filetype& type;
        switch (type)
        {
        case Filetype::Archive:
            os << "archive";
            break;
        case Filetype::Hidden:
            os << "hidden";
            break;
        case Filetype::ReadOnly:
            os << "read-only";
            break;
        case Filetype::System:
            os << "system";
            break;
        case Filetype::FileNotSupported:
            os << "not-supported";
            break;
        }
        os << dir.m_strDirectory << "\n";
        for(auto &p : dir.m_DirectoryMap) {
           os << p.first.getFileName() << '\t' << p.first.getFileSize() << '\t' << p.second << '\n';
        }
        return os;
    }
hopittyhop
  • 119
  • 12
  • 3
    What is the exact problem you seem to be having? – Armen Tsirunyan Feb 23 '16 at 08:29
  • Well, I want to overload my operator for all of the private members - the `enum class`, `m_strDirectory` and the `multimap` as well. The `CFile` class has it's own operator << overloaded correctly. – hopittyhop Feb 23 '16 at 08:32
  • 4
    It would a lot helpful if you would remove the unnecessary code. – Raxvan Feb 23 '16 at 08:33
  • I don't know if it's unnecessary code, because when I tried to overload the operator for all private members I made the code compile after that but got errors in the `multimap.insert` part, which were connected with the `enum`. – hopittyhop Feb 23 '16 at 08:35
  • It is unclear where you want to call which stream inserter. Please provide a [mcve]. – Murphy Feb 23 '16 at 09:10
  • You have to add an overloaded operator<< for the entire class, like that you wrote in the question, which prints the string, the enum and iterate through the multimap printing the cfiles. You have already written the overloaded operators needed, just use them together. – Bob__ Feb 23 '16 at 09:14
  • You say you fail to implement the operator<< for your class. What exactly have you tried and what were the errors you got? – rozina Feb 23 '16 at 09:26
  • I have updated my post with my implementation. – hopittyhop Feb 23 '16 at 09:34

3 Answers3

2

I assume in the end you want to be able to do something like this:

CDirectory dir("input_file");
std::cout << dir;

and this should output everything at once (basically everything that CDirectory::printMap() currently prints, right?

For this to work, you need to overload operator<< for your CDirectory class as a non-member function:

std::ostream& operator<<(std::ostream &os, const CDirectory &dir)
{
    os << dir.m_strDirectory << "\n";
    for(auto &p : dir.m_DirectoryMap) {
        std::cout << p.first.getFileName() << '\t' << p.first.getFileSize() << '\t' << p.second << '\n';
    }

    return os;
}

and then make this function a friend of your class:

friend std::ostream& operator<<(std::ostream &os, const CDirectory &dir);

for it to be able to access the class's private members.

Alternatively, because you already have a print function in your class, you can just forward to that one so you don't need to make operator<< a friend (some people frown on friend functions). For this to work, you have to change your printMap() function a bit:

void printMap (std::ostream &os) const
{
     auto p = m_DirectoryMap.begin();
     os << m_strDirectory << endl;
     while ( p != m_DirectoryMap.end()) {
            os << endl << p->first.getFileName() << '\t' << p->first.getFileSize() << '\t' << p->second << endl;
            ++p;
    }
}

std::ostream& operator<<(std::ostream &os, const CDirectory &dir)
{
    dir.printMap();
    return os;
}

Once you have this operator, written in either of the two variants, in your main() function you can write:

CDirectory dir("input_file");
std::cout << dir;

Two notes regarding the operator that you already overloaded for Filetype. It is the only other operator that is necessary to be overloaded for your class to be streamable (operator<< is already overloaded for std::string and integer types, so you don't have to do it to be able to write strings and numbers). Also keep in mind that, even though you wrote it inside your class, it is not actually a member function and can't be a member function (why?). You were able to define it there because you declared it friend, but the usual way of defining friend functions is this:

class A {
    ...
    friend void f();
    ...
};

void f() {
    ...
}
Ionut
  • 6,436
  • 1
  • 17
  • 17
  • Thank you, you were extremely helpful. One thing I did not understand completely though - I must keep my overloaded operator << for the `enum class`? I can't just have 1 overloading function, like the one you suggested, where I somehow implement the << operator for the `enum`? Or is there anyway to actually use the strings of the enum (and not the integers) and output them, without having to overload an << operator for it? Thanks in advance and really thank you for the time, I appreciate it! – hopittyhop Feb 23 '16 at 09:44
  • 1
    You have to convert the enum values to strings somehow, and you must do this explicitly (there are languages where you can do this using reflection for example, but not in C++). So you have to either keep the second `operator<<` for your `enum class` or move the entire conversion to `operator<<` for `CDirectory` and keep only this overload. The first option (separate overload for the `enum`) I think is more clean and clear, and it doesn't really make the code longer. – Ionut Feb 23 '16 at 10:07
  • Thank you. I have tried to move the entire conversion in one `operator<<`, but it produces an error. I have updated my answer, can you please take a look, if you have the time for it of course. I think that I got it overall but I'm failing at something. – hopittyhop Feb 23 '16 at 11:20
  • There are a few problems in that code. You're declaring a reference, `type`, without initializing it, which is not possible in C++ (the error message you're getting on that line should be pretty explicit). Then you're still trying to write `p.second` in the stream, which is of your `enum class` type can't be written directly without an operator overload. Instead of that you have to write the correct string. – Ionut Feb 23 '16 at 11:27
  • Hm, so I can't print the enum string with `p->second` and have to use something else? I'm lost. – hopittyhop Feb 23 '16 at 11:31
  • You can't because p.second is not a string, it's a Filetype. But you have that switch statement there, you could build a string with it, give it a try. – Ionut Feb 23 '16 at 11:41
  • I'm trying, but at this point I really don't have a clear view on the things and I'm confused. :( – hopittyhop Feb 23 '16 at 11:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/104283/discussion-between-hopittyhop-and-ionut). – hopittyhop Feb 23 '16 at 11:56
1

I have the impression that you are confused between data members of your class and nested types (member types). Operators are (to be) defined for types, not data members.

You have to overload the operator<< (for stream output) for every class/type for which you want to use it and for which it is not already defined. From your code, I reckon that you only need to overload

ostream& operator<<(ostream& os, Filetype const type)

which you did. So it is unclear what precisely your problem is and what it symptoms are.

Walter
  • 44,150
  • 20
  • 113
  • 196
  • I'm sorry, I guess that I really can't explain good. I want to overload the operator for the whole `class CDirectory` and not only the `enum class Filetype`, which is nested in `CDirectory`. – hopittyhop Feb 23 '16 at 08:52
  • @hopittyhop What will the operator for `CDirectory` do? – rozina Feb 23 '16 at 09:14
  • Well I want to output objects of the `class CDirectory` in my main(). Initialize an object of `CDirectory` and then `cout << obj`. – hopittyhop Feb 23 '16 at 09:18
  • 1
    @hopittyhop: ...which means my (deleted) answer was correct all along: The function `operator<<()` should be *global*, declared `friend` by your class, and either print the member variables' values directly or recursively call the `operator<<` defined for *their* types. – DevSolar Feb 23 '16 at 09:34
0

It seems to me you have everything you need, you just need to put it together.

printMap() function already prints everything in your class CDirectory. It prints everything to cout. So you could just do the following and it should work.

friend ostream& operator<<(ostream& os, const CDirectory& dir)
{
    dir.printMap();
}

However this code is not well written since the opeartor<< is more general and not just made for cout. To make it better you could rewrite your printMap() function to be take a reference to ostream to which you want to output.

void printMap (ostream& os) 
{
     os << m_strDirectory << endl;

     auto p = m_DirectoryMap.begin();
     while ( p != m_DirectoryMap.end()) 
     {
            os << endl << p->first.getFileName() << '\t' << p->first.getFileSize() << '\t' << p->second << endl;
            ++p;
     }
}

void printMap () { printMap(std::cout); }

Now the operator would look like this:

friend ostream& operator<<(ostream& os, const CDirectory& dir)
{
    dir.printMap(os);
}

I hope this better explains what is going on.

rozina
  • 4,120
  • 27
  • 49