The merits of dynamic_cast<>()

N

Noah Roberts

True, but the OP did not like that approach, and was asking for an
alternative. This alternative replaces dynamic_cast followed by a method
call with only the method call. But doing so violates some kind of an
utopian design goal, apparently, so we're told it's no good because of that.
See, because -- according to our learned mentors -- some lofty design
principle gets violated, we shouldn't use it. Never mind that the result is
cleaner, more readable code. We can't use it. It's not ideologically pure.

The problem I see with your mentality is that you stopped looking for
cleaner alternatives.

I've seen code like you suggest people do here and it is NOT cleaner nor
more readable. When it comes time to add some new class you have to
track down each and every part of the monolythic monstrosity you're
inheriting from and tell if it's got a reasonable default or will throw.
You might claim this to be a "junior developer" problem but in fact it
costs hours of time for anyone that has to deal with such ugliness.

Further, you have to check ALL client code that attempts to make calls
to the functions you have no reasonable definition for to make sure they
do the appropriate checking. They'd better be checking their types
before making calls (which sort of negates the entire point you're
saying this design has) and not making those calls when the type is
wrong. If they do not then you need to come up with some "reasonable"
definition when you don't have one or hope they're handling the
exception (which you claim in not necessary).

It's a horrible, horrible practice that may get the job done for you,
but when people have to maintain such garbage it costs hours and hours
in extra development time and debugging effort. I ton and a half of
copy/paste code would be better.

But, the really fascinating thing here is that you just stopped at
seeing one of two alternatives: dynamic_cast everywhere or monolythic
God objects. Point of fact is that there are many possible ways around
these problems. If you just pay attention to your principles instead of
tossing them out at the earliest inconvenience you just might find
youself coming up with intelligent and innovative solutions rather than
writing trash code.
 
N

Noah Roberts

That's not true. His problem is that class B is not just a
class A; it's a class A and something more. That, in itself,
frequently occurs, even in the best designs. There's certainly
nothing wrong per se with a concrete class implementing several
different interfaces.

Sigh...that is not the problem this Sam person is creating. What he is
doing is making class B an A when class B is just "almost" an A but
actually does not implement all the features of A.

Don't worry...you're going back into this new client's killfile.
 
N

Noah Roberts

Yeah it seems there was a typo or logical error on Noah's part
Ie did he mean to say

'A "is not" a B'

? because clearly in the LSP sense they have been chatting about

"B is-a A"

given the B inherits (publicly) from A scenario described (and
absent information to the contrary such as B breaking semantics
of A).

You are partially correct in that by inheriting publically they are
*stating* that B is-a A. I did not make a typo or anything like that
though. When B does not implement the **entire** interface of A but
still declares itself as-an A...someone's being dumb.

The OP's direction was indeed of having B is-a A and some more. The
proposed solution by this Sam guy is to add the extra behavior of B to A
and then force C to implement B's behavior, either with bogus functions
or by tossing exceptions, when it's only an A (in the original sense);
it's an introduction of a whole new level of unnecessary coupling.
Although there's nothing wrong with the former, the later is an open
door for all kinds of serious issues.

Logic error isn't on my part. It seems some just aren't following the
context of threads.
 
N

Noah Roberts

Generally use of the visitor pattern involves dynamic casting but *centralized*
at a single point. However, when you know all possible classes up front then you
can completely avoid casting. This is shown below.

I would suggest that though you might *think* you know all possible
classes up front, you never really do. Someday the customer will ask
you for something that requires you add one.
 
N

Noah Roberts

James,
Why not:
std::cout<< double(count) * 100 / max << '%' ;

and save some typing ?

Because saving time now can cost later. A search for "static_cast" is
very easy.

Saving some typing is never a particularly good reason to do anything.
 
K

Keith H Duggar

You are partially correct in that by inheriting publically they are
*stating* that B is-a A. I did not make a typo or anything like that
though. When B does not implement the **entire** interface of A but
still declares itself as-an A...someone's being dumb.

B only needs to implement as much of A as A does to be "is-a A".
See the example below.
The OP's direction was indeed of having B is-a A and some more. The
proposed solution by this Sam guy is to add the extra behavior of B to A
and then force C to implement B's behavior, either with bogus functions
or by tossing exceptions, when it's only an A (in the original sense);

C has nothing to do with whether B is-a A.
it's an introduction of a whole new level of unnecessary coupling.
Although there's nothing wrong with the former, the later is an open
door for all kinds of serious issues.

Ok. But this has nothing to do with LSP.
Logic error isn't on my part. It seems some just aren't following the
context of threads.

If it was not a typo then you made a logical error. Here is an
example of what we both understood Sam to mean

class A {
public :
class ErrorIsNotB { } ;
class ErrorIsNotC { } ;
int fooA ( ) { return 0 ; }
int fooB ( ) { throw ErrorIsNotB() ; }
int fooC ( ) { throw ErrorIsNotC() ; }
} ;

class B : public A {
public :
int fooB ( ) { return 0 ; }
} ;

class C : public A {
public :
int fooC ( ) { return 0 ; }
} ;

and by LSP "B is-a A". So it is simply logically false to claim
that "B is not a A" as you did.

KHD
 
S

Stuart Redmann

Andreas Dehmel wrote:

[snipped arguments about keeping separate containers for objects of
each subclass of a base class, thus avoiding dynamic_casts]
Even something
as superficially simple as keeping two lists in sync quickly ceases to
be so in the presence of things like out-of-memory errors. Then you'll
usually have to process twice as many objects whenever the list changes
(object insertion or deletion). For instance if you want to delete an
object you'll have to search it not only in the generic container but
all the other ones as well. Yes, all the other ones, because member
functions like isCircle(), isBezier() etc. which could be used as a
shortcut are actually equivalent to dynamic casts and without that you
won't know which specialization container to check for (in contrast to
creation, one usually doesn't know the type of the "active" object).
The runtime complexity isn't the main problem, you could at least
reduce that with something like a map rather than a list, but the
additional code is.

That's a good argument. Luckily, this one can be solved without
dynamic_casts as well: The application object can store not just a
simple pointer but an object that behaves like two pointers (there
would be a class for each CGeometricObject-derived class). When such
an object is deleted from the list of CGeometricObjects (for example
because the user wants to delete the active object), this class knows
which particular type-specific container must delete which entry. This
way deleting the active object will be reduced to constant time.

[snip]
Not necessarily true. A library user could use the library infrastructure
for managing the objects, but only feed it his own specializations
which know about each other. I generally consider library writers who think
they know every way their lib can be used and needlessly finalize large
parts of it 1) usually simply lacking imagination and 2) a major pain
in the butt. At least once we leave the realm of trivialities like
yet-another-container-class.

Agreed. Anyone who wanted to use their own CGeometricObject-derived
class would have to resort to dynamic_casts. This is one of the
technical reasons that make dynamic_casts unavoidable: If the library
code may be expanded with an unknown number of types, we cannot avoid
dynamic_cast (or any hand-written type information mechanism).

[snipped code example that computes intersections between geometric
objects through Double Dispatch]
I'm not too hot about finalizing the design the way you have to in order
to make double dispatch work. Apart from the non-extensibility it would work,
but the resulting interface once you realize it's not just Intersects() but
10 other functions which need the same treatment is not something I'd want
to use. And from a maintenance point of view: if I have to change the
interface because I find I'll need other/additional parameters down the line
I'll have to change M methods rather than one. While I'd concede this
technique has its uses in some situations I don't think the advantages
outweigh the disadvantages in cases like this.

Agreed. Once one realizes that extensions have to live with
dynamic_casts you can use them throughout the library core code as
well.
I think you have way too much free time ;-).
Touche.

I'm also convinced that
attempting to implement such a multi-container design for an actual drawing
program, widget library or similar rather than heavily idealized, stripped-down
example code, won't survive its own weight long enough to finish the first step,
let alone run the mile. Or can anyone show me a successful, fully-fledged
implementation?

I just wanted to show that for most dynmic_casts there is a solution
that can do without them. Admittedly, those solutions are often much
more abstract (try explaining the Double Dispatch meachnism to a
newbee programmer ;-)

Thanks for _your_ time,
Stuart
 
N

Noah Roberts

Yes. You get fired from your job. And replaced by someone with more
experience.

I'm curious: what did you think would happen?


Well, my practices, as good or as bad as they are, have done pretty well for
me, for the last 15+ years (more, if I include other things besides C++).

Yeah, yeah, yeah...I know a LOT of "experienced" programmers that are
complete morons.
Don't worry. I'll say that in about two or three years you'll also come
around to agreeing that proper exception handling, and implementation, is a
superior model for most common situations where error handling is called
for.

This has nothing to do with anything. I came to this conclusion years
ago.
In many cases, strong aversion to exception processing is a direct result of
not having sufficient expertise or understanding of the subject matter.
Leading to hilarity like this:
http://thedailywtf.com/Articles/Fine,_I_0x27_ll_Handle_Exceptions.aspx or
this: http://thedailywtf.com/Articles/Exceptional_Error_Handling_.aspx

I always find it fascinating when someone takes anothers objection to a
poor use of exceptions to being against them entirely. Of course, you
are well aware that what you are now arguing against is a complete straw
man, you just don't have anything valid to say anymore.
 
J

Joshua Maurice

Perhaps we could get this discussion back into civil territory? If
not, I'm going to assume Sam is a troll, and that Noah is feeding him,
and remove myself from this discussion.

Well, you don't seem to have been fascinated here, seeing how it took you
five days to figure out a reply. Seems that if you were truly fascinated,
you would've expressed your fascination a lot sooner.

Sam, some people have a life. Or for whatever reason she replied now.
That does not diminish the value of any claims made. I'm sure this is
some logical fallacy, attacking the date of publication as being slow
instead of the actual contents.
And what you ended up producing is just a wordier version of "you suck too".

… wait, I figured it out. There reason you were so stunned, for a little
while, is because you suddenly recognized those featured WTFs. Don't tell me
those were yours!

And this is why I think you're a troll. Noah is right. So, if you're
not a troll, here's your last chance to keep me interested in this
discussion. Please read this as each sentence prefaced with: "Noah and
I believe, along with a majority of the learned C++ community:"

It is bad to make a base class and (public inheritance) derived class,
where the derived class does not support the full contract of the base
class. Also, having a base class contract of "Each of these operations
may not be supported, and will throw in that case" is also a bad idea.
(Public) inheritance in C++ should only be used for subtypes, and that
your base class "throw by default" does not produce subtype
relationships.

In other words, if you have a base class and a derived class as you
suggest, and we have a pointer to base class, which may point to a
derived class object, then we do not know offhand which functions
we're allowed to call on the object. It may result in a thrown
exception. Such a thing is difficult and unwieldy to use compared to
proper abstractions.

In other words, such a thing is a strong indicator of bad
abstractions. There is very usually a different abstraction which does
not have a class hierarchy with lots of "does not support". In such a
case, you can call functions on 'pointer to base class', and expect
that it will behave according to the contract of the base class in a
sensible and usable way, aka not throw "unsupported exception".

Your suggestion that we believe exceptions is bad is entirely
unfounded and incorrect. Suggesting such a thing is an almost willful
ignorance of our point, and it is a strawman argument. We disagree
with your class design if it uses exception and we disagree if it uses
error return codes. We disagree fundamentally with "does not support"
as a default for a function in a abstract base class as general
operating procedure.
 
J

Joshua Maurice

Perhaps we could get this discussion back into civil territory? If
not, I'm going to assume Sam is a troll, and that Noah is feeding him,
and remove myself from this discussion.




Sam, some people have a life. Or for whatever reason she replied now.
That does not diminish the value of any claims made. I'm sure this is
some logical fallacy, attacking the date of publication as being slow
instead of the actual contents.




And this is why I think you're a troll. Noah is right. So, if you're
not a troll, here's your last chance to keep me interested in this
discussion. Please read this as each sentence prefaced with: "Noah and
I believe, along with a majority of the learned C++ community:"

It is bad to make a base class and (public inheritance) derived class,
where the derived class does not support the full contract of the base
class. Also, having a base class contract of "Each of these operations
may not be supported, and will throw in that case" is also a bad idea.
(Public) inheritance in C++ should only be used for subtypes, and that
your base class "throw by default" does not produce subtype
relationships.

In other words, if you have a base class and a derived class as you
suggest, and we have a pointer to base class, which may point to a
derived class object, then we do not know offhand which functions
we're allowed to call on the object. It may result in a thrown
exception. Such a thing is difficult and unwieldy to use compared to
proper abstractions.

In other words, such a thing is a strong indicator of bad
abstractions. There is very usually a different abstraction which does
not have a class hierarchy with lots of "does not support". In such a
case, you can call functions on 'pointer to base class', and expect
that it will behave according to the contract of the base class in a
sensible and usable way, aka not throw "unsupported exception".

Your suggestion that we believe exceptions is bad is entirely
unfounded and incorrect. Suggesting such a thing is an almost willful
ignorance of our point, and it is a strawman argument. We disagree
with your class design if it uses exception and we disagree if it uses
error return codes. We disagree fundamentally with "does not support"
as a default for a function in a abstract base class as general
operating procedure.

I'm sorry. I forgot another import phrasing which I like and find
useful.

I believe that virtual functions, dynamic dispatch, (public)
inheritance, basically Object Oriented (as understood as dynamic
dispatch, not encapsulation, data hiding, and abstraction), should
very usually only be used to avoid a switch on type. If your class
hierarchy does not replace a giant switch statement on the type of the
object, ex:
if type A, then frabulate A,
else if type B, then frabulate B,
else if type C, then frabulate C,
etc.
then you're probably doing it wrong. If you are not avoiding a switch
on type, there are probably better ways to do what you are trying to
do than using inheritance.
 
N

Noah Roberts

Your suggestion that we believe exceptions is bad is entirely
unfounded and incorrect. Suggesting such a thing is an almost willful
ignorance of our point, and it is a strawman argument. We disagree
with your class design if it uses exception and we disagree if it uses
error return codes. We disagree fundamentally with "does not support"
as a default for a function in a abstract base class as general
operating procedure.


I'd also say I disagree with the statement that these exceptions that
can be thrown don't need to be properly handled or that the
responsibility of the client to make sure the call will work, or did
work, magically goes away. You've simply got to do one of the
following:

1) make sure your cast is valid (if using dynamic cast instead of Sam's
proposal)

2) make sure the call you're about to make will not explode.

3) make sure the call you just made did not explode.

A responsible client of such higherarchies and container setups (the
kind the OP wants to discuss, not the double dispatch alternatives and
such that have been mentioned) cannot avoid doing one of the three.

Frankly, contrary to Sam's repeated assertions the former is by far the
most direct and meaningful. Further, you can abstract it in a visitor
pattern/concept that minimizes and certralizes dynamic_cast calls and
the whole thing becomes that much more maintainable and expressive. I'm
sure there are many other great ways to deal with the problem if some
thought and attention is paid to good practices and, as you call it,
indicators of bad abstractions.
 
N

Noah Roberts

class A {
public :
class ErrorIsNotB { } ;
class ErrorIsNotC { } ;
int fooA ( ) { return 0 ; }
int fooB ( ) { throw ErrorIsNotB() ; }
int fooC ( ) { throw ErrorIsNotC() ; }
} ;

class B : public A {
public :
int fooB ( ) { return 0 ; }
} ;

class C : public A {
public :
int fooC ( ) { return 0 ; }
} ;

and by LSP "B is-a A".

No, it isn't; not in any way that doesn't totally misunderstand the
point of LSP. Although syntatically it appears to, conceptually B is
not implementing the A interface. Thowing an "Is not a supported call"
function is not an implementation unless it's part of the specification
of A.

Further, you are misrepresenting the LSP. B does not need to, "only
implement as much of A as A does." B needs to be a proper subtype of A:

"Let q(x) be a property provable about objects x of type T. Then q(y)
should be true for objects y of type S where S is a subtype of T."

The problem here is that nothing is provable about "x of type T" when it
comes to fooB and fooC in your above example. Type T (A) is not
providing a contract that can be followed or not followed by subtypes
and these calls make no sense in terms of A. It's an improper
abstraction; LSP doesn't even really apply to it.

If we were to apply LSP though then we need to say what "fooC" is
supposed to do. Let us say that preconditions state it should be a
valid object. Postcondition states it returns an integer. As you can
see, B does not respect this contract. Exceptions are used in cases
when preconditions are not met or when postconditions cannot be.

Of course you might say that the precondition of "fooC" is that "this"
is a pointer to a C. This just restates my original statement that A is
not a proper abstraction. It's incomplete in that it depends on
subobjects for its own definition. A is not even an A...it's an ABC and
whatever else might subclass it.

To understand why this would be different with a possible state machine
where "fooC" cannot be called in certain states the contract would be
reworded such that a precondition is that the machine is not in a state
that makes it invalid. This *may or may not* be implemented by a scheme
like shown in your example; there are many ways to do it. Further, fooC
will more likely make sense in more subobjects. Yet further we aren't
saying, "*this must be a C," but we are saying "A must be in state C";
in other words we are making a conceptual statement about *A* in terms
of A, not a type statement about A in terms of the C type.

You can see this even further by the fact that you'd probably implement
this machine using the state pattern and classes A, B, and C would be
implementation details to a higher abstraction.

It's all in the process of discovery that makes it a good vs. bad design
decision. We don't say, "Hey, I want to stick B in a container of A
objects so I'll stick B functionality in A that doesn't belong there."
That's a bad guiding principle, is what Sam is proposing, and it DOES
violate the LSP on many levels. As such it is not only prone to
problems, it will inevitably cause them. Not ALL cases when B throws an
exception in reply to a "fooC" are such violations but the likelihood of
one being guided by the proposed principle is very high. The design
practice that Sam is recommending is pure hackery that gives no thought
to the abstraction being implemented.

In short, whether or not C or B throw an exception should be on the
basis of A, not the other way around. C and B should be implementing
the interface of A, the interface of A should not be altered to suit the
needs of B and C nor defined in their terms. This is the fundamental
meaning behind the LSP. As an alumni from MIT I'd actually expect you
to know this. Might not be design 101 stuff but it's not that advanced
either. Are you just trolling?
 
N

Noah Roberts

Hi,

I am curious what is the current opinion about the merits of using
dynamic_cast<>(). I remember reading somewhere several years ago
that it was considered a bad practice to use the dynamic casting,
and that in cases when such a need arises, one should think about
redesigning the code. My question is how to do it.

I think it should be mentioned that the key word here is "think". Just
because you use dynamic_cast doesn't mean you should redesign your code,
it means you should *think* about it. Before you ask how you should be
asking if you should and only you can answer that question for your
particular problem...whatever it is.
 
J

Joshua Maurice

Says who? This appears to be some arbitrary pronouncement, apparently
imposed by fiat. I do not see anything unpalatable about defining a
superclass as "here's a list of methods that may be implemented by a given
subclass; if a subclass does not implement the method an exception gets
thrown; it is your responsibility to catch any exceptions, and handle them
accordingly".

Potentially thrown exceptions, of course, must be defined by the API.
However, if the superclass's API is defined, as such, I see nothing wrong
with that.

Perhaps it might violate some unimportant design principle birthed in the
ivory schools of academia, where self-proclaimed experts toiled away for
decades, drawing up grandiose theories on how the real world outside should
work like, without the benefit of actually stepping outside and taking a
breath. If that be the case, I'm not much worried about that.


Yes, you do. The API documentation will include a method that interrogates
the class instance and returns an indication of which set of APIs the given
class instance supports, with all other ones explicitly defined by the API
as throwing an exception.


I wholeheartedly agree that some may find it difficult to implement proper
handling and usage of exceptions, resulting in a mandatory definition of
"proper", as just a simple alias for "not complicated". Just like some may
also find it difficult to properly indent their code, or document their
functions. But just because some may find that these requirements are beyond
their abilities doesn't mean that everyone else should stop writing code.


Certainly. I'm sure you're not the only one that holds such an opinion.

Fair enough.

You are still trying to dodge answering my point, but I think you're
at least sufficiently acknowledging it. At this point, I believe our
differences are not reconcilable in this forum. I hold my view as a
very strong "gut instinct", and I believe in it from having seen too
much code break the rule and be unusable precisely because it broke
it.

Your reply can be summed up "No it's not" and "Must be some academia
thing". I shall draw parallels to "Goto considered harmful", where you
are the goto user, and I am the person suggesting structured
programming. (I mention this analogy only to elucidate my views on
your "academia" argument, not as a proof of "I win".)
 
N

Noah Roberts

Says who? This appears to be some arbitrary pronouncement, apparently
imposed by fiat. I do not see anything unpalatable about defining a
superclass as "here's a list of methods that may be implemented by a given
subclass; if a subclass does not implement the method an exception gets
thrown; it is your responsibility to catch any exceptions, and handle them
accordingly".

Point of fact though, this isn't what you've been saying. Not that I
agree with what you're saying now, but it is different.

You said, "Take what is implemented by subclasses but not necessary for
the abstraction and add them to your base." That's very different than,
"Here's functions that supply the interface for an abstraction, they may
not be implemented by all adherants."

Furthermore, you said, quite directly, that the need to handle
exceptions magically disappeared by doing this:

"On one hand, you are required to invoke dynamic_cast<> all over the
place.
You are required to check the result of the dynamic_cast<>, every time.
If
you end up with a null pointer, you must explicitly handle the error
condition, every time.

On the other hand, virtual methods are now invoked in the superclass,
instead. If the superclass is not an instance of an appropriate
subclass,
the default implementation in the superclass ends up throwing an
exception.
It may come as a shock to the junior apprentices, but they do not need
to
catch an exception from every occurence of the virtual method call, that
may
potentially throw an exception."

Furthermore, coding practices and design principles are not declared by
fiat. They have been discovered after many decades of practice by
people better than either of us. Ignore them at your peril.

As to what is unpalaple about it: You're not considering an
abstraction, thus you're not even in the process of design. Good design
comes not from throwing a bunch of unrelated things together because
it's convenient. Good design is the constant search for appropriate and
workable abstractions that are reusable.
 
N

Noah Roberts

class A {
public :
class ErrorIsNotB { } ;
class ErrorIsNotC { } ;
int fooA ( ) { return 0 ; }
int fooB ( ) { throw ErrorIsNotB() ; }
int fooC ( ) { throw ErrorIsNotC() ; }
} ;

class B : public A {
public :
int fooB ( ) { return 0 ; }
} ;

class C : public A {
public :
int fooC ( ) { return 0 ; }
} ;

I think a more intuitive set of objects needs to be hypothesized to show
the problem here:

class Vehicle
{
public:
virtual void turn(double rad) = 0;
virtual void forward(double speed) = 0;

// I put this here because I need to call
// it on cars that are Vehicle*
virtual void insert_gas(double amount) { throw "Wot?! 0_o."; }
};

class Car : Vehicle
{
public:
...implement vehicle interface...
void insert_gas(double amount) { ... }
};

class Bicycle : Vehicle
{
public:
...implement turn and forward...
};

Although *syntatically* we could say that the Bicycle appears to be a
Vehicle it actually isn't. Inserting gas into a bicycle is nonsense.

Thus we don't put "insert_gas" into Vehicle because it's convenient. We
take it out of that interface and put it where it belongs, in Car. Only
then will Bicycle be a proper subtype of Vehicle.
 
N

Noah Roberts

It might be necessary to strain one's brain, but the two are logically
equivalent.

Only when strained to the point of snapping.
I suspect that the disconnect here is that you read "add them to
your base" to mean "implement the subclass's method in the base class".
Nope.


I really hoped that it wouldn't be necessary to chew and digest everyone's
food before giving it to them, to swallow, by clarifying that "add them to
your base class" means "define the methods in the base class, with a default
implementation", and without spelling out that the methods in question
would, of course, be defined as virtual methods. I would've really liked to
take it for granted that it was not necessary to go into such excruciating
details, that all of that would be self evident, and did not need to be made
explicit.

No, you're taking it for granted that you're right and that anyone
disagreeing must have misunderstood you because they're too incompetent
to get it. What's ironic is that this very assumption shows your own
incompetence for your position was addressed quite directly, but
apparently you so poorly understood it that you now come up with this.

I have no incling to read or respond to the rest of your drivel.
 
K

Keith H Duggar

First I note that you snipped two important points without responding
to them.
No, it isn't; not in any way that doesn't totally misunderstand the
point of LSP. Although syntatically it appears to, conceptually B is
not implementing the A interface. Thowing an "Is not a supported call"
function is not an implementation unless it's part of the specification
of A.

Well let's make this as simple as possible. Given the above and this

class D : public A { } ;

and keeping in mind that the topic here is C++ not rarified type
theory, I have three yes or no questions for you.

Is A an A?
Is B an A?
Is D an A?
Further, you are misrepresenting the LSP. B does not need to, "only
implement as much of A as A does." B needs to be a proper subtype of A:
"Let q(x) be a property provable about objects x of type T. Then q(y)
should be true for objects y of type S where S is a subtype of T."

As others have already pointed out, there are various notions
of LSP which may or may not apply to a given context. In the
context of C++ the wikipeducated notion above has innumerable
problems. Perhaps the simplest example is property "sizeof".
Can you think of any other problems with it? Which formulation
of LSP do you think applies most usefully to C++?
The problem here is that nothing is provable about "x of type T" when it
comes to fooB and fooC in your above example. Type T (A) is not
providing a contract that can be followed or not followed by subtypes
and these calls make no sense in terms of A. It's an improper
abstraction; LSP doesn't even really apply to it.

Earlier you wanted to use the second order predicate

"B is-a A" iff FORALL(q)[Provable(q,A) implies q(B)]

to define the notion "B is-a A". Now you are claiming that if we
partition q into qa, qb, and qc where qb are predicates involving
fooB, qc involving fooC, and qa the rest, then you claim

FORALL(qb)[NOT Provable(qb,A)]
FORALL(qc)[NOT Provable(qc,A)]

But if that is the case then it is immediately obvious (since
q = UNION(qa qb qc) and false implies anything) that

FORALL(qa)[Provable(qa,A) implies qa(B)]
FORALL(q)[Provable(q,A) implies q(B)]

is true since B and A differ only in their fooB predicates (in
this rarified world that doesn't apply to C++). In other words,
you have just stated that even in your universe B is-a A.

[snip more about the Noah-Type-Theory and universe]
needs of B and C nor defined in their terms. This is the fundamental
meaning behind the LSP. As an alumni from MIT I'd actually expect you
to know this. Might not be design 101 stuff but it's not that advanced
either. Are you just trolling?

So far you have accused me of "totally misunderstanding" and
"misrepresenting" and now of "trolling" and "knowing less than
[Noah] expects an MIT alum to know". In other words you endeavor
to make things personal. Why is that? Have you stopped to
consider yourself?

You are young and full of energy and drive and that is a very
wonderful thing. Focus that energy on refining yourself and
listening to others, even the dull and ignorant such as myself.

And to answer your question, no I'm not trying to troll. To prove
that I will not respond further to your posts in this thread unless
you give me permission. So if/when you respond to this post and if
you want my further participation, please make sure to let me know
it's ok to respond without being personally attacked and accused
of being a troll by you.

KHD
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,067
Latest member
HunterTere

Latest Threads

Top