Emulating C preprocessor's #if

Usually, when writing ruby code, I don’t worry about conditional
compilation, and just use a run-time test. But for some critical code, I
prefer to factor the conditional out to compile time. Unfortunately,
this can require either a lot of duplication of source code, or
awkwardly factoring out sub-methods which are conditionally compiled.
Sometimes, the following approach is more convenient:

module M
solaris = (RUBY_PLATFORM =~ /solaris/i)
linux = (RUBY_PLATFORM =~ /linux/i)
windows = (RUBY_PLATFORM =~ /win/i)

module_eval %{
def self.print_platform
print "Platform is… "
#{
if solaris then %{
puts “solaris”
}
elsif linux then %{
puts “linux”
}
elsif windows then %{
puts “windows”
}
else %{
puts “unknown”
}
end
}
end
}
end

M.print_platform

Perhaps I might have misunderstood the main point of your letter, but I
think Ruby is dynamic enough to do what you want without any other
changes in the core language:

module M

def M.print_platform
print “Platform is…” + M.platform_dependent_code() + "\n"
end

case RUBY_PLATFORM

  when /solaris/i then
     def M.platform_dependent_code
        "Solaris"
     end
  when /linux/i then
     def M.platform_dependent_code
        "Solaris"
     end
  when /win/i then
     def M.platform_dependent_code
        "Solaris"
     end
  else
     def M.platform_dependent_code
        "unknown"
     end
 end

end

M.print_platform

···

On Fri, 24 Jan 2003 03:36:04 +0900 Joel VanderWerf vjoel@PATH.Berkeley.EDU wrote:

Usually, when writing ruby code, I don’t worry about conditional
compilation, and just use a run-time test. But for some critical code,
I prefer to factor the conditional out to compile time. Unfortunately,

this can require either a lot of duplication of source code, or
awkwardly factoring out sub-methods which are conditionally compiled.
Sometimes, the following approach is more convenient:

module M
solaris = (RUBY_PLATFORM =~ /solaris/i)
linux = (RUBY_PLATFORM =~ /linux/i)
windows = (RUBY_PLATFORM =~ /win/i)

module_eval %{
def self.print_platform
print "Platform is… "
#{
if solaris then %{
puts “solaris”
}
elsif linux then %{
puts “linux”
}
elsif windows then %{
puts “windows”
}
else %{
puts “unknown”
}
end
}
end
}
end

M.print_platform


Best regards,
Yuri Leikind

The debate rages on: Is PL/I Bachtrian or Dromedary?

Usually, when writing ruby code, I don’t worry about conditional
compilation, and just use a run-time test.

What about using the Factory pattern, and instantiating an object of a platform-specific subclass?

http://c2.com/cgi-bin/wiki?FactoryMethodPattern

This lets you limit conditional code to as few places as possible.

But for some critical code, I
prefer to factor the conditional out to compile time.

The Factory pattern will give you at least the same performance in interpreted Ruby as conditional compilation. Since you have late binding for method calls in Ruby, you are getting the flexibility of conditional compilation “for free.” In fact, depending on how many conditional compilations you avoid, and when they happen, it may give you higher performance. (Perhaps I don’t have a clear picture of what you propose?)

Unfortunately,
this can require either a lot of duplication of source code, or
awkwardly factoring out sub-methods which are conditionally compiled.

Done correctly, it will also avoid both of the above. Like most OO Patterns, this is just a specific case of using Martin Fowler’s “Replace Conditional with Polymorphism” refactoring.

http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html

–Peter Kwangjun Suk

···

On Fri, 24 Jan 2003 03:36:04 +0900 Joel VanderWerf vjoel@PATH.Berkeley.EDU wrote:

sorry, wrong values in M.platform_dependent_code method:

module M

def M.print_platform
print “Platform is…” + M.platform_dependent_code() + "\n"
end

case RUBY_PLATFORM

  when /solaris/i then
     def M.platform_dependent_code
        "Solaris"
     end
  when /linux/i then
     def M.platform_dependent_code
        "Linux"
     end
  when /win/i then
     def M.platform_dependent_code
        "Windows"
     end
  else
     def M.platform_dependent_code
        "unknown"
     end
 end

end

M.print_platform

···

On Fri, 24 Jan 2003 03:51:44 +0900 Yuri Leikind YuriLeikind@scnsoft.com wrote:

module M

def M.print_platform
print “Platform is…” + M.platform_dependent_code() + "\n"
end

case RUBY_PLATFORM

  when /solaris/i then
     def M.platform_dependent_code
        "Solaris"
     end
  when /linux/i then
     def M.platform_dependent_code
        "Solaris"
     end
  when /win/i then
     def M.platform_dependent_code
        "Solaris"
     end
  else
     def M.platform_dependent_code
        "unknown"
     end
 end

end

M.print_platform


Best regards,
Yuri Leikind

The people sensible enough to give good advice are usually sensible
enough to give none.

Yuri Leikind wrote:

Perhaps I might have misunderstood the main point of your letter, but I
think Ruby is dynamic enough to do what you want without any other
changes in the core language:

module M

def M.print_platform
print “Platform is…” + M.platform_dependent_code() + "\n"
end

case RUBY_PLATFORM

  when /solaris/i then
     def M.platform_dependent_code
        "Solaris"
     end
  when /linux/i then
     def M.platform_dependent_code
        "Solaris"
     end
  when /win/i then
     def M.platform_dependent_code
        "Solaris"
     end
  else
     def M.platform_dependent_code
        "unknown"
     end
 end

end

M.print_platform

My example was too simple :slight_smile:

I’m thinking of a case where platform-dependent code is mixed with
independent code and hard to factor out without making a lot of extra
method calls. Also, if you have more variables than just platform, you
get combinatorial explosion if you have to write a separate method for
each possibility. My suggestion is a last resort…

Yuri Leikind wrote:

Perhaps I might have misunderstood the main point of your letter, but I
think Ruby is dynamic enough to do what you want without any other
changes in the core language:

And just to clarify: I’m not suggesting any changes at all. I’m using
ruby’s dynamism and string handling features to emulate a (rather ugly)
feature of C.

I don’t advocate widespread use of this. Factoring is better if you can
do it elegantly.

Peter Kwangjun Suk wrote:

···

On Fri, 24 Jan 2003 03:36:04 +0900 > Joel VanderWerf vjoel@PATH.Berkeley.EDU wrote:

Usually, when writing ruby code, I don’t worry about conditional
compilation, and just use a run-time test.

What about using the Factory pattern, and instantiating an object of a platform-specific subclass?

Thanks, but I was avoiding any extra objects or method calls because the
code is fairly performance critical. (But then maybe I should just be
using C anyway, rather than try to write C in Ruby.)

If you do it right, you can instantiate only once. Also, another thing you should do is to code for maintainability, then profile once it runs correctly. I have used profilers all through my career, and rarely have I ever been not surprised in some way. Also, there’s the 80/20 rule. So when I code up front for performance, I end up writing difficult to maintain code in sections that didn’t really need it.

And no, you shouldn’t try to write C in Ruby. That’s just the flip side of trying to write an OO program in C. Play to your language’s strengths.

If you post your code, I’ll be happy to look at it. It might make a great case study for Refactoring vs performance.

–Peter Kwangjun Suk

···

On Fri, 24 Jan 2003 04:40:38 +0900 Joel VanderWerf vjoel@PATH.Berkeley.EDU wrote:

Peter Kwangjun Suk wrote:

On Fri, 24 Jan 2003 03:36:04 +0900 > > Joel VanderWerf vjoel@PATH.Berkeley.EDU wrote:

Usually, when writing ruby code, I don’t worry about conditional
compilation, and just use a run-time test.

What about using the Factory pattern, and instantiating an object of a platform-specific subclass?

Thanks, but I was avoiding any extra objects or method calls because the
code is fairly performance critical. (But then maybe I should just be
using C anyway, rather than try to write C in Ruby.)

Peter Kwangjun Suk wrote:

If you post your code, I’ll be happy to look at it. It might make a great case study for Refactoring vs performance.

Wow, thanks for the offer, Peter. At the moment I’m more concerned about
getting the program to work correctly on several platforms than I am
about elegance. (There’s some difference between flock on linux and
fcntl locks on solaris that cause a problem on solaris when code is both
multiprocess and multithread.)

I may post some code if I can isolate the problem, but now I’m veering
wildly off topic.

In any case, I will release an alpha version of the library soon, and
would appreciate refactoring suggestions then. But correctness first…