Find file
Fetching contributors…
Cannot retrieve contributors at this time
147 lines (104 sloc) 4.07 KB

Version blocks

Syntax

Version blocks use the following syntax:

version (<version expression>) {
    <body>
}

Where can be any of:

<version name>
!<version expression>
<version expression> && <version expression>
<version expression> || <version expression>

Semantics

Version blocks aren't if-else blocks - they aren't evaluated at runtime. In rock, version blocks aren't evaluated at ooc-compile-time either. They're evaluated at C compile time. Which means the C code generated by rock should be the same on any platform.

Practically, in rock, version blocks are an abstraction for #ifdef / #endif blocks. The syntax makes it harder to forget to close a version block than an #ifdef / #endif block, and a few handy aliases (listed below) for commonly used version names are standard, so that developers don't have to remember the convoluted corresponding C defines.

For other compilers not based on the C language, version block handling may happen at any stage of the compilation (if any), as long as the version expressions are correctly evaluated and have the correct meaning (for example, a 'windows' version block should be ignored on OSX)

Built-in version names

C defines are included here for completeness, but are only relevant for people who want to implement ooc on top of C.

Name corresponding C define
windows WIN32
linux linux
solaris __sun
unix unix
beos BEOS
haiku HAIKU
apple APPLE
gnuc GNUC
i386 i386
x86 X86
x86_64 x86_64
ppc ppc
ppc64 ppc64
64 x86_64
gc OOC_USE_GC

Custom version names

Most of the standard version names above depend on your building environment, and the 'gc' name depends on the compiler setting -gc=[off,static,dynamic].

Custom version names can be used, and turned on/off with the -D and -U compiler flags, for example:

version(debug) {
    "[%d] Saving database %s" println(timestamp(), db name)
}
db save()

The code inside the version(debug) block will be compiled if -Ddebug is used.

Semantics continued

Version blocks can be used in function bodies, to make certain parts of the code OS-specific, or they can be used at the mdule-level to make functions or types OS-specific.

If different types are defined in different version blocks, make sure they expose the same interface. It's not necessary for them to have the exact same class layout, but they should at least have all the methods/fields that are used in every OS.

Examples

version(windows) {
    "Hi, Bill!" println()
}
version(apple) {
    "Hi, Steve!" println()
}
version(linux) {
    "Hi, Linus!" println()
}
version(!windows && !apple && !linux) {
    "Who are you, and what did you do to my OS?"
}

See also io/File and os/Time in the SDK for real-world examples of heavily versioned code.

Pattern for OS-specific classes

In ooc, 'new' isn't a keyword but a static method. As a result, you can define new yourself. This allows an interesting pattern for OS-specific classes in ooc:

// io/File
import io/[FileUnix, FileWin32]

File: class {
    path: String

    new: static func (.path) -> This {
        version(windows) { return FileWin32 new(path) }
        version(unix)    { return FileUnix  new(path) }
        Exception new(This, "Unsupported platform") throw()
    }

    // abstract methods
}

// io/FileUnix
FileUnix: class extends File {
    init: func (=path) {}

    // implement abstract methods for unix
}

// io/FileWin32
FileWin32: class extends File {
    init: func (=path) {}

    // implement abstract methods for Win32
}