Switching code in or out.

J

John Carter

I tend to do a mix of Test Driven Development and Design By Contract.

I also tend to write code that chugs away for hours on a fast machine even
when tightly optimized and all preconditions and invariants commented out.

Can anyone think of a convenient way of taking a class like....


class MyHairyClass
def invariant
raise "Invariant Failure: Bah" unless
deep_expensive_check_on_the_invariants_of_the_class
end

def my_public_method( foo)
invariant
raise "Precondition failure: Blah" unless
some_expression_involving_foo

# DO STUFF

raise "Postcondtion failure: Bloo" unless
some_expression
invariant
end

end

and being able to selectively switch on and off the precondition and
postcondition and pre and post invariant checks.

ie. I want all checks on maximum when I run the unit tests, I want them
all switched off (compiled out) when I'm running the two hour long deep
thought, and I want them selectively switched on when it crashes half way
through the big run. eg. All the precond checks on.

The simplist would be...

if $debug
def invariant
# Do check
end
else
def invariant
end
end

but that still does two method calls in some of my inner loops and doesn't
handle the {pre,post}conditions nicely.

John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly wrong later."

From this principle, all of life and physics may be deduced.
 
J

Jason Voegele

--nextPart1146384.NG9BLsQP3r
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

Can anyone think of a convenient way of taking a class like....

class MyHairyClass
def invariant
raise "Invariant Failure: Bah" unless
deep_expensive_check_on_the_invariants_of_the_class
end

def my_public_method( foo)
invariant
raise "Precondition failure: Blah" unless
some_expression_involving_foo

# DO STUFF

raise "Postcondtion failure: Bloo" unless
some_expression
invariant
end

end

Have you looked at the Ruby DbC package? It is not very robust solution, b=
ut=20
it is simple and sounds like it might suit your needs:

http://www.rubycentral.com/downloads/dbc.html

=2D-=20
Jason Voegele
The little town that time forgot,
Where all the women are strong,
The men are good-looking,
And the children above-average.
-- Prairie Home Companion

--nextPart1146384.NG9BLsQP3r
Content-Type: application/pgp-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)

iD8DBQBDM1C1hnQPOwpx0DURAq7xAJ40tIE9ldhjgtwTDKkNGfO2wHsd3QCfWu2w
XXQx1Tj68XR90KuzCC0qS2c=
=E6I1
-----END PGP SIGNATURE-----

--nextPart1146384.NG9BLsQP3r--
 
D

Devin Mullins

Would this suffice? Its syntax is limited compared to the 'raise'
statement, but if YAGNI...

module Kernel
def do_raise
def maybe_assert(msg,&blk) raise msg unless blk.call end
end
def dont_raise
def maybe_assert(msg) end
end
dont_raise
end

And replace use of 'raise ... unless ... ' with 'maybe_assert ... do ...
end' in the code below.

I don't know what the performance hit of calling an empty method is,
though....

Devin
 
J

John Carter

Ok, so there seems to be three basic methods of doing this, the most hairy
involves using class_eval to interpose an aspect on function call
and return.

Cute.

Scary.

Anywhoo, the other two are basically to make the invariant method empty or
to only invoke the invariant method if a global or const is true....

require 'benchmark'

DEBUG = false
$foo = false

class A

attr_reader :inv

alias_method :in_very_ant, :eek:bject_id

def invariant
end

def very
in_very_ant
in_very_ant
end

def empty_method
invariant
invariant
end

def if_const
invariant if DEBUG
invariant if DEBUG
end

def if_global
invariant if $foo
invariant if $foo
end

def attr_thing
inv
inv
end

def no_code
end
end

n = 1000000
a = A.new

Benchmark.benchmark(" "*7 + Benchmark::CAPTION, 7, Benchmark::FMTSTR, ">total:", ">avg:") do |x|
tf = x.report("empty_method:") { n.times do ; a.empty_method; end }
tt = x.report("if_const:") { n.times do ; a.if_const ; end }
tg = x.report("if_global:") { n.times do ; a.if_const ; end }
tu = x.report("no_code:") { n.times do ; a.no_code ; end }
tv = x.report("inv:") { n.times do ; a.attr_thing ; end }
tw = x.report("very:") { n.times do ; a.very ; end }
x.report("nothin:") { n.times do ; a ; end }
[tf+tt+tg+tu+tv+tw, (tf+tt+tg+tu+tv+tw)/6]
end

The results are using Ruby 1.8.2...

ruby-1.8.2 binch.rb;ruby binch.rb
user system total real
empty_method: 1.210000 0.000000 1.210000 ( 1.218951)
if_const: 1.010000 0.000000 1.010000 ( 1.016450)
if_global: 1.020000 0.000000 1.020000 ( 1.021371)
no_code: 0.580000 0.000000 0.580000 ( 0.584446)
inv: 0.850000 0.000000 0.850000 ( 0.852560)
very: 0.870000 0.000000 0.870000 ( 0.863922)
nothin: 0.260000 0.010000 0.270000 ( 0.261910)
total: 5.540000 0.000000 5.540000 ( 5.557700)
avg: 0.923333 0.000000 0.923333 ( 0.926283)

And a fairly recent version from CVS....
user system total real
empty_method: 1.160000 0.000000 1.160000 ( 1.159409)
if_const: 0.950000 0.000000 0.950000 ( 0.953207)
if_global: 0.970000 0.000000 0.970000 ( 0.972160)
no_code: 0.570000 0.000000 0.570000 ( 0.569933)
inv: 0.790000 0.000000 0.790000 ( 0.789721)
very: 0.800000 0.000000 0.800000 ( 0.796247)
nothin: 0.260000 0.000000 0.260000 ( 0.266906)
total: 5.240000 0.000000 5.240000 ( 5.240677)
avg: 0.873333 0.000000 0.873333 ( 0.873446)

notes:

1) I was surprised to find the if const and if global so close. Obviously
no constant folding is being done on branches.

2) Of course, no code is faster than no code. So the hairy class_eval
tricks would be faster. Although not tremendously faster than the
attr_reader trick.

3) Ruby 1.9 is noticably faster than 1.8.2

Thanks for all the responses and wild creativity.

4) Ruby-contract, Awesome and ruby-dbc are very very very scary modules.

I'm not sure I can cope with anything quite that clever near my code. It
has all (and more, much more) the horror of the C preprocessor, without
the gcc -E option to see the what it actually did.


John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly wrong later."

From this principle, all of life and physics may be deduced.
 

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,995
Messages
2,570,228
Members
46,818
Latest member
SapanaCarpetStudio

Latest Threads

Top