Thursday, July 10, 2008

Screenshots of my Visibility Problem

Below are screenshots of a debugging session showing the visibility issue. I blacked out any proprietary code and information, hopefully still leaving enough code to show the problem I'm running into.

Here is my procedure, showing the var definition. Note that "global" is in the "uses" part of the implementation section.

Here is my procedure, showing the constructor call.

Here we are running my app, hitting the breakpoint in my procedure.

Here we are in my procedure, about to enter the constructor.

Here we are in the constructor. Notice that now we are inside global.pas, this is the correct location for CParsedFile.Create.

The instant I hit "Step into" I get an access violation error. If you look at the code at the top of the constructor (above screenshot), you will see that it's nothing fancy, just a bunch of string assignments (these strings are defined in the class definition of CParsedFile in the implementation section of global.pas).

If I was referencing a separate var, or there was another definition of CParsedFile, why am I stepping into the correct constructor? If I edit my procedure so that "CParsedFile.Create" is now "global.CParsedFile.Create", and step through it, everything is fine. So you understand my frustration when:
  1. I can compile
  2. When I step through my code with the debugger, I get to the appropriate constructor
  3. I get a runtime error in the constructor
  4. This runtime error is fixed by prefacing the unit in front of my constructor call in my procedure.

9 comments:

  1. I hope you guys can see the expanded pics when you click on the thumbnail. Blogger seems kinda wonky at times.

    ReplyDelete
  2. The first screenshot doesn't pop up.

    ReplyDelete
  3. Remember that Delphi is NOT case sensitive.

    That means that cParsedFile equals CParsedFile.

    By convention you should prefix all types with a "T". Rename your CParsedFile class to TParsedFile.

    Tip: The easiest way is to select all text in global.pas and hit Ctrl+Shift+J. This triggers the "Sync Edit Mode" witch will rename every CParsedFile synchronously.

    BTW: Pic 1 is not expandable...

    ReplyDelete
  4. One thing I noticed that you use a construction like A.B.C[i].foo. There isn't any checking on the validity of B or C[i]. It might well be that either is invalid and the software runs into an execption when using that (which is probably inside the constructor). There would be no way for the compiler to know that this would happen by the way.

    ReplyDelete
  5. Maybe you can add the screendumps as an enclosure to the posting?

    ReplyDelete
  6. Jorn: Thanks, I realized the case insensitivity as I was posting it. I have too much C# and Java on the brain =). I will also use the "T" in my naming convention in the future.

    Bart: That makes sense, except how does changing "CParsedFile.Create" to "global.CParsedFile.Create" solve a case where there may be an invalid parameter passed to the constructor?

    ReplyDelete
  7. It is the case-thing that causes the trouble.

    When you call Variabel.Create, the (already created) instance of Variabel is Re-Initialized.

    When you call CParsedFile.Create, Delphi tries to re-initialize the variable cParsedFile. cParsedFile isn't created, you get the Access violation...

    Se example: http://delphi.about.com/od/adptips2006/qt/init_object.htm

    ReplyDelete
  8. duh, stupid I missed that :-) Yes, Jørn is right.

    As a naming convention you should stick to: TFoo is a class, aFoo is an instance and cFoo is a constant.

    ReplyDelete
  9. Thanks Jørn and Bart! You guys were right!

    ReplyDelete