Instance_exec

i seems to recall someone came up with an impl of instance_exec
(instance_eval
that takes args) a while back - might have even been me! :wink: anyone
remember?

-a

On Sat, Jul 08, 2006 at 02:22:31AM +0900, [email protected] wrote:

i seems to recall someone came up with an impl of instance_exec
(instance_eval
that takes args) a while back - might have even been me! :wink: anyone
remember?

There is one in Rails’ ActiveSupport:

class Object
unless defined? instance_exec # 1.9
def instance_exec(*arguments, &block)
block.bind(self)[*arguments]
end
end
end

class Proc
def bind(object)
block, time = self, Time.now
(class << object; self end).class_eval do
method_name = “_bind#{time.to_i}_#{time.usec}”
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end

marcel

On 7/7/06, [email protected] [email protected] wrote:

i seems to recall someone came up with an impl of instance_exec (instance_eval
that takes args) a while back - might have even been me! :wink: anyone remember?

-a

You did indeed! And so did I. However, Mauricio F. came up with
what seems to be a fairly complete implementation (thread safe,
handles frozen objects, etc.) at:

http://eigenclass.org/hiki.rb?cmd=view&p=instance_exec&key=instance_exec

Regards,
Sean

On 7/7/06, Marcel Molina Jr. [email protected] wrote:

unless defined? instance_exec # 1.9
method_name = “_bind#{time.to_i}_#{time.usec}”
Marcel Molina Jr. [email protected]

This is not thread-safe - relying on time.usec may not work on
sufficiently fast or clock-impaired machines.

Regards,
Sean

On Sat, 8 Jul 2006, Marcel Molina Jr. wrote:

unless defined? instance_exec # 1.9
method_name = “_bind#{time.to_i}_#{time.usec}”
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end

thanks! i just hacked this one out:

harp:~ > cat a.rb
class Object
unless instance_methods.include? ‘instance_exec’
def instance_exec *a, &b
m = “instance_exec#{ Thread.current.object_id.abs }_”
singleton_class{ define_method m, &b }
send m, *a
ensure
singleton_class{ undef_method m } rescue nil
end
end
unless instance_methods.include? ‘singleton_class’
def singleton_class &b
sc = class << self; self; end
sc.module_eval &b if b
sc
end
end
end

a = []
a.instance_exec 42 do |elem|
push elem
end
p a

harp:~ > ruby a.rb
[42]

regards.

-a

On 7/7/06, [email protected] [email protected] wrote:

Huh. That looks like the one I wrote a while back (minus the time
stamp).

Mauricio F.'s looks better though --think I’ll have to rip it
for Facets :wink:

T.

Do - it’s the best version for 1.8 so far (IMHO).

Regards,
Sean

Marcel Molina Jr. wrote:

unless defined? instance_exec # 1.9
method_name = “_bind#{time.to_i}_#{time.usec}”
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end

Huh. That looks like the one I wrote a while back (minus the time
stamp).

Mauricio F.'s looks better though --think I’ll have to rip it
for Facets :wink:

T.

On Sat, 8 Jul 2006 [email protected] wrote:

Mauricio F.'s looks better though --think I’ll have to rip it
for Facets :wink:

i just came across a bug in it (it’s not re-entrant) - here’s the fix:

harp:~ > cat a.rb
class Object
unless instance_methods.include? ‘instance_exec’
def instance_exec *a, &b
m, n = nil, -1
loop{
m = “instance_exec#{ Thread.current.object_id.abs }_#{ n
+= 1 }
_”
break unless respond_to? m
}
singleton_class{ define_method m, &b }
send m, *a
ensure
singleton_class{ undef_method m }
end
end

 unless instance_methods.include? 'singleton_class'
   def singleton_class &b
     sc =
       class << self; self; end
     sc.module_eval &b if b
     sc
   end
 end

end

a = []

a.instance_exec 42 do |x|
instance_exec x do |y|
push y
p size
end
end

harp:~ > ruby a.rb
1

figured you’d use this if anyone would tom… cheers.

-a

Thanks Ara.

Do you have a testcase I can use?

T.

On Sun, Jul 09, 2006 at 03:09:52PM +0900, [email protected] wrote:

Mauricio F.'s looks better though --think I’ll have to rip it
for Facets :wink:

i just came across a bug in it (it’s not re-entrant) - here’s the fix:

[#instance_exec implementation]

The example you gave does work with my implementation too:

RUBY_VERSION # => “1.8.5”
RUBY_RELEASE_DATE # => “2006-06-24”
class Object
module InstanceExecHelper; end
include InstanceExecHelper
def instance_exec(*args, &block)
mname =
“_instance_exec#{Thread.current.object_id.abs}_#{object_id.abs}”
InstanceExecHelper.module_eval{ define_method(mname, &block) }
begin
ret = send(mname, *args)
ensure
InstanceExecHelper.module_eval{ remove_method(mname) } rescue
nil
#

                                                  #       thx to 

this
end
ret
end
end

a = []

a.instance_exec 42 do |x|
instance_exec x do |y|
push y
p size
end
end

a # => [42]
a.size # => 1

>> 1

I had actually a test for that (see test_instance_exec_nested below). Do
you
have any additional assertions handy?
(So far test_instance_exec_with_frozen_obj fails in all the other
implementations I’ve seen.)

require ‘test/unit’
class TestInstanceEvalWithArgs < Test::Unit::TestCase
def test_instance_exec
# Create a block that returns the value of an argument and a value
# of a method call to +self+.
block = lambda { |a| [a, f] }

  assert_equal [:arg_value, :dummy_value],
    Dummy.new.instance_exec(:arg_value, &block)
end

def test_instance_exec_with_frozen_obj
  block = lambda { |a| [a, f] }

  obj = Dummy.new
  obj.freeze
  assert_equal [:arg_value, :dummy_value],
    obj.instance_exec(:arg_value, &block)
end

def test_instance_exec_nested
  i = 0
  obj = Dummy.new
  block = lambda do |arg|
    [arg] + instance_exec(1){|a| [f, a] }
  end

  # the following assertion expanded by the xmp filter automagically 

from:
# obj.instance_exec(:arg_value, &block) #=>
assert_equal([:arg_value, :dummy_value, 1],
obj.instance_exec(:arg_value, &block))
end
end

The example you gave does work with my implementation too:

RUBY_VERSION # => “1.8.5”
RUBY_RELEASE_DATE # => “2006-06-24”
class Object
module InstanceExecHelper; end
include InstanceExecHelper
def instance_exec(*args, &block)
mname = “_instance_exec#{Thread.current.object_id.abs}_#{object_id.abs}”

If instance_exec is called recursively, like in the example,
the mname for the outer method is reused for defining the inner
method. This isn’t really a problem, but conceptually not very
“clean”…

Did it work “by accident”, or did you really intent to reuse
the name? ;]

InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil

This “rescue nil” might have been an indication of this “bad
design”… ;]

I wonder, why did you use a module for the context, instead of
the good old meta class?

gegroet,
Erik V. - http://www.erikveen.dds.nl/

Okay, criticizing somebody’s work without providing a
“solution”, isn’t nice at all…

So, here’s my version…

gegroet,
Erik V. - http://www.erikveen.dds.nl/

PS: I’m still curious about the reason for using a module.


class Object
def instance_exec(*args, &block)
object = self
res = nil

 class << self
   self
 end.instance_eval do
   begin
     Thread.critical = true
     @_instance_exec_count_ ||= 0
     @_instance_exec_count_ += 1
     method_name = "_instance_exec_#{@_instance_exec_count_}_"
   ensure
     Thread.critical = false
   end

   begin
     define_method(method_name, &block)

     res = object.send(method_name, *args)
   ensure
     remove_method(method_name) rescue nil
   end
 end

 res

end
end

Anyway, here the version I put in Facets. Please evaluate.

The implementation is thread-safe thanks to the

Thread.current.object_id trick, but it doesn’t work with

immediate values (Fixnums and friends),…

(You’re version does work with immediate versions.)

Mmh… The Module version does work for immediate versions,
too. That might be a good reason… ;]

Mine doesn’t…

…and most

importantly it bombs when given a frozen object.

Object.class_eval{ define_method(mname, &block) }

This pollutes the global class Object. Not funny… ;] But,
yes, it does work…

The Module versions only pollutes the class Object with one,
dedicated Module. That’s not too bad.

If we want to handle immediate values and we can’t create
singleton classes for them, we have to abuse something.

Maybe, after all, the Module version isn’t that bad… ;]

gegroet,
Erik V. - http://www.erikveen.dds.nl/

Erik V. wrote:

Mine doesn’t…

…and most

importantly it bombs when given a frozen object.

Object.class_eval{ define_method(mname, &block) }

This pollutes the global class Object. Not funny… ;] But,
yes, it does work…

It should delete it when done, so it’s a temporary pollution. But I
think to do that right, instead of undef_method that should be
remove_method:

Object.class_eval{ remove_method(mname) }

Good catch thanks.

T.

On Mon, 10 Jul 2006 [email protected] wrote:

PS: I’m still curious about the reason for using a module.

Thread.current.object_id trick, but it doesn’t work with

 Object.class_eval{ undef_method(mname) }

end
ret
end

end

-a

Duh! I’m sitting here screwing with this and the answer’s sitting one
directory over… with only a minor modification (I added recv=nil) I
introduce you to Facets’ Proc#to_method:

#–

Credit for #to_method goes to Forian Gross (flgr).

#–

require ‘thread’

class Proc

MethodMutexes = Hash.new do |hash, key|
  hash[key] = Mutex.new
end

# Creates a local method based on a Proc.
def to_method(name=nil, recv=nil)
  name ||= "!to_method_temp#{self.object_id}"
  recv ||= eval("self", self)
  klass = recv.class
  MethodMutexes[klass => name].synchronize do
    begin
      klass.send(:define_method, name, &self)
      return recv.method(name)
    ensure
      klass.send(:remove_method, name) if not name
    end
  end
end

end

Then instance_exec is simply:

module Kernel

def instance_exec(*args, &block)
  block.to_method(nil,self).call(*args)
end

end

Proof is in the pudding. Facets’ really does pay off!

T.

On Mon, Jul 10, 2006 at 08:29:46PM +0900, [email protected] wrote:

PS: I’m still curious about the reason for using a module.

  • it allows instance_exec to work on frozen objects and immediate values
  • it doesn’t pollute Object with several methods (even if temporarily),
    only
    one module

immediate values (Fixnums and friends), and most

ret

end

end

heh you copied the comment from my blog entry[1] but that’s not the
implementation it applied to ;). The above does work with immediate
values and
frozen objects. But I now consider it flawed nonetheless, read

http://eigenclass.org/hiki.rb?bounded+space+instance_exec

[1] http://eigenclass.org/hiki.rb?instance_exec

On Mon, Jul 10, 2006 at 10:44:32PM +0900, [email protected] wrote:

Duh! I’m sitting here screwing with this and the answer’s sitting one
directory over… with only a minor modification (I added recv=nil) I
introduce you to Facets’ Proc#to_method:

#–

Credit for #to_method goes to Forian Gross (flgr).

                                (Florian)
# Creates a local method based on a Proc.
def to_method(name=nil, recv=nil)
  name ||= "!to_method_temp#{self.object_id}"
              ================================

This can create a lot of different symbols, leading to large memory
leaks.

  recv ||= eval("self", self)
  klass = recv.class
  MethodMutexes[klass => name].synchronize do
    ============================

This makes the memleak even heavier, going from potentially ~70 bytes to
nearly 300 bytes per call…

    begin
      klass.send(:define_method, name, &self)
        ==========

This will fail if the class was frozen.

See my blog for a better implementation.

Erik V. wrote:

Okay, criticizing somebody’s work without providing a
“solution”, isn’t nice at all…

So, here’s my version…

gegroet,
Erik V. - http://www.erikveen.dds.nl/

PS: I’m still curious about the reason for using a module.

I think the idea was to save frm creting a singleton class, but at the
expense of a module I don’t see why either. Anyway, here the version I
put in Facets. Please evaluate.

module Kernel

Like instace_eval but allows parameters to be passed.

The implementation is thread-safe thanks to the

Thread.current.object_id trick, but it doesn’t work with

immediate values (Fixnums and friends), and most

importantly it bombs when given a frozen object.

def instance_exec(*args, &block)
mname =
“__instance_exec(#{Thread.current.object_id},#{caller.object_id})”
Object.class_eval{ define_method(mname, &block) }
begin
ret = send(mname, *args)
ensure
Object.class_eval{ undef_method(mname) }
end
ret
end

end

On Mon, Jul 10, 2006 at 07:33:34PM +0900, Erik V. wrote:

Okay, criticizing somebody’s work without providing a
“solution”, isn’t nice at all…

nah, what matters is improving the code (and focusing on it)

PS: I’m still curious about the reason for using a module.

As I said, it reduces pollution and makes it work with frozen and
immediate
values. See the unit tests and the explanation at
http://eigenclass.org/hiki.rb?instance_exec.

     @_instance_exec_count_ ||= 0

======================
adds an instance variable for each object to which we send the
instance_exec
message (fails for frozen objects, pollutes and costs more memory)

     @_instance_exec_count_ += 1
     method_name = "_instance_exec_#{@_instance_exec_count_}_"
   ensure
     Thread.critical = false
       =======================

The old Thread.critical value is lost, so this could easily break
multi-threaded code… It should be
begin
old, Thread.critical = Thread.critical, true

ensure
Thread.critical = old
end

   end

   begin
     define_method(method_name, &block)

     res = object.send(method_name, *args)
   ensure
     remove_method(method_name) rescue nil
   end
 end

All in all, this implementation is the second best one as far as space
efficiency is concerned :wink:

I don’t like having to point to my own site repeatedly, but you should
really
have a look at

http://eigenclass.org/hiki.rb?bounded+space+instance_exec

So far all the implementations I’ve seen posted were worse than the one
shown
there (and than the original one in my older blog entry). It’s the only
thread-safe, reentrant, bounded-space, frozen-object-safe implementation
as
far as I know.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs