From octave-maintainers-request at octave dot org Thu Apr 22 12:38:47 2004 Subject: Octave_value class hierachy From: "John W. Eaton" To: Jens Ruecknagel Cc: octave-maintainers-list Date: Thu, 22 Apr 2004 12:37:34 -0500 --hMH3LCQKts Content-Type: text/plain; charset=us-ascii Content-Description: message body text Content-Transfer-Encoding: 7bit On 22-Apr-2004, Jens Ruecknagel wrote: | Why is count a attribute of octave_value? Why is ref *octave_value? | Shouldn't it be: count attribute of octave_base_value and | octave_value::ref* octave_base_value? | Wouldn't it be better to make octave_value the proxy class and | octave_base_value the referenced class? It is used this way anyway? | | I'm just curios? Does anybody know the reason, I did not think of? I think I followed the structure of the Number class hierarchy in Coplien's "Advanced C++; Programming Styles and Idioms" book from 1992. I must admit that at the time, that code seemed quite complex to me, so probably I copied a lot more than I really understood. Looking at the code now, I think the rep object must be a pointer to an octave_value object so that the virtual functions in the octave_value class work properly. If you see another way to do this that is better, then please submit a complete working example that demonstrates your ideas. It can be greatly simplified compared to the octave_value classes (for example, just two derived types and one or two operations on them). You could start with the code in the first attachment below, and fix it so that it works correctly. Currently, both calls to the functions in main end up in the methods of base_value class when only the one for type_two should. When you are done, I think you will probably end up with something much like what is in the second attachment, which is a simplified version of the current octave_value hierarchy. But maybe there is a simpler and better way that I'm not seeing. In any case, going through the exercise yourself will probably help you to understand why things are the way they are. jwe --hMH3LCQKts Content-Type: text/plain Content-Disposition: inline; filename="foo.cc" Content-Transfer-Encoding: 7bit #include class base_value; class value { public: value (int t = 0); value (const value& v); value& operator = (const value& v); virtual ~value (void); virtual value fun (void) const; private: base_value *rep; }; class base_value { friend class value; public: base_value (void) { count = 1; } value fun (void) const { std::cerr << "base_value::fun" << std::endl; return value (); } private: int count; }; class type_one : public base_value { public: type_one (void) : base_value () { } value fun (void) const { std::cerr << "type_one::fun" << std::endl; return value (); } }; class type_two : public base_value { public: type_two (void) : base_value () { } // Let the base class handle this one... // value fun (void) const }; value::value (int t) { switch (t) { case 1: rep = new type_one (); break; case 2: rep = new type_two (); break; default: rep = new base_value (); break; } } value::value (const value& v) { rep = v.rep; rep->count++; } value& value::operator = (const value& v) { if (this != &v) { if (--rep->count == 0) delete rep; rep = v.rep; rep->count++; } return *this; } value::~value (void) { if (--rep->count == 0) delete rep; } value value::fun (void) const { return rep->fun (); } int main (void) { value one (1); value two (2); // Should call type_one::fun one.fun (); // Should call base_value::fun, because type_two does not // implement fun. two.fun (); return 0; } --hMH3LCQKts Content-Type: text/plain Content-Disposition: inline; filename="bar.cc" Content-Transfer-Encoding: 7bit #include class base_value; class xvalue { public: xvalue (void) { } }; class value { public: value (int t = 0); value (const value& v) { rep = v.rep; rep->count++; } value& operator = (const value& v) { if (this != &v) { if (--rep->count == 0) delete rep; rep = v.rep; rep->count++; } return *this; } virtual ~value (void) { if (rep && --rep->count == 0) { delete rep; rep = 0; } } virtual value fun (void) const { return rep->fun (); } private: union { value *rep; int count; }; protected: value (const xvalue&) : rep (0) { } }; class base_value : public value { public: base_value (void) : value (xvalue ()) { } base_value (const base_value&) : value (xvalue ()) { } ~base_value (void) { } value fun (void) const { std::cerr << "base_value::fun" << std::endl; return value (); } }; class type_one : public base_value { public: type_one (void) : base_value () { } ~type_one (void) { } value fun (void) const { std::cerr << "type_one::fun" << std::endl; return value (); } }; class type_two : public base_value { public: type_two (void) : base_value () { } ~type_two (void) { } // Let the base class handle this one... // value fun (void) const }; value::value (int t) { switch (t) { case 2: rep = new type_two (); rep->count = 1; break; default: rep = new type_one (); rep->count = 1; break; } } int main (void) { value one (1); value two (2); // Should call type_one::fun one.fun (); // Should call base_value::fun, because type_two does not // implement fun. two.fun (); return 0; } --hMH3LCQKts--