eyepopslikeamosquito has asked for the wisdom of the Perl Monks concerning the following question:

Our code runs on three broadly different platforms: Unix (many variants), NSK, and Windows. Typically, the Unices are similar enough that we use the same code on all variants, but the NSK and Windows code is often completely different.

I noticed some existing code structured like this:

use strict; sub win_fn { print "windows version of function\n" } sub nsk_fn { print "nsk version of function\n" } sub unix_fn { print "unix version of function\n" } sub xplatform_fn { if ($^O eq 'MSWin32') { return win_fn(); } elsif ($^O eq 'nonstop_kernel') { return nsk_fn(); } else { return unix_fn(); } } xplatform_fn();

I'm seeking a better way to structure this type of code. I've taken a look at how File::Spec does it and whipped up two different approaches as shown below.

use strict; my $OS_TYPE = $^O eq 'MSWin32' ? 'win' : ($^O eq 'nonstop_kernel' ? 'nsk' : 'unix'); sub win_fn { print "windows version of function\n" } sub nsk_fn { print "nsk version of function\n" } sub unix_fn { print "unix version of function\n" } my %xplat = ( 'win' => \&win_fn, 'nsk' => \&nsk_fn, 'unix' => \&unix_fn, ); sub xplatform_fn { return $xplat{$OS_TYPE}->() } xplatform_fn();

and:

use strict; my $OS_TYPE = $^O eq 'MSWin32' ? 'win' : ($^O eq 'nonstop_kernel' ? 'nsk' : 'unix'); sub XPlat::win_fn { print "windows version of function\n" } sub XPlat::nsk_fn { print "nsk version of function\n" } sub XPlat::unix_fn { print "unix version of function\n" } sub xplatform_fn { return &{$XPlat::{$OS_TYPE."_fn"}} } xplatform_fn();

What I'm after is examples of nice, clean, general ways to structure cross-platform code.

Replies are listed 'Best First'.
Re: Seeking good ways to structure cross-platform code
by Corion (Patriarch) on Sep 29, 2004 at 06:40 UTC

    I approach this a bit differently, by having n+1 modules for n platforms like this:

    package My::Functions; use strict; my $OS_TYPE = $^O eq 'MSWin32' ? 'win' : ($^O eq 'nonstop_kernel' ? 'nsk' : 'unix'); if ($OS_TYPE eq 'win') { require My::Functions::Win32; } else { require My::Functions::Unix; }; 1; # ---- File My/Functions/Win32.pm: package My::Functions::Win32; package My::Functions; sub foo {}; # ---- File My/Functions/Unix.pm: package My::Functions::Unix; package My::Functions; sub foo {};

    That way, I lose access to the Unix functions under Win32 and vice versa, but the functions live all in the My::Functions namespace and not below it, which I find nice and more convenient. Functions common to all platforms end up in My/Functions.pm.

      I like Corion's approach, in fact I use a similar approach as well. I think this approach is cleaner than mixing codes for different platforms into a single module.

      My similar approach:

      package My::Functions; ... my $OS_TYPE = ...; eval "require My::Functions::$OS_TYPE" or die "Can not load module"; ..


      Having said that, simon's approach from File::Spec is nice too. As Perl programmers always quote: there is more than one way to do it. :-)

      Thanks Corion. I'm now using your approach and am very happy with it. I've changed $OS_TYPE to be 'Win32' and 'Unix' so I could replace:

      if ($OS_TYPE eq 'win') { require My::Functions::Win32; } else { require My::Functions::Unix; };

      with:

      eval "require My::Functions::$OS_TYPE";

      or:

      require "My/Functions/$OS_TYPE.pm";

      Both seem to work ok. Is there a reason to prefer one over the other?

Re: Seeking good ways to structure cross-platform code
by simonm (Vicar) on Sep 29, 2004 at 06:37 UTC
    This is a better match for what File::Spec is doing:
    package XPlat; use strict; my $OS_TYPE = $^O eq 'MSWin32' ? 'win' : ($^O eq 'nonstop_kernel' ? 'nsk' : 'unix'); push @ISA, "XPlat::$OS_TYPE"; sub XPlat::win::fn { print "windows version of function\n" } sub XPlat::nsk::fn { print "nsk version of function\n" } sub XPlat::unix::fn { print "unix version of function\n" } package main; XPlat->fn(); # calls function in whichever package is appropriate

    As a bonus, you can use inheritance to share common methods by moving them up to a superclass, or produce other effects by creating additional subclasses.

Re: Seeking good ways to structure cross-platform code
by dragonchild (Archbishop) on Sep 29, 2004 at 12:35 UTC
    I like File::Spec's way, but not for what File::Spec does. The overloaded method works best when dealing with OO code. F::S should really have used some 'require' magic instead of forcing everone to say File::Spec->foo( ... ). That's really annoying.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

    I shouldn't have to say this, but any code, unless otherwise stated, is untested