Get object from its object-specific class?

Hello,

is there a way to get an object, if only its object-specific class is
given? For instance, given

  c = class << Hash[:a, 5]
    def mult(x)
      self[:a] * x
    end
    self
  end

can I retrieve the hash object via evaluation of c?

Regards
  Thomas

harp:~ > cat a.rb
   c = class << Hash[:a, 5]
     def mult(x)
       self[:a] * x
     end
     self
   end

   p c.instance_eval{ self }

   harp:~ > ruby a.rb
   #<Class:#<Hash:0xb75cfd18>>

if you are doing much of this sort of thing check out the prototype lib

harp:~ > cat a.rb
require 'rubygems'
require 'prototype'

c = Object.prototype(Hash) do
   def mult(x)
     self[:a] * x
   end

   def initialize
     self[:a] = 5
   end
end

p c

harp:~ > ruby a.rb
{:a=>5}

-a

···

On Sun, 11 Mar 2007, Thomas Hafner wrote:

Hello,

is there a way to get an object, if only its object-specific class is
given? For instance, given

c = class << Hash[:a, 5]
   def mult(x)
     self[:a] * x
   end
   self
end

can I retrieve the hash object via evaluation of c?

--
be kind whenever possible... it is always possible.
- the dalai lama

ara.t.howard@noaa.gov wrote/schrieb <Pine.LNX.4.62.0703110817390.31042@harp.ngdc.noaa.gov>:

   p c.instance_eval{ self }

Thanks a lot.

if you are doing much of this sort of thing check out the prototype lib

I'll have a look, thanks.

Regards
  Thomas

I thought we wanted the result to be #<Hash:0xb75cfd18>?

T.

···

On Mar 11, 10:23 am, ara.t.how...@noaa.gov wrote:

On Sun, 11 Mar 2007, Thomas Hafner wrote:
> Hello,

> is there a way to get an object, if only its object-specific class is
> given? For instance, given

> c = class << Hash[:a, 5]
> def mult(x)
> self[:a] * x
> end
> self
> end

> can I retrieve the hash object via evaluation of c?

   harp:~ > cat a.rb
   c = class << Hash[:a, 5]
     def mult(x)
       self[:a] * x
     end
     self
   end

   p c.instance_eval{ self }

   harp:~ > ruby a.rb
   #<Class:#<Hash:0xb75cfd18>>

Trans schrieb:

···

On Sun, 11 Mar 2007, Thomas Hafner wrote:

is there a way to get an object, if only its object-specific class is
given?

I thought we wanted the result to be #<Hash:0xb75cfd18>?

Tom, I thought so, too, but I didn't want to show my solution before there's a demand for it :slight_smile:

Regards,
Pit

rick@frodo:/public/rubyscripts$ ruby object_from_sc.rb
#<Class:#<Hash:0xb7df1774>>
{:a=>5}
oXb7df1774

And here's how:

rick@frodo:/public/rubyscripts$ cat object_from_sc.rb
# this method returns an integer value of the address of an object
with a standard
# object id.
# Note that internally Ruby FixedNums are stored as a value which is
the integer value
# shifted left one bit with a 1 bit ored into the LSB.
# For most objects the object_id is just the address with the LSB set.
This method
# undoes this magic to reveal the actual address.
def hex_oid(obj)
  "oX#{(obj.object_id*2 & 0xffffffff).to_s(16)}"
end

c2 = class << {:a => 5}
  def mult(x)
    self[:a] * x
  end
  self
end

p c2

ObjectSpace.each_object(c2) {|o| p o; puts hex_oid(o)}

···

On 3/13/07, Trans <transfire@gmail.com> wrote:

hought we wanted the result to be #<Hash:0xb75cfd18>?

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

By the way.

I was a little concerned that having just a reference to a singleton
class without a reference to its single instance might not protect
that instance from being GCed.

I took a quick look through the ruby1.8 source and it looks like
singleton class objects have a hidden instance variable named
__attached__ which refers to the instance. This is what it uses to
compute it's string representation for example.

I don't think that theres any way to get at that through Ruby however,
for example instance_variable_get(:"__attached__") complains that
__attached__ is not a legal instance variable name.

So I guess you could write a C extension which accessed that hidden
variable, but in pure Ruby scanning using ObjectSpace.each_object
works albeit potentially less efficiently.

···

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

IPMS/USA Region 12 Coordinator
http://ipmsr12.denhaven2.com/

Visit the Project Mercury Wiki Site
http://www.mercuryspacecraft.com/

Rick DeNatale schrieb:

ObjectSpace.each_object(c2) {|o| p o}

Yeah Rick, this is what I came up with at first, too. But I didn't like that it examines each Ruby object. Here's another solution, much uglier but also faster:

   def instance_of_singleton_class_1(sc)
     ObjectSpace.each_object(sc) do |obj|
       return obj
     end
   end

   def instance_of_singleton_class_2(sc)
     obj = nil
     sc.class_eval do
       if instance_methods(false).include?("singleton_method_added")
         org = instance_method("singleton_method_added")
         remove_method("singleton_method_added")
       end
       define_method("singleton_method_added") do |m|
         obj = self
       end
       remove_method("singleton_method_added")
       define_method("singleton_method_added", org) if org
     end
     obj
   end

Test that they both work the same:

   sc = class << {:a => 5}; self; end

   p instance_of_singleton_class_1(sc) # => {:a=>5}
   p instance_of_singleton_class_2(sc) # => {:a=>5}

And the benchmark:

   require "benchmark"

   Benchmark.bmbm do |bm|
     bm.report "ObjectSpace" do
       1000.times do instance_of_singleton_class_1(sc) end
     end
     bm.report "singleton_method_added" do
       1000.times do instance_of_singleton_class_2(sc) end
     end
   end

yields:

   Rehearsal ----------------------------------------------------------
   ObjectSpace 3.109000 0.000000 3.109000 ( 3.188000)
   singleton_method_added 0.266000 0.000000 0.266000 ( 0.265000)
   ------------------------------------------------- total: 3.375000sec

                                user system total real
   ObjectSpace 3.016000 0.000000 3.016000 ( 3.032000)
   singleton_method_added 0.266000 0.000000 0.266000 ( 0.266000)

Regards,
Pit

I imagine ObjectSpace._id2ref and parsing inspect would work.

T.

···

On Mar 13, 11:38 am, Pit Capitain <p...@capitain.de> wrote:

Rick DeNatale schrieb:

> ObjectSpace.each_object(c2) {|o| p o}

Yeah Rick, this is what I came up with at first, too. But I didn't like
  that it examines each Ruby object. Here's another solution, much
uglier but also faster:

   def instance_of_singleton_class_1(sc)
     ObjectSpace.each_object(sc) do |obj|
       return obj
     end
   end

   def instance_of_singleton_class_2(sc)
     obj = nil
     sc.class_eval do
       if instance_methods(false).include?("singleton_method_added")
         org = instance_method("singleton_method_added")
         remove_method("singleton_method_added")
       end
       define_method("singleton_method_added") do |m|
         obj = self
       end
       remove_method("singleton_method_added")
       define_method("singleton_method_added", org) if org
     end
     obj
   end

Test that they both work the same:

   sc = class << {:a => 5}; self; end

   p instance_of_singleton_class_1(sc) # => {:a=>5}
   p instance_of_singleton_class_2(sc) # => {:a=>5}

And the benchmark:

   require "benchmark"

   Benchmark.bmbm do |bm|
     bm.report "ObjectSpace" do
       1000.times do instance_of_singleton_class_1(sc) end
     end
     bm.report "singleton_method_added" do
       1000.times do instance_of_singleton_class_2(sc) end
     end
   end

yields:

   Rehearsal ----------------------------------------------------------
   ObjectSpace 3.109000 0.000000 3.109000 ( 3.188000)
   singleton_method_added 0.266000 0.000000 0.266000 ( 0.265000)
   ------------------------------------------------- total: 3.375000sec

                                user system total real
   ObjectSpace 3.016000 0.000000 3.016000 ( 3.032000)
   singleton_method_added 0.266000 0.000000 0.266000 ( 0.266000)

   def instance_of_singleton_class_2(sc)

not only is this method faster but it works in a situation where the
ObjectSpace does not. Consider this rspec test pasted in from a personal
introspection lib. It will work with this method but not with the
ObjectSpace:

  def singleton
     class<<self;self;end
  end

  def singleton_ancestors
    result = []
    return result unless pointer = singleton_parent
    result << self
    begin
      result << pointer
    end while pointer = pointer.singleton_parent
    result
  end

    it "should get singletons back in the correct order" do
      parent = ""
      a = parent.singleton
      b = a.singleton
      c = b.singleton
      d = c.singleton
      e = d.singleton
      f = e.singleton
      f.singleton_ancestors.should == [ f, e, d, c, b, a, parent ]
    end

···

--
Posted via http://www.ruby-forum.com/.

Trans schrieb:

I imagine ObjectSpace._id2ref and parsing inspect would work.

Too easy, Tom, much too easy :slight_smile: Yes, this should work, too, of course. The only gotchas are that you should make sure that the original #to_s method is used, and you'd have to add a special case for singleton classes of modules:

   p class << Module; self; end # => #<Class:Module>

Regards,
Pit

Two approaches with a benchmark.

And as I surmised, there's a really quick way to do this with a
relatively simple extension (at least for ruby1.8, I haven't looked at
this on 1.9).

rick@frodo:/public/rubyscripts/getsinginst$ cat get_sing_inst.c
#include "ruby.h"

static VALUE singleton_instance(VALUE obj)
{
  if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON)) {
      return rb_iv_get(obj, "__attached__");
  }
  rb_raise(rb_eTypeError, "not a singleton class");
}

void Init_get_sing_inst()
{
  rb_define_method(rb_cClass, "singleton_instance", singleton_instance, 0);

}

rick@frodo:/public/rubyscripts/getsinginst$ cat gsi.rb
require 'get_sing_inst'

h = {:a => 1}
sc = class << h
    def foo
    end

    self
   end

p sc.singleton_instance
p sc.singleton_instance.object_id == h.object_id

def instance_of_singleton_class_1(sc)
    ObjectSpace.each_object(sc) do |obj|
      return obj
    end
  end

  def instance_of_singleton_class_2(sc)
    obj = nil
    sc.class_eval do
      if instance_methods(false).include?("singleton_method_added")
        org = instance_method("singleton_method_added")
        remove_method("singleton_method_added")
      end
      define_method("singleton_method_added") do |m|
        obj = self
      end
      remove_method("singleton_method_added")
      define_method("singleton_method_added", org) if org
    end
    obj
  end

require "benchmark"

  Benchmark.bmbm do |bm|
    bm.report "ObjectSpace" do
      10000.times do instance_of_singleton_class_1(sc) end
    end
    bm.report "singleton_method_added" do
      10000.times do instance_of_singleton_class_2(sc) end
    end
    bm.report "primitive" do
      10000.times do sc.singleton_instance end
    end
  end

And now for the benchmark results, note that I'm doing 10 times as
many interations as Trans did, 10000 vs. 1000, the primitive is on the
order of two orders of magnitude faster than either of the other
approaches.

rick@frodo:/public/rubyscripts/getsinginst$ ruby gsi.rb
{:a=>1}
true
Rehearsal ----------------------------------------------------------
ObjectSpace 3.270000 0.100000 3.370000 ( 3.469175)
singleton_method_added 4.450000 0.110000 4.560000 ( 4.580452)
primitive 0.040000 0.000000 0.040000 ( 0.046098)
------------------------------------------------- total: 7.970000sec

                             user system total real
ObjectSpace 2.300000 0.050000 2.350000 ( 2.352730)
singleton_method_added 4.430000 0.070000 4.500000 ( 4.510719)
primitive 0.040000 0.000000 0.040000 ( 0.037970)

···

On 3/13/07, Trans <transfire@gmail.com> wrote:

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Hi --

···

On Fri, 21 Mar 2008, Rob Pitt wrote:

   def instance_of_singleton_class_2(sc)

not only is this method faster but it works in a situation where the
ObjectSpace does not. Consider this rspec test pasted in from a personal
introspection lib. It will work with this method but not with the
ObjectSpace:

def singleton
    class<<self;self;end
end

def singleton_ancestors
   result = []
   return result unless pointer = singleton_parent

I think you forgot to define singleton_parent.

David

--
Upcoming Rails training from David A. Black and Ruby Power and Light:
   ADVANCING WITH RAILS, April 14-17 2008, New York City
   CORE RAILS, June 24-27 2008, London (Skills Matter)
See http://www.rubypal.com for details. Berlin dates coming soon!

Pit Capitain schrieb:

The only gotchas are that you should make sure that the original #to_s method is used, and you'd have to add a special case for singleton classes of modules:

  p class << Module; self; end # => #<Class:Module>

Tom, sorry for the noise, but you can create pathological situations where you can't get at the module via its name:

   obj = M = Module.new
   obj.to_s

   Object.class_eval do
     remove_const "M"
   end

   p class << obj; self; end # => #<Class:M>
   p Object.const_get("M") rescue p $! # => #<NameError: M>

Maybe someone doing this deserves some troubles...

Regards,
Pit

Actually, you mean Pit. He did the benchmarks. This is definitely the
way to do it right though. I would submit this to ruby-core mailing
list and see how it takes over there.

T.

···

On Mar 13, 1:56 pm, "Rick DeNatale" <rick.denat...@gmail.com> wrote:

On 3/13/07, Trans <transf...@gmail.com> wrote:
Two approaches with a benchmark.

And as I surmised, there's a really quick way to do this with a
relatively simple extension (at least for ruby1.8, I haven't looked at
this on 1.9).

rick@frodo:/public/rubyscripts/getsinginst$ cat get_sing_inst.c
#include "ruby.h"

static VALUE singleton_instance(VALUE obj)
{
  if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON)) {
      return rb_iv_get(obj, "__attached__");
  }
  rb_raise(rb_eTypeError, "not a singleton class");

}

void Init_get_sing_inst()
{
  rb_define_method(rb_cClass, "singleton_instance", singleton_instance, 0);

}

rick@frodo:/public/rubyscripts/getsinginst$ cat gsi.rb
require 'get_sing_inst'

h = {:a => 1}
sc = class << h
    def foo
    end

    self
   end

p sc.singleton_instance
p sc.singleton_instance.object_id == h.object_id

def instance_of_singleton_class_1(sc)
    ObjectSpace.each_object(sc) do |obj|
      return obj
    end
  end

  def instance_of_singleton_class_2(sc)
    obj = nil
    sc.class_eval do
      if instance_methods(false).include?("singleton_method_added")
        org = instance_method("singleton_method_added")
        remove_method("singleton_method_added")
      end
      define_method("singleton_method_added") do |m|
        obj = self
      end
      remove_method("singleton_method_added")
      define_method("singleton_method_added", org) if org
    end
    obj
  end

require "benchmark"

  Benchmark.bmbm do |bm|
    bm.report "ObjectSpace" do
      10000.times do instance_of_singleton_class_1(sc) end
    end
    bm.report "singleton_method_added" do
      10000.times do instance_of_singleton_class_2(sc) end
    end
    bm.report "primitive" do
      10000.times do sc.singleton_instance end
    end
  end

And now for the benchmark results, note that I'm doing 10 times as
many interations as Trans did, 10000 vs. 1000, the primitive is on the
order of two orders of magnitude faster than either of the other
approaches.

No, this was deliberate. Maybe it was a mistake... My singleton_parent
retrieves __attached__, but I was saying Pit's
instance_of_singleton_class_2 method works as a replacement for this
where as ObjectSpace does not. Here, I've refactored the
instance_of_singleton_class_2 into a singleton_parent method for you:

  def singleton_parent
    # __instance_variable_get__( '__attached__' )
    return nil unless is_a? Class
    obj = nil
    class_eval do
      if instance_methods( false ).include?( "singleton_method_added" )
        org = instance_method( :singleton_method_added )
        remove_method( :singleton_method_added )
      end
      define_method( :singleton_method_added ) do |m|
        obj = self
      end
      remove_method( :singleton_method_added )
      define_method( :singleton_method_added, org) if org
    end
    obj
  end

···

I think you forgot to define singleton_parent.

David

--
Posted via http://www.ruby-forum.com/.