Overloadedoperator was fine and now..... nope!

C++ Team, (I guess that's sort of like the A-Team but twice as better!!)

I have an overloaded output stream operator "<<" defined as seen here==>

1
2
3
4
5
6
7
  std::ostream & operator<<(std::ostream& os, const book& cb)
	{
		return os << "title: " << cb.get_title() << "\n"
			<< "Author: " << cb.get_author() << "\n"
			<< "ISBN #: " << cb.get_ISBN() << "\n"
			<< "genre category: " << cb.get_genre() << "\n";
	}	        //end of << overload 


This overloaded operator was working fine until I inserted he last line!

The "genre" you are seeing is an enumerated class for member data and the compiler (VS 2015) was printing this code as an output stream until the addition of this member and the helping function which I pretty much just copied verbatim from the others above (which are working).

NOW, the compiler is saying that trying to obtain cb's genre member data through the function you see is now not matching any overload OR it is not a valid right-hand value for this operation even though it is a pretty standard return of member data. (Which from the previous entries you can tell was working fine)

Any idea of what is going on here?

By having this member as an enumerated class type - is this screwing something up for the overload definition?

Xanadu
I'm assuming you're talking about an enum class. It's a new type. There is no existing overload as there are for the std::string and ints and whatever you printed before.

Probably the best way to deal with it is to make a conversion function that returns a string version of the enum tag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

enum class Stuff {
    Thing, Another, ThisGuy
};

std::string stuffToString(Stuff stuff) {
    switch (stuff) {
    case Stuff::Thing:   return "Thing";
    case Stuff::Another: return "Another";
    case Stuff::ThisGuy: return "ThisGuy";
    }
    return "NoneOfTheAbove";
}

int main() {
    Stuff stuff = Stuff::Another;
    std::cout << stuffToString(stuff) << '\n';
}

Holy crap Dutch!

You're telling me I'm actually doing something somewhat "cutting edge" and I've had my first LEGITIMATE real problem that doesn't bore you all to death? (I might be assuming too much there with the boredom .....)

That's a first!

Ok, so just make a simple helping function (as you've so succinctly shown BTW!!) and just convert the enumeration into a string and call it a day...… I think I can do that (especially since you've painted the picture already!)

Thank you for the quick reply and expert advice!

I'll give that a try and get back to you on how it goes down.

Xanadu


By "new type" I meant a new type in the same sense that any newly-minted class is a new type for which there are no overloads except those which you supply. However, enum classes are new with C++11 and have some nice features relative to the old "leaky" enums (leaking their filthy tags all over the enclosing scope and being basically indistinguishable from ints).
Dutch,

I think I'm following you...…. I thought you meant the operator<< for an enum class was a new TYPE and hence why you suggested the fix you did (Which worked flawlessly BTW!). But from your explanation that isn't what you were saying since an operator is NOT a type...…

I guess I didn't really catch your meaning at first in that by the term "new type" you were simply referring to it in the regular, generic "User Defined Type" sense.


I guess you can't be "cutting edge" if you're too dumb to know the difference! (LOL!)

however, with that said...… I suppose the enum class is still pretty cutting edge for it to merit your suggestion for conversion and printing to string as specified above ......correct?

Am I assuming poorly by thinking: if it wasn't still kinda new they would have already had a generic overload for output stream "<<" in the STD for simply printing it out correct?

Xanadu


The tags are identifiers, just like variable names. There is no (non-preprocessor) way to print variable names. And there's no way to print enum tag names. You can print their integer values by casting, but their names are basically non-existent in the program once it's compiled. Again, just like variable names (although debug compilations contain a symbol table). They're all turned into numbers of one form or another -- addresses, offsets -- or occasionally eliminated altogether.

I think you'll be making string conversion for your enum tags for a long time to come.
Dutch,

I am reading you Lima Charlie (Loud and Clear). So you mentioned a "symbol table" for this sort of thing. Would YOU recommend that a table is the preferable way to do this or would you say the solution you gave is the preferred method - or is it a "6 of one half a dozen of the other" way of handling this and either one is fine and will just really depend on program demands as to which one I choose?

Thoughts?

Xanadu
To be sure we understand the problem, please post the declaration for class book and whatever bool::get_entry() returns.

It could be something as simple as get_entry() not being declared const.
The "symbol table" that I was talking about is a table of mappings from the magic numbers in the program (addresses, offsets, enum tag values) back to the actual "symbols" (identifiers) that you used in your source code. It is added to the executable (or object) file and is not part of the program proper. It's used by linkers and debuggers.

As for whether you would use a switch or, perhaps, an array, it's up to you. Usually if there's just a few tags the switch is good enough. Although you could also use an array, if it seems more natural. I've shown that below.

I also used the preprocessor to do a little trick that allows you to print variable names. This is possible since the macro is replaced at compile time when the names are still known. The # in front of var means to create a string out of it (so if var is number, it's turned into "number"). This is entirely preprocessor text-replacement magic and is quite at odds with the general philosophy of C++, which is to eschew macros whenever possible.

The SIZE tag at the end of the enum tags provides a tag with the integer value equal to the number of tags (except for the SIZE tag itself). So below, SIZE would have integer value 5, which we use to bounds-check the array index.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>

#define dump(var) std::cout << #var ": " << var << '\n';

class Object {
public:
    enum class Greek {
        Alpha, Beta, Gamma, Delta, Epsilon, SIZE
    };

    Object() : alphabet(Greek::Delta) {}

    const char* to_string() const {
        const char* strs[] {"Alpha", "Beta", "Gamma", "Delta", "Epsilon"};
        const int Size = int(Greek::SIZE);
        int i = int(alphabet);
        return i >= 0 && i < Size ? strs[i] : "error";
    }

private:
    Greek alphabet;
};

std::ostream& operator<<(std::ostream& os, const Object& o) {
    return os << o.to_string();
}

int main() {
    Object obj;
    dump(obj);
    int number = 42;
    dump(number);
}

Last edited on
Ok Dutch,

good example. Even though pre-processor "activities" are frowned upon I see what you're doing here. I think your first version is more in-tune with the C++ type-safe and not as "magic" as the pre-processor example you showed but I will definitely stash this away for use if it gets me out of a jam. I just wonder if this will put me in a bind if cross-platform or different compilers comes into play?

dhayden, I definitely declared all of those "fetching" functions as const since I don't intend to have them change any data. However, I will have to post the class tonight if you still want to see it?? Just let me know please and I'll post that.

Thanks again Dutch. C++ really is absolutely boundless and there is too much to know!

But, I figure I'll know some of it by the time I'm done.

Xanadu
Topic archived. No new replies allowed.