New-ish REALbasic features (Part Three)

| | Comments (11)

Continuing right along from our previous post, we're going to talk about structures today. Structures are another new-ish addition to the REALbasic language. They are datatypes which define a set of other datatypes. Sounds scary, eh? It's not -- I promise. You can think of a structure as being a "dumbed-down" kind of class. Classes can contain all sorts of cool things like methods and constants and properties. A structure is more like a class that contains only public properties. You can't put any methods or constants, etc onto a structure. And all of its members are public. That's why it's "dumbed-down."

So why would want to use a structure instead of a class? The biggest reason has to do with how they're stored (and hence, how they're accessed). If you recall our discussion last time about pointers, you'll remember two important terms: value and reference. Classes are reference types -- when you modify the contents of a class, anything holding a reference to that class is immediately updated. Structures, on the other hand, are value types. If you have two structures, then you've got two totally separate structures -- changing one cannot affect the other.

Structures are basically a big block of bytes in memory which are laid out in a specific way. You define the way a structure is laid out via the structure editor. You'll notice that when you add fields to a structure, the IDE tells you two important pieces of information: an offset and a size. Let's prove to ourselves that structures are laid out in the proper fashion.

First, make a new module (structures can only reside in a module as of 2007r1, and they can only be public in scope). Then, make a new structure by going to Project->Add->Structure. Name it "Test" and make sure that its scope is Public.

Now you'll be presented with a familiar looking UI which allows you to define the contents of the structure. We're going to add three fields: an Integer, a string of 10 bytes, and a Double. You do this by clicking the + sign and adding the declaration. Your declarations should look like this:

i as Integer
s as String * 10
d as Double

What you will end up with is a 22 byte structure that looks like this:

The structure editor in REALbasic

Now that we've defined the structure, we're ready to use it! As you can see, defining a structure is a breeze. What we end up with is a brand-new datatype that we're able to declare variables as. So go to the Open event of the window, and enter this code:

dim s as Test
s.i = 5
s.s = "Awesome"
s.d = 1.2

MsgBox s.s

When you run the project, you should get a dialog box popping up that says "Awesome" in it. But that isn't verifying the layout of the structure in memory. Remember from the previous post that Ptr has this awesome feature which allows you to dereference pointers in a type-safe manner? Well, let's try that out!

We're going to make a new Ptr, but we're going to cheat and let the MemoryBlock do all of the memory allocation and management for us. We can do this because MemoryBlock has an automatic conversion to and from the Ptr datatype. So make a new Ptr, and allocate memory for it by using the MemoryBlock. Then we want to assign our structure to the Ptr, which we can do by using p.Test. Finally, we notice that Test.d is at offset 14, so we'll grab the double from p directly and display it. Your code will look like this:

dim s as Test
s.i = 5
s.s = "Awesome"
s.d = 1.2

dim mb as new MemoryBlock( 1024 )
dim p as Ptr = mb
p.Test = s
MsgBox CStr( p.Double( 14 ) )

You may be wondering why I didn't simply assign new MemoryBlock to the Ptr, like this:

// Don't do this, it's wrong
Dim p as Ptr = new MemoryBlock( 1024 )

That's because we'd crash otherwise. Before you go blaming REALbasic, take a moment to think about what's happening here. A MemoryBlock is being constructed, and we're assigning its internal pointer to data to p. Then, on the next statement to be executed, the MemoryBlock goes out of scope and is destroyed, taking its internal pointer with it. Now we have what's called a dangling pointer stored in p -- trying to use it only causes heartache. But if we assign the MemoryBlock reference to a local variable, the memory will remain valid until the method exits, and we're fine.

Ok, enough about that -- run the project, and you'll see that the value we stuff into our structure is indeed laid out in memory exactly how we'd like to see it. That means that we can use structures and ptrs together to work on our ultimate goal: making the templates for our dialog resource to use with DialogBoxIndirect.

11 Comments

Good article, but can you drop the distracting exaggerated narratives such as "Sounds scary, eh? It's not"?

Yeah Aaron, when your providing this free info, can you also dress up in a tight mini skirt and get me a friggin beer?

Sheesh, the gratitude of some people.

@DeanG -- sorry. Since it's not an article, I tend to take a more conversational approach to these things.

Looks like there may be a bug with Doubles on the Mac side (PPC 10.4.8)

dim s as Test
s.i = 5
s.s = "Awesome"
s.d = 1.2

dim mb as new MemoryBlock( 1024 )
dim p as Ptr = mb
p.Test = s
MsgBox CStr( p.Double( 14 ) ) ' shows 0
MsgBox CStr( p.Test.d ) ' shows 0
MsgBox p.Test.s ' shows "Awesome" as expected
MsgBox Str(p.Integer) ' shows 5 as expected

Also although p.String autocompletes:

MsgBox P.String(4)

won't compile and says the method or property (P.string) does not exist

- Karen

That may very well be a bug -- good catch. You should report it via the feedback system (and attach your project).

As for Ptr.String, that's actually a bug with autocomplete. Ptr does not allow you to get a string back (there's no compiler support for it). Mars and I discussed it when I was writing this example, and we're mulling over some ideas for it. It's a sensible feature request, so you're welcome to file one if you'd like.

I submitted the bug report (pxilmmig ) and found out things were stranger than I thought ... at least on a Mac

I changed the the above code to:
dim s as Test
s.i = 5
s.s = "Awesome"
s.d = 1.2

dim mb as new MemoryBlock( 1024 )
dim p as Ptr = mb
p.Test = s

MsgBox p.Test.s ' MsgBox shows 33 instead of "Awesome" !!!!!!

MsgBox CStr( p.Double( 14 ) ) 'MsgBox shows 0 should be 1.2
MsgBox CStr( p.Test.d ) ' MsgBox shows 0 should be 1.2
MsgBox p.Test.s ' MsgBox shows "Awesome" as expected
MsgBox Str(p.Integer) ' MsgBox shows 5 as expected

MsgBox CStr( p.Double( 14 ) ) 'MsgBox shows 1.2 as it should!!!!!

And found that the results i got seemed to depend on the order of accessing the data through the Ptr !!!!

Looks like a really nasty bug for those using this technique, at least on the Mac side!

That does seem strange -- good catches. The somewhat good news is that this doesn't affect the entire reason for using this since it's a Win32 API. The bad news is, something strange is happening on the Mac.

Windows (at least XP on VPC) although better is not totally immune either...

With the above code the first msgbox: MsgBox p.Test.s
should show "Awesome" but I see a lower case o with an accent over it followed by a question mark!


The rest of the msgboxes display the correct values.

I'm not certain that it is a bug you're seeing there. Since the string isn't null terminated, the MsgBox probably doesn't know where to stop showing you parts of the string. I bet if you null terminated the string, it'd work. Also, if you assigned the string to a local string variable and then MsgBox that, does that work?

On XP in VPC:

dim s as Test
s.i = 5
s.s = "Awesome"
s.d = 1.2

dim mb as new MemoryBlock( 1024 )
dim p as Ptr = mb
p.Test = s

Dim Str as String = p.Test.s
MsgBox Str ' Shows the wrong characters
MsgBox CStr( p.Double( 14 ) ) 'MsgBox shows 1.2
MsgBox CStr( p.Test.d ) ' MsgBox shows 1.2
MsgBox p.Test.s ' MsgBox shows "Awesome"
MsgBox Str(p.Integer) ' MsgBox shows 5 as expected

MsgBox CStr( p.Double( 14 ) ) 'MsgBox shows 1.2


That code still gives me a lower case o with an accent over it followed by a question mark in the first MsgBox as does using:

s.s = "Awesome" + Encodings.UTF8.Chr(0)

to assign the string to the structure variable S

But also on XP
Dim Str as String = DefineEncoding(p.Test.s, Encodings.UTF8)
MsgBox Str ' Shows "Awesome"

works fine... On XP it seems the first time one accesses p.Test.S RB messes up encoding but on subsequent calls it gets it right...

No matter what I do on the Mac I get "33" in the first Msgbox

That last post was me!!!!

Leave a comment

Disclaimer

I'm currently an employee of REAL Software. My blog is mine. The opinions represented in this blog are mine as well and may not represent my employer's opinions. All original material is copyrighted and property of the author.

REALbasic® is a registered trademark of REAL Software, Inc. REAL SQL Server™ and Lingua™ are pending trademarks of REAL Software, Inc. All rights reserved.