Arjen Markus Part three of the tutorial How to make a Tcl application
8. Platform issues
Tcl applications are highly portable in general:
But despite of this, there are a few aspects that you do need to consider, when your application or library is to run on several platforms - and believe me: they will. This chapter is meant to introduce them and the means to solve the problems they cause.
8.1 The tcl_platform array
Tcl collects all information about the platform in a global array, called tcl_platform. Here is a typical listing of its contents:
% parray tcl_platform tcl_platform(byteOrder) = littleEndian tcl_platform(machine) = intel tcl_platform(os) = Windows 95 tcl_platform(osVersion) = 4.0 tcl_platform(platform) = windows tcl_platform(user) = arjen tcl_platform(wordSize) = 4
(Note the use of the array printing command parray - very useful in an interactive shell)
The most useful of these for distinguishing between platforms is tcl_platform(platform): it gives you a standard characterisation of the OS. Typical values: windows, unix and macos.
Tip:
Put everything that is platform-specific in a central place - do not scatter checks on the type of platform all over your code, as that makes it difficult to maintain it. Probably the best approach is to use one or two source files with platform-specific procedures whose interface is essentially platform-independent.
Remember that in Tcl it is quite possible to use constructions like:
if { $tcl_platform(platform) == "windows" } { proc getColours {} { ... } } if { $tcl_platform(platform) == "unix" } { proc getColours {} { ... } } if { $tcl_platform(platform) == "macos" } { proc getColours {} { ... } }
This way there is no need to do any run-time checking.
8.2 File and directory names
Tcl offers a wealth of possibilities to manipulate file names and file attributes via the file command and its subcommands. Use them, whenever you need to: the file command gives you a platform-independent way of forming a full path to a file or to examine the last time of modification of a file.
In most cases, the platform-independent representation of file names and directories is quite adequate, so you do not need to worry about the exact directory separator for the current platform, for instance. However, sometimes you can not quite avoid it:
# read line from file: c:\myapp c:\mysecondapp c:\mydocs set firstdir [lindex $line 0] puts $firstdir ==> c:myapp (Note that the backslash is gone!)
file nativename "c:/my documents/myfile.txt" ==> c:\my documents\myfile.txt
file attributes "c:/my documents" -shortname ==> C:/MYDOCU~1
(These issues mainly arise on Windows platforms, for a variety of reasons, but the most important ones is that the backslash has a special, different meaning on Windows that clashes with its meaning in many programming languages, including Tcl.)
8.3 Fonts and colours
While this is not a style guide for the design of user-interfaces, such guides usually have a lot to say about the use of fonts and colours, a few words of advice are needed:
Some wise words about characters outside the ASCII set:
Colours (and other visual options) present a somewhat greater challenge:
Note:
More on this subject can be found at: The Tk revitalisation project [L1 ] There is a lot of work going on to provide Tk applications that look better without you having to tune the myriad of options.
8.4 Interacting with the desktop
8.5 Dealing with numbers
Fortunately, there are very few platform-dependent issues with numbers:
catch { set x 0.0 set y [expr {1.0/$x}] } msg puts $msg ==> divide by zero The global variables errorInfo and errorCode hold more detailed information: % puts $errorInfo divide by zero while executing "expr {1.0/$x}" % puts $errorCode ARITH DIVZERO {divide by zero}
The most important aspects of handling with numbers hold for all platforms:
Use of braces
Consider the script:
set x 1.1 set y [expr $x*2.3]
and the variation:
set x 1.1 set y [expr {$x*2.3}]
You are almost always better off using the second version! The braces have a number of consequences that are not obvious but that are very welcome:
If you time these commands:
puts [time {set x 1.1; set y [expr $x*2.3]} 10000 ===> 135 microseconds per iteration puts [time {set x 1.1; set y [expr {$x*2.3}]} 10000 ===> 14 microseconds per iteration
you will see the difference (the computer I did this on was not very fast by today's standards).
Effects of floating-point arithmetic
If you are not familiar with floating-point numbers and their behaviour, have a look at this script:
for { set i 0 } { $i < 100 } { incr i } { set x [expr {$i*0.01}] set y [expr {$x*100.0-$i}] puts "$x $y" }
you might expect to see a value of zero for all y's. But on my computer, this is the result:
0.0 0.0 0.01 0.0 0.02 0.0 0.03 0.0 0.04 0.0 0.05 0.0 0.06 0.0 0.07 8.881784197e-016 0.08 0.0 0.09 0.0 0.1 0.0 0.11 0.0 0.12 0.0 0.13 0.0 0.14 1.7763568394e-015 0.15 0.0 ... 0.27 0.0 0.28 3.5527136788e-015 0.29 -3.5527136788e-015 0.3 0.0 ... 0.54 0.0 0.55 7.1054273576e-015 0.56 7.1054273576e-015 0.57 7.1054273576e-015 0.58 -7.1054273576e-015 0.59 0.0 ... 0.97 0.0 0.98 0.0 0.99 0.0
(the dots represent left-out entries with y = 0.0)
So, in many cases we do get y = 0.0, but not in all. The reason? Floating-point arithmetic is not the same as the decimal arithmetic we know from school. Just as 1/3 can not be represented exactly as a decimal fraction (it is a repetitive fraction: 0.33333...), 1/5 can not be represented exactly in floating-point arithmetic, which usually has a base 2 instead of 10.
Moreover, computers have a limited number of digits to work with. This is exemplified by:
set x 1.0e30 set y 1.0e-30 set z 1.0e30 puts [expr {$x+$y-$z}] ===> 0.0 puts [expr {$x-$z+$y}] ===> 1e-030
The first result is zero, because the sum of x and y can not be represented accurately enough - y is too small in comparison with x. In the second result, x and z cancel each other, leaving room for y.
More on floating-point arithmetic in general can be found in the document by Daniel Goldberg, ...
The previous part of this tutorial is How to make a Tcl application - part two and the next part of this tutorial is How to make a Tcl application - part four.