 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Thomas Hansen Guest
|
Posted: Tue Oct 26, 2004 1:25 pm Post subject: I have proven Mrs. Liskov to be wrong! (Liskov Substitution |
|
|
NOW I got your attention...
;)
Sorry couldn't resist, but I AM serious, follow up and I'll try to
convince you too...
Regarding this post:
I had some posts earlier in this newsgroup about this particular
subject, but now I have refined my thoughts a bit and I have gotten to
test them in practice and I am becoming more and more convinced about
the usefulness of "Magic Enums"!
In the last 20 years or so we have been thought that in OO to use
Inheritance you MUST have one thing existing in order for a class to
be "inheritable" from another class:
"The IS A relationship" or the Liskov Substitution Principle!
The best example of why is when it is NOT present as in the
Rectangle/Square or Circle/Ellipsis problem...
By definition I agree totally with the Barbara Liskov but I have found
implementation semantics in C++ which will make the Liskov
Substitution Principle "obselete"...
Note that this does only work in C++ and not any other languages (at
least not languages which I know... ;)
Imagine you're having this class AspectSizable which can resize
Widgets, the class comes with several "generic" functions and some
"not so very generic functions"...
class AspectSizable
{
public:
// Implementation left undefined since it's of no importence
void setSize( unsigned posX, unsigned posY, unsigned sizeX, unsigned
sizeY );
void setWidth( unsigned newWidth );
/* etc... */
/* Now here comes the "not so generic functions" */
void maximize();
void minimize();
void restore();
};
The NICE thing about the above class is that it is ONE class for
everything that have to do with sizing of Widgets!
This is nice since they may share implementation and you know where to
find "everythign that has to do with sizing of Widgets"
Now the BAD thing about the above class is that not all Widgets which
can use the AspectSizable class comes with support for being
"restored()", "maximized()" or "minimized()"!
It doesn't make much sense in trying to "maximize()" a WidgetButton
for instance or a WidgetDropDownList...
Q:
So what do we do?
A:
Magic Enums!
Q:
How can we (compile time) inherit a class from only "parts" of the
AspectSizable class?
A:
By using Magic Enums!
Lets declare the WidgetDialog (which SHOULD have support for being
maximized...
class WidgetDialog
: public AspectSizable<WidgetDialog>
// Mark! AspectSizable is now a template class and
// it is "brought" the class inheriting from it in the
// template parameter
{
public:
enum canBeMaximized; // MAGIC ENUM!
/*... rest of class ...*/
};
then lets declare the WidgetButton (which should NOT have support for
being maximized...
class WidgetButton
: public AspectSizable<WidgetButton>
{
public:
// Here there is NO enum which is called canBeMaximized...
*/... rest of class ...*/
};
Now let's "rewrite" the AspectSizable (or parts of it)
template<class WidgetType>
class AspectSizable
{
public:
// I've only included impl. of this one function since
// it's the only relevant for the example
void maximize()
{
// Here we will get a compile time error if the
// WidgetType does not have
// a member called canBeMaximized!!
WidgetType::canBeMaximized;
/* ... implementation of maximize ...*/
}
};
According to the template specifications a member function to a
template class will NOT get compiled unless it is referenced,
basically meaning that we can inherit from AspectSizable in both
WidgetDialog and WidgetButton and as long as we don't explicitly call
"maximize()" it will never be compiled!
Now if you have an object of type WidgetDialog and you call the member
"maximize()" all is well, but if you have a WidgetButton and call
"maximize()" YOUR CODE WON'T COMPILE!!
Just what we wanted!
We have a compile time mechanism to ensure that our code (and
application) behaves correct!!
And off course it comes with ZERO runtime overhead!
(the optimizer of even "the worst" compiler will optimize away the
reference to the Magic Enum)
Q:
But what is the advantage?
A:
You get to use inheritance in a more "natural" way, you can stuff
functions together inside a class which you "feel" are the right
places to put them, thus making the code more intuitive...
After all at the end of the day a Square IS (yes really it IS) a
Rectangle!!
If you ask a mathematician, a child or basically anybody else then a
hardcore OOP developer they will tell you that a Square really IS a
Rectangle!
Everybody in the whole world can assume that a Square is a Rectangle
EXCEPT OO developers...
So you can design your classes in a more intuitive hierarchi since you
have compile time mechanisms to ensure that you don't "mezz up" and
tries to call "maximize()" on your WidgetDropDownList or "fly()" on
your Submarine...
;)
Rewrite your OO books, C++ to the rescue!
If you want to see a concrete implementation of the concept feel free
to look here:
http://cvs.sourceforge.net/viewcvs.py/smartwin/SmartWin/Aspects/AspectSizable.h?rev=1.11&view=markup
And look in the implementation of "restore()" (at the bottom of the
file) and you will see a dereference of the
"WidgetType::MaxiMiniRestorable;" which is only declared in the:
http://cvs.sourceforge.net/viewcvs.py/smartwin/SmartWin/Widgets/WidgetWindowBase.h?rev=1.6&view=markup
class... (which CAN be restored)
While widgetCheckBox (which can NOT be restored):
http://cvs.sourceforge.net/viewcvs.py/smartwin/SmartWin/Widgets/WidgetCheckBox.h?view=markup
does NOT declare the "enum MaxiMiniRestorable;"...
So I get to group things which I feel is natural to have together in
ONE class (AspectSizable) instead of having to temper with TWO
classes...
I think this is pretty awesome!!
PS!
If you think this looks like something you've seen before then I must
admit that Andrei Alexandrescu and Compile Time Assert was the
inspiration of the concept, but allthough Compile Time Assert is very
nifty it can not solve the problems that Magic Enums can solve...
The magic enums gives you the possibility of creating UDT union
classes...
In fact it would be possible (theoretically, I don't say it's
feasable) to take all your classes in your > 1MILL classes application
and merge every class together into ONE hugh template class and just
instantiate that one single class with different template parameters
(consisting of policy classes with lots of Magic Enums) without
loosing typesafety at all!!
(all though I would buy lots and lots of painkillers and neuroleptica
before doing that if I were you... )
Thomas Hansen
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Michael Kurz Guest
|
Posted: Tue Oct 26, 2004 11:29 pm Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitut |
|
|
"Thomas Hansen" <polterguy (AT) gmail (DOT) com> schrieb im Newsbeitrag
news:101a1348.0410250652.76b2c74f (AT) posting (DOT) google.com...
| Quote: | NOW I got your attention...
;)
In the last 20 years or so we have been thought that in OO to use
Inheritance you MUST have one thing existing in order for a class to
be "inheritable" from another class:
"The IS A relationship" or the Liskov Substitution Principle!
|
No. Somehow a lot of stuff I want to argue about leads to "Modern C++
Desgign" from Andrei Alexandrescu.
So I think its really one of the most important books I have ever read about
programming (C++).
So the LSP with the "IS A" is one interpretation (and usage) for public
inheritance.
The policy based design is another one. :-)
So IMHO nothing new about that LSP is not a strict rule. It's only a
guidline (think) model for lots of situations.
It's like it is in many (all) sciences, humans work (and think) with (in)
models. The only thing you need to know is when a model is applicable and
when it's not.
Kind Regards
Michael
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Victor Bazarov Guest
|
Posted: Tue Oct 26, 2004 11:30 pm Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitut |
|
|
Thomas Hansen wrote:
| Quote: | NOW I got your attention...
;)
Sorry couldn't resist, but I AM serious, follow up and I'll try to
convince you too...
[...]
|
template<class U> struct UniversalBaseForAll {};
struct MyFancyClass : UniversalBaseForAll<MyFancyClass> {};
struct MyOtherFancyClass : UniversalBaseForAll<MyOtherFancyClass> {}
With this setup MyFancyClass and MyOtherFancyClass are not related.
Their respective base classes are not the same. One _cannot_ say that
UniversalBaseForAll is the base for these classes simply because the
UniversalBaseForAll is not a class. It's a class of classes.
Liskov substitution principle relates to polymorphism achieved through
[pointers or references to] _objects_. In your case there can be no
function that expects UniversalBaseForAll object because there can be
no such _object_.
You could, of course, create a function _template_:
template<class T> void foo(UniversalBaseForAll<T>& ubtT);
but, alas, after your code is compiled, there is not one but as many
'foo' functions as there are instantiations for UniversalBaseForAll,
and they cannot be cross-used.
Start convincing.
V
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Balog Pal Guest
|
Posted: Wed Oct 27, 2004 1:00 am Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitut |
|
|
"Thomas Hansen" <polterguy (AT) gmail (DOT) com> wrote
| Quote: | Sorry couldn't resist, but I AM serious, follow up and I'll try to
convince you too...
|
Convince about what?
okey, you presented an interesting trick, let's assume it actually works
after some fixing. But how that all relates to LSP or inheritance?
In your solution there is no single base class, but every wigget has its own
separate base class with the functions it supports. The fact that the base
classes are generated from a single template do not make them related in any
way, just like vector<int> has nothing to do OO-wise with vector<double>,
or even vector<Base> with vector<Derived> where Derived is subclass of Base.
| Quote: | After all at the end of the day a Square IS (yes really it IS) a
Rectangle!!
|
Did you show that? If you can, please do. Show the code.
Paul
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thomas Hansen Guest
|
Posted: Fri Oct 29, 2004 2:06 am Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitu |
|
|
"Michael Kurz" <mkurz (AT) move-multimedia (DOT) com> wrote
| Quote: | "Thomas Hansen" <polterguy (AT) gmail (DOT) com> schrieb im Newsbeitrag
news:101a1348.0410250652.76b2c74f (AT) posting (DOT) google.com...
NOW I got your attention...
;)
In the last 20 years or so we have been thought that in OO to use
Inheritance you MUST have one thing existing in order for a class to
be "inheritable" from another class:
"The IS A relationship" or the Liskov Substitution Principle!
No. Somehow a lot of stuff I want to argue about leads to "Modern C++
Desgign" from Andrei Alexandrescu.
So I think its really one of the most important books I have ever read about
programming (C++).
So the LSP with the "IS A" is one interpretation (and usage) for public
inheritance.
The policy based design is another one.
|
You are off course totally right here, the basic idea is just to free
you from a mindsetting prison from which you can only create classes
that guarantees to fullfill _ONE_ single task to perfection...
Now you could have one "general" class called e.g. RoundishThing with
functions like "setRadius()", "setMajorAxis()", "setMinorAxis()",
"setVolum()" etc...
While this doesn't make much sense alot of the reason it isn't is
probably that you are not USED to thinking of classes like that...
We're being taught up thinking that ONE CLASS == ONE PROBLEM when
maybe in some areas that just doesn't apply, often it would be
mindfreeing to let one class solve more then one problem...
One example was my AspectSizable another example would be Circle and
Ellipsis or Rectangle and Square...
"If all you have is a hammer, everything will look like nails..."
As for me I'm a "religious believer" of the "Once And Once Only"
saying and I must say that the LSP (as I have been raised to look at
it, one class == one problem) is restricting the inheritance tree ALOT
for me very often since I don't want to copy/paste code, and the Magic
Enums can solve if not all problems but very many of the problems
related to both copy/paste and "weird inheritance tree"...
Now if you don't believe me as to getting weird inheritance trees /
copy/paste coding try to implement a Circle and an Ellipsis class and
see either how many more (functional) classes you're gonna get or how
much more copy/paste code it will generate for you...
HINT!
The rendering implementation or the calculateArea etc. of those
classes would normally be more or less exactly equal...
Try to implement them once without the magic enums and once with the
Magic Enums and see for yourself what gives the cleanest code and MOST
code and the cleanest interface. (public member functions)
However your argument about those classes not being interchangeable is
off course totally right, but you save code, you get a cleaner
inheritance tree and you get less copy/paste coding...
So at the end of the day Magic Enums have given you an opportunity to
be more flexible in your design and more freedom in how you percieve
your classes...
Nothing more, nothing less...
| Quote: |
So IMHO nothing new about that LSP is not a strict rule. It's only a
guidline (think) model for lots of situations.
It's like it is in many (all) sciences, humans work (and think) with (in)
models. The only thing you need to know is when a model is applicable and
when it's not.
|
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thomas Hansen Guest
|
Posted: Fri Oct 29, 2004 2:07 am Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitu |
|
|
Victor Bazarov <v.Abazarov (AT) comAcast (DOT) net> wrote
[snip]
| Quote: | but, alas, after your code is compiled, there is not one but as many
'foo' functions as there are instantiations for UniversalBaseForAll,
and they cannot be cross-used.
Start convincing.
|
Off course there are several classes being compiled into the finishing
image, but the point is that you have a class which instead of
polimorphically allows for different DEFINITIONS (Implementations, as
in virtual functions) allows for different "DECLARATIONS" (since you
can't use some of the functions)
The whole idea about the cconcept is just to "surgically remove" the
allowance of using specific member functions since the "contract"
don't allow using them...
I think it's a mind freeing concept giving opportunities for freedom
in design which otherwise would have been virtually impossible to
achieve...
BTW!
[moderator]
Where is my original post??
{Moderator's note: are you referring to this article:
<http://groups-beta.google.com/group/comp.lang.c++.moderated/msg/601b68057e86aeee?dmode=source>?
It seems to be there at google, at least using the new interface. I
also haven't found it using the old interface but this has to be a
problem at google. -mod/dk}
I have searched in google groups and it's just not there!!
Am I "banned" from this group or something??
I have seen this happening several times that people have responded to
my posts and then I couldn't find my original post...
For me it feels like the post is being submitted then later someone
gets second thoughts and pulls it down but not fast enough for
somebody to respond to it leaving "small traces of existance"...
{Moderator's note: nobody is banned from comp.lang.c++.moderated.
Also, the only cases where articles are cancelled after being approved
is when the author asks for it or when it was a gross error (although
we normally don't bother as cancel messages are ignored by most
news servers anyway). -mod/dk}
Thomas Hansen
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thomas Hansen Guest
|
Posted: Fri Oct 29, 2004 2:13 am Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitu |
|
|
"Balog Pal" <pasa (AT) lib (DOT) hu> wrote
| Quote: | "Thomas Hansen" <polterguy (AT) gmail (DOT) com> wrote in message
news:101a1348.0410250652.76b2c74f (AT) posting (DOT) google.com...
Sorry couldn't resist, but I AM serious, follow up and I'll try to
convince you too...
Convince about what?
okey, you presented an interesting trick, let's assume it actually works
after some fixing.
|
What fixing?
| Quote: | But how that all relates to LSP or inheritance?
|
In implementation it does since you can have one class that doesn't
necesarely allow different "instantiations" to use the "whole"
class...
But you are off course right in that you can't substitute one (of the
instantiations) for another (that carries a different contract)
| Quote: |
In your solution there is no single base class, but every wigget
^^^^^^ |
hmm...
;)
| Quote: | has its own
separate base class with the functions it supports. The fact that the base
classes are generated from a single template do not make them related in any
way, just like vector<int> has nothing to do OO-wise with vector<double>,
or even vector<Base> with vector<Derived> where Derived is subclass of Base.
|
Yet again you are still totally right, however the big idea about it
is that you can merge three classes into "one" (if you percieve one
template class like one class)
In the alternative you would have one base class for the common things
and two classes (if you had specializations also for the
non-maximizable types) for each specialized use of the
AspectSizable... (with each class carrying implementation and being
complex types)
The Magic Enums gives you three classes also, but two of them are
trivial and of no importence other then giving the template class a
"contract" that "declares" the different aspects of that class you are
allowed to use!
Just imagine how to e.g. write a copy CTOR to the alternative solution
with three "functional" classes compared to writing a COPY ctor to the
Magic Enum template class...
(hint, object slicing... )
And even in the alternative solution polymorphism works AGAINST you
since you often don't want to be able to substitue an instance af a
WidgetSizableBase with all the different derived classes (which will
not be a problem in the Magic Enum construct)
There is probably ALOT of other aspects which complifies the "three
functional classes" solution...
The whole idea of the construct is that you get WAY less code and WAY
more intuitive class design and WAY more "automatic" safety (I
mentioned two "object slicing" and polymorphism)
| Quote: |
After all at the end of the day a Square IS (yes really it IS) a
Rectangle!!
Did you show that? If you can, please do. Show the code.
|
| Code: |
#include <iostream>
class Square
{
protected:
static enum canSetSize;
};
class Rectangle
{
static enum canSetWidth;
static enum canSetHeight;
};
template<class SquarishType>
class SquarishObject
{
int itsWidth;
int itsHeight;
public:
void setWidth( int newSize )
{
SquarishType::canSetWidth;
itsWidth = newSize;
}
void setHeight( int newSize )
{
SquarishType::canSetHeight;
itsHeight = newSize;
}
void setSize( int newSize )
{
SquarishType::canSetSize;
itsWidth = itsHeight = newSize;
}
int calculateArea()
{
return itsWidth * itsHeight;
}
};
int main()
{
SquarishObject<Rectangle> rect;
rect.setWidth(5);
rect.setHeight(10);
std::cout << rect.calculateArea();
SquarishObject<Square> square;
square.setSize(8);
std::cout << square.calculateArea();
}
|
Off course here the Liskov Substitution Princip still applies, you
can't substitute one for another and the square technically has one
data member "too much" so this is probably not a good example of the
Magic Enums, I think the AspectSizable is probably a better example.
But you are right in the pure sense since you can't substitute one for
another unless you wrote a template function only utilizing the common
functions (usable by every concrete of type SquarishObject) or derived
from some sort of interface class (abstract base class)
But still I think it raises enough questions to make people able to
ask questions about LSP and maybe not coming up with the same answer
we've had before...
Not to mention that no matter how you count template classes you would
not get away (unless using copy/paste) with less then three classes
for a Circle/Ellipsis classes if you wanted to also being able to have
common operations on them (calculateArea)
I would believe that not using Magic Enums but instead using the
traditional way for the above code would at least generate three times
as much code as the Magic Enum concept without giving you any gain at
all...
Thomas Hansen
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Craig Henderson Guest
|
Posted: Fri Oct 29, 2004 2:18 pm Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitut |
|
|
"Thomas Hansen" <polterguy (AT) gmail (DOT) com> wrote
Hi Thomas,
I understand your goal, but I wonder if you're making life more complex than
it needs to be. It's a neat trick, but the compilation errors are will show
the line of reference to WidgetType::canBeMaximized rather than the real
error line, which is the point at which the 'user' calls maximize() on a
concrete class that cannot be maximised.
| Quote: | Now let's "rewrite" the AspectSizable (or parts of it)
template<class WidgetType
class AspectSizable
{
public:
// I've only included impl. of this one function since
// it's the only relevant for the example
void maximize()
{
// Here we will get a compile time error if the
// WidgetType does not have
// a member called canBeMaximized!!
WidgetType::canBeMaximized;
/* ... implementation of maximize ...*/
}
};
|
We can use scope restriction to accomplish the same thing, but eliminate the
enums and get the compiler to report the error where the method is called,
and not an error inside the implementation of a library class.
template
class AspectSizable
{
protected:
void maximize();
};
class WidgetDialog
: public AspectSizable<WidgetDialog>
{
public:
using AspectSizable<WidgetDialog>::maximize();
};
Without the 'using' line, maximize() will be unavailable through the
WidgetDialog class, and it is protected in the base class, so is
inaccessible through polymorphism. IMO, this is a neater solution, and I
don't see that the 'Magic Enums' gives anything extra?
-- Craig
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thomas Hansen Guest
|
Posted: Fri Oct 29, 2004 2:19 pm Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitut |
|
|
| Quote: | Sorry couldn't resist, but I AM serious, follow up and I'll try to
convince you too...
Convince about what?
okey, you presented an interesting trick, let's assume it actually
works
after some fixing.
|
Sorry, I just discovered the bug, the magic enum in my OP should have
been static...
;)
Thomas
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
JKop Guest
|
Posted: Fri Oct 29, 2004 2:42 pm Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitut |
|
|
How about:
class AspectSizable
{
public:
// Implementation left undefined since it's of no importance
void setSize( unsigned posX, unsigned posY, unsigned sizeX, unsigned
sizeY );
void setWidth( unsigned newWidth );
/* etc... */
};
class AspectSizeable_CanMaximize : virtual public AspectSizeable
{
void Maxamize();
};
class AspectSizable_CanMinimize : virtual public AspectSizeable
{
void Minimize();
};
class AspectSizeable_CanRestore : virtual public AspectSizeable
{
void Restore();
};
class WidgetDialog : virtual public Aspect_Sizeable, virtual public
AspectSizeable_CanMaxamize, virtual public AspectSizeable_CanMinimize,
virtual public AspectSizeable_CanRestore {};
or something along those lines...
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Terje Slettebų Guest
|
Posted: Sun Oct 31, 2004 10:53 am Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substit |
|
|
"Thomas Hansen" <polterguy (AT) gmail (DOT) com> wrote
| Quote: | "Balog Pal" <pasa (AT) lib (DOT) hu> wrote in message
news:<2u7e9rF27dnvuU1 (AT) uni-berlin (DOT) de>...
"Thomas Hansen" <polterguy (AT) gmail (DOT) com> wrote in message
news:101a1348.0410250652.76b2c74f (AT) posting (DOT) google.com...
After all at the end of the day a Square IS (yes really it IS) a
Rectangle!!
Did you show that? If you can, please do. Show the code.
|
Which compiler did you test this on? I get the following errors on Intel C++
7.1 and g++ 3.2:
| Quote: | | Code: |
#include <iostream
class Square
{
protected:
static enum canSetSize; // g++ 3.2: use of enum `canSetSize' without
previous declaration
};
class Rectangle
{
static enum canSetWidth;
static enum canSetHeight;
};
template<class SquarishType
class SquarishObject
{
int itsWidth;
int itsHeight;
public:
void setWidth( int newSize )
{
SquarishType::canSetWidth; // Intel C++ 7.1: error: class "Rectangle"
has no member "canSetWidth"
itsWidth = newSize;
}
void setHeight( int newSize )
{
SquarishType::canSetHeight; // Intel C++ 7.1: error: class "Rectangle"
has no member "canSetHeight"
itsHeight = newSize;
}
void setSize( int newSize )
{
SquarishType::canSetSize; // Intel C++ 7.1: error: class "Square" has
no member "canSetSize"
itsWidth = itsHeight = newSize;
}
int calculateArea()
{
return itsWidth * itsHeight;
}
};
int main()
{
SquarishObject<Rectangle> rect;
rect.setWidth(5);
rect.setHeight(10);
std::cout << rect.calculateArea();
SquarishObject<Square> square;
square.setSize(8);
std::cout << square.calculateArea();
}
|
|
Furthermore, your object carries always both width and height data members,
even for a square where only one is needed. If you have a Square class, with
"size" datamember, and a Rectangle class, with "width" and "height"
datamembers, you don't risk any of the member functions accidentally setting
the object in an invalid state (like width!=height for a square).
| Quote: | Not to mention that no matter how you count template classes you would
not get away (unless using copy/paste) with less then three classes
for a Circle/Ellipsis classes if you wanted to also being able to have
common operations on them (calculateArea)
|
In the above, you have two classes and one class template, whereas in the
obvious implementation given below, there are two classes and less code. The
objects in your code takes more space than needed (which would get worse the
more concepts are mixed together in one class), and you mix together
concepts in a class, thus giving the classes more than one responsibility.
Several member functions will typically be "dead weight", compilation wise,
which may increase compilation time, as well. Not my idea of a win.
#include <iostream>
class Square
{
public:
void setSize( int newSize )
{
itsSize = newSize;
}
int calculateArea()
{
return itsSize * itsSize;
}
private:
int itsSize;
};
class Rectangle
{
public:
void setWidth( int newSize )
{
itsWidth = newSize;
}
void setHeight( int newSize )
{
itsHeight = newSize;
}
int calculateArea()
{
return itsWidth * itsHeight;
}
private:
int itsWidth;
int itsHeight;
};
int main()
{
Rectangle rect;
rect.setWidth(5);
rect.setHeight(10);
std::cout << rect.calculateArea();
Square square;
square.setSize( ;
std::cout << square.calculateArea();
}
| Quote: | I would believe that not using Magic Enums but instead using the
traditional way for the above code would at least generate three times
as much code as the Magic Enum concept without giving you any gain at
all...
|
Count the number of lines in the classes... It's less than your code. I also
find the code clearer, this way. No "magic" anything.
Regards,
Terje
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
M Jared Finder Guest
|
Posted: Sun Oct 31, 2004 11:01 am Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitu |
|
|
Thomas Hansen wrote:
| Quote: | Victor Bazarov <v.Abazarov (AT) comAcast (DOT) net> wrote in message
news:<QPsfd.6934$Ae.4775 (AT) newsread1 (DOT) dllstx09.us.to.verio.net>...
[snip]
but, alas, after your code is compiled, there is not one but as many
'foo' functions as there are instantiations for UniversalBaseForAll,
and they cannot be cross-used.
Start convincing.
Off course there are several classes being compiled into the finishing
image, but the point is that you have a class which instead of
polimorphically allows for different DEFINITIONS (Implementations, as
in virtual functions) allows for different "DECLARATIONS" (since you
can't use some of the functions)
The whole idea about the cconcept is just to "surgically remove" the
allowance of using specific member functions since the "contract"
don't allow using them...
I think it's a mind freeing concept giving opportunities for freedom
in design which otherwise would have been virtually impossible to
achieve...
|
If the functions don't all act on one data structure, and the base class
that is generated isn't useful as a shared base class, why even group
the functions into a class? C++ isn't like Java where all functions
have to be members of some class.
You could do the same thing without the derivation, in which case you're
writing algorithms in the style of the STL (but with readable error
messages). I find this clearer for your example as I think of a class
being "a set of data with an invariant" or "a logically cohesive
interface", and the AspectSizable class is neither.
-- MJF
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thomas Hansen Guest
|
Posted: Sun Oct 31, 2004 11:18 am Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitut |
|
|
Hi Craig, you are "almost" right and it probably might have several
advantages that the Magic Enums don't have, this only proves the
flexibility of the language, one problem a zillion solutions, but...
I am not sure if you ment the inheritance from AspectSizable to be
private, if you did not you can still inherit from the WidgetDialog and
use "maximize()" in derived classes (which in SmartWin is "the way to
use widgets" and exposes non functional member functions)
Still if you make the inheritance private and declares using maximize
in WidgetDialog you're in trouble...
For one you have a solution which will "default to allowance" for the
next widget you create and derive from AspectSizable, while the Magic
Enums will "default to deniance", kind of like being "secure by
default"...
Secondly, AspectSizable again may inherit from other classes which you
want to expose as public, this will make for a lot of using
statements...
At the end of the day the Magic Enums are declarative and you have to
intentionally mezz them up to do something wrong with them while the
private inheritance/using pattern is not and you will have to
explicitly make code around them to make them "safe"...
Not to mention the gain you can get if you bring this into a hierarchy
of classes where you can "pass the Policy Class around" to give other
classes access to the policy...
Plus you can derive policy classes from other policy classes...
Plus you can choose if you want to inherit from the Magic Enum
container class which gives you the opportunity to put code into them
(you can off course anyway)...
Example:
struct AllowMaximizing
{
enum can Maximize;
};
struct AllowMinimizing : public AllowMaximizing
{
enum canMinimize;
};
struct AllowDissappearing : public AllowMinimizing
{
void makeDissappear();
};
template<class VisibilityPolicy>
class AspectReallyFancy : public VisibilityPolicy
{
void minimize()
{
canMinimize;
/*... implementation ...*/
}
void maximize()
{
canMaximize;
/*... implementation ...*/
}
// We also get the makeDissappear here if policy is of right type...
};
class WidgetEverything : public AspectReallyFancy<AllowDissappearing>
{/*...*/};
or...
class WidgetAlmostEverything : public
AspectReallyFancy<AllowMinimizing>
{/*...*/};
class WidgetTheLeast : public AspectReallyFancy<AllowMaximizing>
{/*...*/};
or this one which is one of my favourites...
;)
template<class CustomPolicy>
class WidgetSomethingUnknownDefinedByUser : public
AspectReallyFancy<CustomPolicy>
{/*...*/};
Not to mention that you can make your own ones and combine the ones you
want to support...
struct MyAspectPolicy
{
enum canMinimize;
};
class WidgetCustom : public AspectReallyFancy<MyAspectPolicy>
{/*...*/};
It's another card up your slieve, it makes you able to do some things
more clean and easy then you could do otherwise...
Think of the possibilities you gain if you think of them as union
objects...
struct Int
{
enum Int;
};
struct String
{
enum String;
};
struct Complex
{
enum Complex;
};
struct ComplexString
{
enum Complex;
enum String;
};
template<class Type>
class OsCustomClipBoardRetriever : private Type
{
public:
Complex getClipboardComplex()
{
Complex;
/*...*/
}
String getClipboardString()
{
String;
/*...*/
}
Int getClipboardInt()
{
Int;
/*...*/
}
};
Now if you think that you might achieve the last one with a pure
template class with only the type of object retrievable from clipboard
you are "kind of right" until you realize that the clipboard might have
some kind of builtin functionality of getting e.g. a Complex out as
both a Complex AND a string while the Int can only come out as an Int
etc...
I think Magic Enums are really cool once you have "shaken off" some of
the cold from the past which denies you to do things like "combining
classes" since it's considered bad because it breaks alot of unspoken
laws like cohesion, encapsulation etc...
Fact is if you start applying them you'll notice they actually
increases cohesion, encapsulation, OO and even ** LSP **...
In fact of the last 15 problems I have had in SmartWin i have solved __
5 __ of them with Magic Enums...
Not because I wanted to "show of" or something, just because they
happened to be the best tool for the job!!
Thomas
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thomas Hansen Guest
|
Posted: Mon Nov 01, 2004 11:54 am Post subject: Re: I have proven Mrs. Liskov to be wrong! (Liskov Substitut |
|
|
I didn't try to compile it, it was not my intention to produce correct
code, it was not my intention to prove that it would give less code or
less overhead then the alternative solution presented by you.
My intention was only to prove that a rectangle COULD be a square
without breaking LSP, and if you fix some bugs in the code I am pretty
sure that most people will se that I have managed that...
I am sorry if I didn't manage to convince you however...
The places where I have found them to be particulary useful though is
in interop with OS functions (they probably apply usefulness though to
ALL libraries/API's written in C, Corba or COM though), like for
instance a HWND in Windows API you have NO WAY to tell compile time if
it's a Button Control, Drop Down List control or a Dialog, the only
difference is that they are created with different parameters
(CreateWindowEx( "EditControl" ), CreateWindowEx( "DropDownList")...
While a dialog can be maximized this doesn't make much sense in a Drop
Down List.
And in my class hierarchy in SmartWin (http://smartwin.sourceforge.net)
I use them as Policies to give widgets access to some (but not all,
depending on their inner type) functions which are wrappers around
Windows API functions.
The only datamember you have access to there is anyway only a HWND and
nothing else, and out from that HWND data member you are supposed to
let the user of the library call Windows API functions.
Some of them are universal for all HWND's while others only make sense
for some specific types of HWND's (which you can't know during compile
time)
In such a scenario I cannot see a concept which is better or carries
less overhead or less code then the Magic Enums...
It's actually quite stunning that SmartWin is almost a complete GUI
library for Windows with less then 15000 lines of code...
And btw, regarding the Rectangle/Ellipsis classes I think that mostly
these days they are being used anyway as wrappers around DirectX or
OpenGL or some other API where you also only have access to a "handle"
of some sort which is the same for a flat circle and a complete 3D
human being with joints and animations.
In such a scenario too I think that the Magic Enums would give you
serious gain in restricting the user by not letting him get to call
"Fly()" on a "handle" which is pointing to a submarine...
Thomas
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|