Getting the binding of the caller

M

Michael Neumann

Hi,

Is there a way to get the binding of the caller?

class A
def m(b=binding)
eval("self", b)
end
end

A.new.m # => #<A:0x81e1bec>
A.new.m(binding) # => main

What I want is exactly the second, but without specifying "binding"
explicitly.

Possible or not?

Regards,

Michael
 
F

Florian Gross

Michael said:
Hi,
Moin!

Is there a way to get the binding of the caller?

Yup, there is. See the attached file. It's a bit obscure, but it works
and shouldn't be too slow.
Regards,
Michael

More regards,
Florian Gross


begin
require 'simplecc'
rescue LoadError
def Continuation.create(*args, &block)
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
result ||= args
return *[cc, *result]
end
end

# This method returns the binding of the method that called your
# method. It will raise an Exception when you're not inside a method.
#
# It's used like this:
# def inc_counter
# Binding.of_caller do |binding|
# eval("counter += 1", binding)
# end
# end
# counter = 0
# 2.times { inc_counter }
# counter # => 2
#
# You will have to put the whole rest of your method into the
# block that you pass into this method. If you don't do this
# an Exception will be raised. Because of the way that this is
# implemented it has to be done this way. If you don't do it
# like this it will raise an Exception.
def Binding.of_caller(&block)
old_critical = Thread.critical
Thread.critical = true
count = 0
cc, result, error = Continuation.create(nil, nil)
error.call if error

tracer = lambda do |*args|
type, context = args[0], args[4]
if type == "return"
count += 1
# First this method and then calling one will return --
# the trace event of the second event gets the context
# of the method which called the method that called this
# method.
if count == 2
# It would be nice if we could restore the trace_func
# that was set before we swapped in our own one, but
# this is impossible without overloading set_trace_func
# in current Ruby.
set_trace_func(nil)
cc.call(eval("binding", context), nil)
end
elsif type != "line"
set_trace_func(nil)
error_msg = "Binding.of_caller used in non-method context or " +
"trailing statements of method using it aren't in the block."
cc.call(nil, lambda { raise(ArgumentError, error_msg ) })
end
end

unless result
set_trace_func(tracer)
return nil
else
Thread.critical = old_critical
yield result
end
end
 
G

Gavin Sinclair

Is there a way to get the binding of the caller?
class A
def m(b=binding)
eval("self", b)
end
end
A.new.m # => #<A:0x81e1bec>
A.new.m(binding) # => main
What I want is exactly the second, but without specifying "binding"
explicitly.
Possible or not?

It's been discussed on -talk this year, so a search in the archive for
'binding' should help. Someone posted a continuations-based solution
that gives you basically what you want, but at considerable runtime
expense, IIRC.

There's no plain-Ruby solution; i.e. the language doesn't go out of
its way to help you here. There's an RCR for it; can't remember which
one.

However, blocks can be useful here, and may just be good enough for
you.

def trace(&block)
expr = block.call
value = eval expr, block
puts "#{expr} = #{value}"
end

def foo
x = 5
y = 12
trace { "Math.sqrt( x**2 + y**2 )" }
end

I gotta say that's pretty handy.

Cheers,
Gavin
 
M

Michael Neumann

Florian said:
Yup, there is. See the attached file. It's a bit obscure, but it works
and shouldn't be too slow.

Thanks for the quick answer... uhm, but it does seem to behave as it should:

require 'binding_of_caller'
class A
def m
Binding.of_caller do |binding|
eval("self", binding)
end
end
end

p A.new.m # => #<A:0x8115a14>

I expect "self" to be "main".

Regards,

Michael
 
M

Michael Neumann

Gavin said:
It's been discussed on -talk this year, so a search in the archive for
'binding' should help. Someone posted a continuations-based solution
that gives you basically what you want, but at considerable runtime
expense, IIRC.

There's no plain-Ruby solution; i.e. the language doesn't go out of
its way to help you here. There's an RCR for it; can't remember which
one.

However, blocks can be useful here, and may just be good enough for
you.

Thanks, this trick did it for me. Of course, it's not 100% perfect but
that doesn't matter.

Regards,

Michael
 
M

Mauricio Fernández

Hi,

Is there a way to get the binding of the caller?

class A
def m(b=binding)
eval("self", b)
end
end

A.new.m # => #<A:0x81e1bec>
A.new.m(binding) # => main

What I want is exactly the second, but without specifying "binding"
explicitly.

Possible or not?

Sure.
Take a look at Florian Groß' Binding.of_caller at
http://noegnud.sourceforge.net/flgr/binding_of_caller.rb

In some contexts, you can avoid the overhead of #callcc, but you get an
ugly interface in exchange.
 
F

Florian Gross

Michael said:
Thanks for the quick answer... uhm, but it does seem to behave as it
should:

require 'binding_of_caller'
class A
def m
Binding.of_caller do |binding|
eval("self", binding)
end
end
end

p A.new.m # => #<A:0x8115a14>

I expect "self" to be "main".

That's weird -- can't find an explanation for it, because the following
works:

require 'binding_of_caller'
this = self
class A
def m
Binding.of_caller do |binding|
eval("this", binding)
end
end
end

p A.new.m # => main

Maybe somebody has the knowledge needed to explain what's going on there?
Regards,
Michael

More regards,
Florian Gross
 
M

Michael Neumann

Florian said:
That's weird -- can't find an explanation for it, because the following
works:

require 'binding_of_caller'
this = self
class A
def m
Binding.of_caller do |binding|
eval("this", binding)
end
end
end

p A.new.m # => main

Maybe somebody has the knowledge needed to explain what's going on there?

Hm, it still doesn't work in my special case, as I get a
"Binding.of_caller used in non-method context or trailing statements of
method using it aren't in the block." exception (and I really don't
understand how this Binding.of_caller really works ;-):

require 'binding_of_caller'

class Decorator
def initialize(&block)
@block = block
end

def >>(meth_id)
obj = Binding.of_caller do |binding|
eval("this", binding)
end
old = method(meth_id)
new = @block.call(old)

# the line below is why I need the Binding.of_caller
obj.class.send:)define_method, meth_id, new)
end
end

def wrapwith(value)
Decorator.new {|old|
proc {|*args|
print "##", value, "\n"
old.call(*args)
}
}
end

wrapwith(42) >>
def f(x) x * 2 end

p f(4)

##############

Regards,

Michael
 
F

Florian Gross

Michael said:
Hm, it still doesn't work in my special case, as I get a
"Binding.of_caller used in non-method context or trailing statements of
method using it aren't in the block." exception (and I really don't
understand how this Binding.of_caller really works ;-):

It works by installing a temporary trace_func. trace_funcs get an event
on returns from methods and the binding of the context you where in
before the method returned. Using continuations Binding.of_caller will
first let method return, grab the binding of the caller, go back to the
Binding.of_caller call and this time execute the block. If I wouldn't
raise the exception you mentioned above then statements that aren't in
the block would be executed twice. This is why there can be no code
outside of the Binding.of_caller block after the call to it.

This ought to work, however I find the 'this = self' line quite ugly --
I still need to find out why just using self doesn't work and if it can
be solved. (It appears like the binding which I get from the trace_func
is nested into the original binding which is why variables can be
accessed, but not the original binding itself which is why self and
method calls don't work.)

I couldn't test this code (haven't applied your def -> symbol patch) so
there might still be a bug in it that I overlooked.

require 'binding_of_caller'

class Decorator
def initialize(&block)
@block = block
end

def >>(meth_id)
Binding.of_caller do |context|
obj = eval("this", context)
old = obj.method(meth_id)
new = @block.call(old)

# the line below is why I need the Binding.of_caller
obj.class.send:)define_method, meth_id, new)
end
end
end

def wrapwith(value)
Decorator.new {|old|
proc {|*args|
print "##", value, "\n"
old.call(*args)
}
}
end

this = self
wrapwith(42) >>
def f(x) x * 2 end

p f(4)
Regards,
Michael

More regards,
Florian Gross
 
M

Michael Neumann

Florian said:
It works by installing a temporary trace_func. trace_funcs get an event
on returns from methods and the binding of the context you where in
before the method returned. Using continuations Binding.of_caller will
first let method return, grab the binding of the caller, go back to the
Binding.of_caller call and this time execute the block. If I wouldn't
raise the exception you mentioned above then statements that aren't in
the block would be executed twice. This is why there can be no code
outside of the Binding.of_caller block after the call to it.

Thanks for this explanation. Now I understand. It's a bit like a time
travel ;-)
This ought to work, however I find the 'this = self' line quite ugly --
I still need to find out why just using self doesn't work and if it can
be solved. (It appears like the binding which I get from the trace_func
is nested into the original binding which is why variables can be
accessed, but not the original binding itself which is why self and
method calls don't work.)

I couldn't test this code (haven't applied your def -> symbol patch) so
there might still be a bug in it that I overlooked.

This should do it for you:

this = self
def f(x) x * 2 end
wrapwith(42) >> :f

Regards,

Michael
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top