June 25, 2023
The game for reading from a file with Kotlin native is to use what they call "cinterop", which gives you things like fopen() and fgets() -- and presumably fclose() from the posix C library.
By following an online example, I try to use allocArray() to get a buffer to pass to fgets to hold the line I want to read. I get:
kotlinc dict.kt -o dict dict.kt:11:18: error: unresolved reference: allocArray val buffer = allocArraySearching on this error, I learn that Alloc() (and its brother AllocArray() presumably) can only be called from within a Memscoped() block. This is new to me, but if I am guessing right, this is a good thing and is a scheme for ensuring that memory gets freed.(readBufferLength)
The error is not very helpful (in fact confusing, if not misleading). Somehow they have things rigged up so that Alloc() is only provided if you are inside a Memscoped block, but once you know, then you know.
Here it is, my first kotlin native program. I allocate a pretty big line buffer -- but no harm done.
import platform.posix.* import kotlinx.cinterop.* val path = "dictionary.txt" fun main() { val readBufferLength = 64 * 1024 println ( "File: $path" ) val file = fopen ( path, "r" ) ?: throw IllegalArgumentException("Cannot open input file $path") memScoped { val buffer = allocArrayI don't like getting a .kexe file as the result of my compile, so I have a Makefile like so:(readBufferLength) var line = fgets(buffer, readBufferLength, file)?.toKString() print ( line ) } }
# Build a simple Kotlin native program dict: dict.kt kotlinc dict.kt -o dict mv dict.kexe dictI "enhance" my program to loop through the entire file, which provides some lessons. I'll note that in Kotlin, you don't return from main() with a value. main() returns the "Unit" which is sort of the Kotlin way of setting up a void function. Also Kotlin doesn't have the C style "for" loop, so you can't use the "for (;;)" idiom to get an infinite loop.
import platform.posix.* import kotlinx.cinterop.* val path = "dictionary.txt" fun main() { val readBufferLength = 64 * 1024 var count = 0 println ( "File: $path" ) val file = fopen ( path, "r" ) if ( file == null ) { println ("Cannot open input file $path") return } memScoped { val buffer = allocArrayNote the nice way you can interpolate values into strings. Also note that you need not write "void fun main ( void )". This is yet another example of kotlin striving to be succinct. Notice also that I have relied upon type inference for every variable. Kotlin is strictly typed, but it can figure out what types I want from the context.(readBufferLength) while ( true ) { var line = fgets(buffer, readBufferLength, file)?.toKString() if ( line == null ) break count++ // print ( line ) } } println ( "$count lines in file" ) }
Adventures in Computing / tom@mmto.org