views:

45

answers:

3

Short version: The default inspect method for a class displays the object's address.* How can I do this in a custom inspect method of my own?

*(To be clear, I want the 8-digit hex number you would normally get from inspect. I don't care about the actual memory address. I'm just calling it a memory address because it looks like one. I know Ruby is memory-safe.)

Long version: I have two classes, Thing and ThingList. ThingList is a subclass of Array specifically designed to hold Things. Due to the nature of Things and the way they are used in my program, Things have an instance variable @container that points back to the ThingList that holds the Thing.

It is possible for two Things to have exactly the same data. Therefore, when I'm debugging the application, the only way I can reliably differentiate between two Things is to use inspect, which displays their address. When I inspect a Thing, however, I get pages upon pages of output because inspect will recursively inspect @container, causing every Thing in the list to be inspected as well!

All I need is the first part of that output. How can I write a custom inspect method on Thing that will just display this?

#<Thing:0xb7727704>

EDIT: I just realized that the default to_s does exactly this. I didn't notice this earlier because I have a custom to_s that provides human-readable details about the object.

Assume that I cannot use to_s, and that I must write a custom inspect.

+2  A: 

You can get the address using object_id and multiplying it by 2* and display it in hex using sprintf (aka %):

"#<Thing:0x%08x>" % (object_id * 2)

Of course, as long as you only need the number to be unique and don't care that it's the actual address, you can just leave out the * 2.

* For reasons that you don't need to understand (meaning: I don't understand them), object_id returns half the object's memory address, so you need to multiply by 2 to get the actual address.

sepp2k
Strictly speaking, I don't think that [object_id](http://ruby-doc.org/core/classes/Object.html#M000339) returns the object address, but the unique identifier should solve the OP's problem.
maerics
@maerics: In MRI it definitely does (for all objects excepts Integers, Symbols, true, false and nil).
sepp2k
Hmm, perhaps on some versions; my nearest MRI (1.8.7 darwin) definitely shows different values between the default `inspect` and `object_id`.
maerics
maerics seems to be right, at least in my Ruby interpreter. While the object_ids for Integers, Symbols, true, false and nil certainly are special (namely, they remain the same each time you run the program), the object_ids for everything else are not equal to the object addresses. They are uniquely identifiable, though, and it is the easiest solution to implement so far, so +1.
Exp HP
@maerics: You're right, object_id returns `(obj|FIXNUM_FLAG)`, which is the address divided by 2. Easy enough to fix though, by multiplying the object_id by 2.
sepp2k
Although, upon looking through the current MRI 1.9.2 source, it appears that the object pointer value _is_ used for object_id (for non-immediate types) so perhaps the difference in reported values has to do with printf's treatment of "%p" vs "%x". Anyway, agreed, sepp2k's solution is much better =)
maerics
@maerics: It's not because of printf. The reason the number is different, is that it doesn't properly convert the pointer to a ruby int. Instead of calling using LONG2FIXNUM (or whatever that was called), it jost `|`s the FIXNUM_FLAG (aka 1) to the pointer. This does make it a valid Fixnum, but not one which represents the actual pointer value (it represents exactly half of the pointer value).
sepp2k
@sepp2k: you're absolutely right. We must have been reading "gc.c" and "ruby.h" at the exact same time =)
maerics
Exp HP
A: 

Instead of subclassing Array your class instances could delegate to one for the desired methods so that you don't inherit the overridden inspect method.

maerics
A: 

This is impossible. There is no way in Ruby to get the memory address of an object, since Ruby is a memory-safe language which has (by design) no methods for accessing memory directly. In fact, in many implementations of Ruby, objects don't even have a memory address. And in most of the implementations that do map objects directly to memory, the memory address potentially changes after every garbage collection.

The reason why using the memory address as an identifier in current versions of MRI and YARV accidentally works, is because they have a crappy garbage collector implementation that never defragments memory. All other implementations have garbage collectors which do defragment memory, and thus move objects around in memory, thereby changing their address.

If you tie your implementation to the memory address, your code will only ever work on slow implementations with crappy garbage collectors. And it isn't even guaranteed that MRI and YARV will always have crappy garbage collectors, in fact, in both implementations the garbage collector has been identified as one of the major performance bottlenecks and it is safe to assume that there will be changes to the garbage collectors. There are already some major changes to YARV's garbage collector in the SVN, which will be part of YARV 1.9.3 and YARV 2.0.

If you want an ID for objects, use Object#object_id.

Jörg W Mittag
Should MRI/YARV have used object id rather than memory address for inspect?
Andrew Grimm