The Nightstar Zoo

Nightstar IRC Network - irc.nightstar.net
It is currently Thu Dec 14, 2017 2:08 pm

All times are UTC - 6 hours [ DST ]




Post new topic Reply to topic  [ 12 posts ] 
Author Message
 Post subject: Ask Mr. Smarty Man
PostPosted: Sun Aug 08, 2004 8:07 pm 
Offline
Nightstar Graveyard Daemon
User avatar

Joined: Mon Jun 03, 2002 8:30 pm
Posts: 1071
Location: Wouldn't you rather observe my Velocity?
Dear Mr. Smarty Man,

I saw this in my project today, and I can't for the life of me figure how it works. (Aside from "just fine", which is true.)

Code:
HWND hCtl = this->CWindow::GetDlgItem(IDC_REFRESH);


Now, GetDlgItem is a method of class CWindow, and the "this" object is an ATL control.

In C++, what the heck does

pObject->classname::method(arglist)

mean? "this" does not have any member object named CWindow... it's a classname, possibly from with "this" is inherited. Is it the same thing as either of these:

Code:
HWND hCtl = ((CWindow*)this)->GetDlgItem(IDC_REFRESH);
HWND hCtl = ((CWindow)(*this)).GetDlgItem(IDC_REFRESH);


Signed,

Perplexed in Peoria

P.S. Here's the declaration for class "this". Yeah, I know it's scary.

Code:
class ATL_NO_VTABLE CCxC3XJobCreate :
   public CComObjectRootEx<CComSingleThreadModel>,
   public IDispatchImpl<ICxC3XJobCreate, &IID_ICxC3XJobCreate, &LIBID_C3ExpertLib>,
   public CComCompositeControl<CCxC3XJobCreate>,
   public IPersistStreamInitImpl<CCxC3XJobCreate>,
   public IOleControlImpl<CCxC3XJobCreate>,
   public IOleObjectImpl<CCxC3XJobCreate>,
   public IOleInPlaceActiveObjectImpl<CCxC3XJobCreate>,
   public IViewObjectExImpl<CCxC3XJobCreate>,
   public IOleInPlaceObjectWindowlessImpl<CCxC3XJobCreate>,
   public ISupportErrorInfo,
   public IPersistStorageImpl<CCxC3XJobCreate>,
   public ISpecifyPropertyPagesImpl<CCxC3XJobCreate>,
   public IQuickActivateImpl<CCxC3XJobCreate>,
   public IDataObjectImpl<CCxC3XJobCreate>,
   public IProvideClassInfo2Impl<&CLSID_CxC3XJobCreate, NULL, &LIBID_C3ExpertLib>,
   public CComCoClass<CCxC3XJobCreate, &CLSID_CxC3XJobCreate>
{


Top
 Profile  
 
 Post subject: Re: Ask Mr. Smarty Man
PostPosted: Sun Aug 08, 2004 11:10 pm 
Chalain wrote:
In C++, what the heck does

pObject->classname::method(arglist)

mean? "this" does not have any member object named CWindow... it's a classname, possibly from with "this" is inherited.

That was my guess. It'd be the one which would be certain to override virtuals, no? So if the parent function is a virtual and the ATL object overrides, then the parent function is called anyway.

Your first cast, AFIAK, doesn't do that. And the second has overhead, doesn't it?


Top
  
 
 Post subject: Re: Ask Mr. Smarty Man
PostPosted: Mon Aug 09, 2004 8:40 am 
Offline
Nightstar Graveyard Daemon
User avatar

Joined: Mon Jun 03, 2002 8:30 pm
Posts: 1071
Location: Wouldn't you rather observe my Velocity?
Dear Mr. Smart Man,

Raif wrote:
And the second has overhead, doesn't it?


I don't think so.... This call would have overhead:

Code:
CWindow(*this).GetDlgItem(...)


Because it invokes CWindow::CWindow( const CWindow& that )... a copy constructor, not a cast to a base class.

The previous two casts have no overhead. They both tell the compiler to slice off the vtable so it looks like CWindow's vtable. To the compiler, GetDlgItem is just an offset into that table of functions. The first cast says, "take the pointer to this object and assume it's a pointer to the sliced-off vtable. Now dereference the pointer, index into it GetDlgItem's amount, and call that function." The second cast says "take the pointer to this object, dereference it to get the vtable, slice it off like a CWindow, and then index into it GetDlgItem's amount and call that function." The only difference is when the dereference happens.

In other words, the compiler thinks "jump to offset 2408 + 12" in one case and "jump to offset 12 + 2408" in the other. Both cases emit "jmp 2420".

It occurs to me that both of my casts may not undo overrides, that if CxC3XJobCreate overrides GetDlgItem, my casts will still end up calling CxC3XJobCreate::GetDlgItem, not CWindow::GetDlgItem. Perhaps that is what this->class::method does... it forces the override to be disabled. This would sort of imply to me two things:

  1. CxC3XJobCreate has a method called GetDlgItem.
  2. It violates the Liskov Substitution Principle. CxC3XJobCreate::GetDlgItem() won't do what you need CWindow::GetDlgItem() to do.


The catch? Item #1 is false. CxC3XJobCreate did not, in fact, inherit any such method. OTOH, This would necessarily make item #2 true.

I still don't get how you can dereference an object pointer with a class name. Could CWindow be a namespace or internal class publicly defined in CxC3XJobCreate?

Signed,

Still Perplexed


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 09, 2004 9:18 am 
Offline
Nightstar Graveyard Daemon
User avatar

Joined: Mon Jun 03, 2002 8:30 pm
Posts: 1071
Location: Wouldn't you rather observe my Velocity?
A quick study of my notes yields this additional tidbit about interface slicing.

If the base class' method is declared virtual, then you cannot undo the override. If it is not declared virtual, then all access to the base class will NOT be overridden. Clear as mud? Here's an example:

Code:
class Person {
public:
    void foo(void) { cout << "I am a person." << endl; }
};

class Programmer : public Person {
    void foo(void) { cout << "I am a programmer." << endl; }
};

void Yawp( const Person& p ) {
    p.foo();
}

Person p;
p.foo(); // "I am a person"
Yawp(p);  // "I am a person"

Programmer p2;
p2.foo(); // "I am a programmer"
Yawp(p2); // "I am a person"


Code:
class Person {
public:
    virtual void foo(void) { cout << "I am a person." << endl; }
};

class Programmer : public Person {
    void foo(void) { cout << "I am a programmer." << endl; }
};

void Yawp( const Person& p ) {
    p.foo();
}

Person p;
p.foo(); // "I am a person"
Yawp(p);  // "I am a person"

Programmer p2;
p2.foo(); // "I am a programmer"
Yawp(p2); // "I am a programmer"

((Person)p2).foo();  // "I am a programmer"  ... *still* overridden


This helps some but I am,

Still Perplexed


Top
 Profile  
 
 Post subject: Re: Ask Mr. Smarty Man
PostPosted: Mon Aug 09, 2004 11:50 am 
Chalain wrote:
The previous two casts have no overhead. They both tell the compiler to slice off the vtable so it looks like CWindow's vtable. To the compiler, GetDlgItem is just an offset into that table of functions.

Sometimes abstraction is more confusing than it ought to be. :) Thanks. I thought doing that type of cast on a class copied it regardless, unless you explicitely told it to reinterpret_cast.

Quote:
The catch? Item #1 is false. CxC3XJobCreate did not, in fact, inherit any such method. OTOH, This would necessarily make item #2 true.

That flew right past me. Care to try again? :)

Quote:
I still don't get how you can dereference an object pointer with a class name. Could CWindow be a namespace or internal class publicly defined in CxC3XJobCreate?

Well, to call a parent function in a class you can do something like this (I think):

Code:
void *GetThing (void)
{
    void *x = Parent::GetThing();
    /* Do some stuff. */
    return x;
}


While the actual mechanics are different from a namespace (AFAIK), that's the behavior it has. To me it's far more intuitive than casting. Instead of saying, "Right, you over there, the dog. You're a cow now. Make your funny animal sound and forget you're a cow," you're saying, "Dog, moo like a cow."

Or:
Code:
((Cow)(*pdoggie)).Sound();
pdoggie->Cow::Sound();


These animal inheritance analogies really can get convoluted. :)

Returning to your next post, that's a good summary of the purpose of virtual functions. Pass the child around as a parent but have it still retain the child's properties. ;) In this case, however, you're forcing an override. Virtual or not, gimme the parent.


Top
  
 
 Post subject:
PostPosted: Mon Aug 09, 2004 12:21 pm 
Here's a little demo of how the post-dereference cast and the scope operator equate:

Code:
class Parent1
{
public:
   virtual void Print (void) { cout << "Parent." << endl; }
};

class Child1 : public Parent1
{
public:
   void Print (void) { cout << "Child." << endl; }
};

class Parent2
{
public:
   void Print (void) { cout << "Parent." << endl; }
};

class Child2 : public Parent2
{
public:
   void Print (void) { cout << "Child." << endl; }
};

int main()
{
   Child1 c1;
   Child2 c2;

   c1.Print();                 // Child.
   c1.Parent1::Print();        // Parent.
   ((Parent1*)(&c1))->Print(); // Child.
   ((Parent1)(c1)).Print();    // Parent.

   c2.Print();                 // Child.
   c2.Parent2::Print();        // Parent.
   ((Parent2*)(&c2))->Print(); // Parent.
   ((Parent2)(c2)).Print();    // Parent.

   return 0;
}


Top
  
 
 Post subject: Re: Ask Mr. Smarty Man
PostPosted: Mon Aug 09, 2004 5:07 pm 
Offline
Nightstar Graveyard Daemon
User avatar

Joined: Mon Jun 03, 2002 8:30 pm
Posts: 1071
Location: Wouldn't you rather observe my Velocity?
Dear Mr. Smarty Man,

Raif wrote:
Chalain wrote:
The catch? Item #1 is false. CxC3XJobCreate did not, in fact, inherit any such method. OTOH, This would necessarily make item #2 true.

That flew right past me. Care to try again? :)


If CxC3XJobCreate were overriding GetDlgItem, it would implement and/or expose GedDlgItem(). It does not. If you try to do this:

Code:
this->GetDlgItem(IDC_CONTROL);


You'll get an error message telling you that CxC3XJobCreate does not have any such function.

Your casting examples are correct, but you're looking at the wrong two-thirds of the problem. :-) When you say object->member, you're saying "Hey you, whatcha got?" To use your example, dog->leg is "Hey dog, let's do something with your leg." When you say class::method, you're saying "I want to do you your method, and I'm speaking to you in your capacity as a member of class." For example, ((CMammal*)pDog)->GetBloodTemperature() says "Hey, dog, you're a mammal, how warm is your blood?"

Here's another illustration that amuses me. :-)

Code:
CUser::Discipline( user ); // sends /me slaps user with a trout
CAdmin::Discipline( user ); // sends /kill user
CGrievanceChair::Discipline( user ); // fires user from staff


Same function, same person, but very different results based on the fact that you're talking with the discipliner in different capacities.

But I digress. The 2/3rds of that statement that confuses me is

Code:
this->CWindow::


The -> implies "has_a", but CWindow is a class. What we're saying is "Hey, CxC3XJobCreate, I want to talk about your ownership of the definition of CWindow...."

I'll dig into it more. Perhaps COM or ATL sets up vtable/interface structs (which would be members accessible via ->) that share the same name as the class that defines them.

Hmmm. HMMM! That's sounding more and more likely.

Code:
this->CWindow::GetDlgItem(...)


could mean, "Hey, CxC3XJobCreate, I wanna talk to you on your CWindow interface, and I want to invoke the GetDlgItem method on it."

I gotta go run errands. If I get time to prove or disprove this tonight, I'll post my notes.

Signed,

Somewhat Less Perplexed


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 09, 2004 5:35 pm 
You're thinking too hard, methinks.

From what I remember reading the C++ spec, and verified with my handy-dandy MSVC7 compiler (YMMV on other compilers?), the code
Code:
pInstanceOfClassA->ClassB::foo(x, y, z);

compiles sort-of<sup>*</sup> like
Code:
static_cast<ClassB>(pInstanceOfClassA)->foo(x, y, z);
if and only if the following two conditions are satisfied.
  • ClassA is derived from ClassB
  • ClassB defines a member foo (Not good enough to inherit one from ClassC)
If those two conditions are not met, it compiles the same as
Code:
#error You dumbass


<sup>*</sup>I say sort-of because the call actually ignores vtables as well, which the sort-of code might use. Really, it compiles more like
Code:
asm {
  push z;
  push y;
  push x;
  push pInstanceOfClassA;
  call ClassB::foo;
}
Some quick tests with the MSVC7 compiler confirms, at least to me, that regardless of vtable, you get the scope you requested.
Code:
struct A {virtual void foo() {printf("A::foo()");}};
struct B : public A {void foo() {printf("B::foo()");}};

B x;
x->A::foo(); // output "A::foo()"


Specifying a scope operator means "I want this scope and only this scope, inheritance and vtable be damned". You will get the class function specified. Period.

Now, the way you describe your code above,
Code:
HWND hCtl = this->CWindow::GetDlgItem(IDC_REFRESH);
the scope resolution would only serve to confuse the poor Chalain. If indeed, CCxC3XJobCreate does not define GetDlgItem() and CWindow is the first base class which does, then you can rewrite as
Code:
HWND hCtl = GetDlgItem(IDC_REFRESH);

Given the hideous tree of inheritance, however, the scope resolution probably exists in order to firmly establish *which* base class carries the function you want to call, and may in some cases be required, if you have more than one base class defining GetDlgItem(). Either way you're still safe dropping the "this->" inside a member function.


Top
  
 
 Post subject:
PostPosted: Mon Aug 09, 2004 6:00 pm 
Pi wrote:
Specifying a scope operator means "I want this scope and only this scope, inheritance and vtable be damned". You will get the class function specified. Period.

Yes! :)

Quote:
Given the hideous tree of inheritance, however, the scope resolution probably exists in order to firmly establish *which* base class carries the function you want to call, and may in some cases be required, if you have more than one base class defining GetDlgItem(). Either way you're still safe dropping the "this->" inside a member function.

So *that's* what he was asking.


Top
  
 
 Post subject:
PostPosted: Mon Aug 09, 2004 9:07 pm 
Offline
Nightstar Graveyard Daemon
User avatar

Joined: Mon Jun 03, 2002 8:30 pm
Posts: 1071
Location: Wouldn't you rather observe my Velocity?
Dear Mr. Smarty Man,

Pi wrote:
Code:
HWND hCtl = GetDlgItem(IDC_REFRESH);


*SIGH*

I *swear* that this morning, this did not work. Now it does. How did you DO that?

Signed,

Remembering to Take My Ritalin Next Time


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 09, 2004 9:38 pm 
Oh, by the way, did I mention that any class which inherits from sixteen base classes really, REALLY, REALLY needs to be refactored down so its objects aren't trying to be quite so many things at once?


Top
  
 
 Post subject:
PostPosted: Mon Aug 09, 2004 11:41 pm 
Offline
Nightstar Graveyard Daemon
User avatar

Joined: Mon Jun 03, 2002 8:30 pm
Posts: 1071
Location: Wouldn't you rather observe my Velocity?
Pi wrote:
Given the hideous tree of inheritance, however, the scope resolution probably exists in order to firmly establish *which* base class carries the function you want to call, and may in some cases be required, if you have more than one base class defining GetDlgItem(). Either way you're still safe dropping the "this->" inside a member function.


I have a more likely proposal. One of my coworkers is Indian, and one is Bangladeshi. They have less than two years and less than two months of C programming experience, respectively.

this->classname::method is probably getting used in exactly the same sense that an ESL speaker might say, "It is an hebetudinous day." It is perfect english, and without a dictionary you have no idea what the hell you've just been told.

So it is at times with their code. That bit of code probably just means, "I must now be casting this pointer to the base class from which is receiving its derivation."

Pi wrote:
Oh, by the way, did I mention that any class which inherits from sixteen base classes really, REALLY, REALLY needs to be refactored down so its objects aren't trying to be quite so many things at once?


Turn around, preacher, the congregation's behind you.

Unfortunately, in this case I think it's because we're doing ATL and COM, and the designers of that trisomial donkeyrape of a technology were trying to create something akin to Java's notion of "mixin" classes. This class inherits from sixteen base classes because it is constructed from 16 microscopic bits. Half those classes provide nothing more that 2 or 3 accessor functions. IDispatch, for example, provides one function that lets VB call functions by passing in the name of the method as a string.

There's a joke in there somewhere, probably ending with "Oooh, you stepped in COM."

I blame your boss for that one, Pi.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 12 posts ] 

All times are UTC - 6 hours [ DST ]


Who is online

Users browsing this forum: No registered users and 1 guest


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

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group