
Jake Voytko wrote:
p << image_size (800, 600) << show_axis << x_axis_color( blue ) << y_axis_color( darkgray ) << x_axis_major_tick_color( gray) << y_axis_major_tick_color( darkgray ) << x_axis_minor_tick_color( gray ) << y_axis_minor_tick_color( gray ) << show_background_border << background_border_color ( darkgray ) << background_border_thickness( 5 ) << background_color ( lightblue )
I not at all keen on this. I'll try to explain why. Consider any of the textbook examples of classes, and how they can (or can't) be extended: - I want something that's like std::string, but I'd really like to be able to have void std::string::toLowerCase(void) { .... } - I want something like textbook::animal, but I need bool textbook::animal::has_four_legs() { return num_legs()==4; } - I want something like textbook::solidshape, but I need int num_holes_through_shape(void) { .... } - I want something like mylibrary::Date, but I need dayofweek mylibrary::Date::day_that_month_started_on() { ..... } So wanting to take a class and extending it is very common, and in the cases of interest these extensions are all things that can be implemented using the public interface of the existing class. There are basically two ways in which it can be done: 1. Create your own subclass which inherits from the thing you're trying to extend and adds the new methods. This gets a bit tricky if you want to add multiple independent methods to the same base class, and any methods in the base class that return things of their own type will return the base class not the subclass. So it's probably not the right thing to do in many cases. 2. Write free functions instead of methods. i.e. instead of writing if (the_animal.has_four_legs()) { ... } you have to write if (has_four_legs(the_animal)) { ... } The only real problem with this way is the you have to remember whether each 'method' is a real method of the base class or a free function. One option for a class that expects to be extended is to put *everything* in free functions, having a minimal set of invariant-preserving public methods that access the private data. (You can even do without them using friend declarations.) Let's think about iostreams for a moment. There the need is to be able to extend the ostream class with new methods for formatted output of new types. Essentially what it does is: ostream strm(fn); int i; char c; std::string s; somo_other_type z; write(strm,i); write(strm,c); write(strm,s); write(strm,z); i.e. free functions for each extension, except that the library designers chose not to use a conventional function name like 'write', but instead chose operator<<. Is this a good choice? Well, there is a famous quote somewhere (would anyone like to remind me who it was) something like "well I gave up on C++ when I saw them doing a left shift on the standard output". It's a jokey quote, but adding extra "magic" syntax is something that makes what's really going on in the program less transparent. It doesn't take at all long for someone to learn that A.B(C) means "pass C as the parameter to method B of object A" (and it helps that it is essentially the same in most OO languages). But you need to get a lot further in to your textbook to understand what is really going on when you write A<<B<<C. Of course the advantage of operator overloading is that the code can be a bit sorter. In the case of iostreams, the library is widely used and early-learnt, and so some increased learning curve has been traded off against saved keystrokes. Now to your code:
p << image_size (800, 600) << show_axis << x_axis_color( blue ) << y_axis_color( darkgray ) << x_axis_major_tick_color( gray) << y_axis_major_tick_color( darkgray ) << x_axis_minor_tick_color( gray ) << y_axis_minor_tick_color( gray ) << show_background_border << background_border_color ( darkgray ) << background_border_thickness( 5 ) << background_color ( lightblue )
My first impression on seeing that is that this _is_ the iostreams overloading of operator<<. For example, a library that rendered text into a bitmap image might subclass ostream so that you could write: bitmap_text_buffer b; int i; some_type_with_operator<<_defined X; b << at(50,50) << "hello world" << X; My second impression is that your class is some sort of non-iostream stream and that each '<<something' is putting something into the stream. (SVG is XML, so maybe each one is adding an element to the XML output.) But no, on further inspection it isn't even doing that: the plot class does not behave as a stream. In fact you're using << purely as a way to save keystrokes. You're just using A<<B(C) as shorthand for B(A,C). In fact it would actually be less confusing to use a different operator: p % image_size (800, 600) % show_axis % x_axis_color( blue ) % y_axis_color( darkgray ) % x_axis_major_tick_color( gray) % y_axis_major_tick_color( darkgray ) % x_axis_minor_tick_color( gray ) % y_axis_minor_tick_color( gray ) % show_background_border % background_border_color ( darkgray ) % background_border_thickness( 5 ) % background_color ( lightblue ) ('operator,' anyone?) So let me write it out in full, as I would probably implement it: image_size (p, 800, 600); show_axis(p); x_axis_color(p, blue ); y_axis_color(p, darkgray ); x_axis_major_tick_color(p, gray); y_axis_major_tick_color(p, darkgray ); x_axis_minor_tick_color(p, gray ); y_axis_minor_tick_color(p, gray ); show_background_border(p); background_border_color (p, darkgray ); background_border_thickness(p, 5 ); background_color (p, lightblue ); Now what is so special about this particular library that that isn't good enough? (Please don't tell me that "it doesn't look as nice". Looking good is not what programs should be judged by.) In summary: - You don't need to overload operator<< to make your class extensible. - Operator overloading makes the library harder to learn because what's going on is less transparent. Regards, Phil.