Re: Writing portable code
by tobyink (Canon) on Mar 08, 2013 at 09:51 UTC
|
Module::Implementation provides an easy way to load the "best" of multiple implementations of the same function. Its documentation mostly revolves around the situation where you have an XS implementation and a pure Perl implementation, and wish to load the XS if posssible, but fall back to pure Perl. But it works just as well to switch between OS-specific implementations, or Perl-version-specific implementations.
That said, Perl does have conditional compilation. It's just not very pretty...
BEGIN {
$^O eq 'Win32'
? eval q[ sub f { ... } ] # Win32 implementation
: eval q[ sub f { ... } ] # Linux implementation
};
Update: For longer pieces of code, heredocs look quite nice...
BEGIN { eval($^O eq 'Win32' ? <<'WIN32' : <<'LINUX') };
sub f { print "f-w\n" }
sub g { print "g-w\n" }
WIN32
sub f { print "f-l\n" }
sub g { print "g-l\n" }
LINUX
Update II: Also, bear in mind that constants used in conditionals are optimized away by the compiler, so:
use constant BROKEN_FORK_IMPLEMENTATION => ($^O eq 'Win32');
sub f {
if (BROKEN_FORK_IMPLEMENTATION) {
...;
}
else {
...;
}
}
... the conditional should be optimized away at compile time, so you don't get the overhead of a string comparison operation every time the function gets called.
package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
| [reply] [d/l] [select] |
|
| [reply] [d/l] |
Re: Writing portable code
by Athanasius (Archbishop) on Mar 08, 2013 at 09:57 UTC
|
Coming as I do from an OO background, I would approach the problem like this:
#! perl
use strict;
use warnings;
{
package FunctionsFactory;
my $class = ($^O =~ /Win/) ? 'WinFunctions' :
'LinuxFunctions';
sub new { return $class->new(); }
}
{
package WinFunctions;
sub new { return bless {}, shift; }
sub f { print "Windows f()\n"; }
sub g { print "Windows g()\n"; }
}
{
package LinuxFunctions;
sub new { return bless {}, shift; }
sub f { print "Linux f()\n"; }
sub g { print "Linux g()\n"; }
}
my $funcs = FunctionsFactory->new();
$funcs->f();
$funcs->g();
Output (on my system!):
19:49 >perl 564_SoPW.pl
Windows f()
Windows g()
19:53 >
This scheme allows you to keep all the code in one file, and gives a clean/transparent interface to the functions concerned.
Hope that helps,
| [reply] [d/l] [select] |
|
The disadvantage here is that the WinFunctions package is still parsed and compiled on Linux machines, and the symbol table still exists using up memory, even if it never gets used.
For a handful of small functions this is unlikely to cause many performance problems.
What you need to look out for though are cases where, say, WinFunctions not only will not run on Linux, but won't even compile on Linux (e.g. because it uses some Win32::* module). Careful use of run-time require should generally solve this.
package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
| [reply] [d/l] [select] |
|
Even thinking about having a few unused entries in the symbol table is a ridiculous micro-optimisation.
If you want to keep the code clean and in one file, then I suggest something like this ...
use Devel::CheckOS;
setup_for_platform();
... application code goes here ...
# decide what platform-specific code to use
sub setup_for_platform {
if(os_is('MicrosoftWindows')) {
*do_stuff = \&do_stuff_for_windows;
} elsif(os_is('Unix')) {
*do_stuff = \&do_stuff_for_unix;
} else {
die("Don't know about $^O\n");
}
}
# platform-specific function implementations
sub do_stuff_for_unix { ... }
sub do_stuff_for_windows { ... }
| [reply] [d/l] |
Re: Writing portable code
by BrowserUk (Patriarch) on Mar 08, 2013 at 10:46 UTC
|
The last approach seems to me the most elegant, but ... I would need 3 files).
You might look into AutoSplit and AutoLoader.
Not used much these days, but they seem ideally placed to address your "one file -- only compile the bits I need" requirement.
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
Re: Writing portable code
by syphilis (Archbishop) on Mar 08, 2013 at 10:23 UTC
|
Or (perhaps):
4. I could use Inline::C or XS:
void f(void) {
#ifdef _WIN32
/* code */
#else
/* other code */
#endif
}
This avoids any runtime or compile-time determination of the OS. (It was already determined when the script/module was built.)
And only the one file !
Cheers, Rob | [reply] [d/l] |
Re: Writing portable code
by daxim (Curate) on Mar 08, 2013 at 09:43 UTC
|
What do you hope to gain by sticking to having your code in just one file? | [reply] |
|
| [reply] [d/l] |
|
| [reply] [d/l] |
|
Since the variations between Windows and Linux are logically part of the module, where they are used, it feels artificial to move them to a different module. That's why I would prefer to keep them together, if (only if!) I find an equally well solution which allows me to do so.
--
Ronald Fischer <ynnor@mm.st>
| [reply] [d/l] |
Re: Writing portable code
by Anonymous Monk on Mar 09, 2013 at 01:16 UTC
|
Perl has neat ways to manage namespaces, so you can avoid singletons, esp for procedural code
If you're going for conditional compilation optimize the code use Devel::CheckOS(); use constant WIN32 => !! Devel::CheckOS::os_is('MicrosoftWindows');
This probably doesn't apply to your codebase, but a common mental block is developing your app in a single file when it naturally lends itself to multiple files
Write/develop/test the code normally in seperate files -- don't complicate development by limited thinking about distribution :)
You can combine it into a single file for distribution later, using fatpack/pp/ http://www.cavapackager.com/ or whatever
PAR/pp can pack your script/modules only without including core modules, but you probably think that's too heavy (requires installing/packing PAR)
fatpack is lighter, and you can fatpack YourApp::Linux/YourApp::Windows, and offer linuxapp.pl/windowsapp.pl downloads
or you can use 'cat' with autosplit/autoload like BrowserUk suggests
| [reply] |