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 = allocArray(readBufferLength)
Searching 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.
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 = allocArray(readBufferLength)
var line = fgets(buffer, readBufferLength, file)?.toKString()
print ( line )
}
}
I don't like getting a .kexe file as the result of my compile, so I have
a Makefile like so:
# 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 = allocArray(readBufferLength)
while ( true ) {
var line = fgets(buffer, readBufferLength, file)?.toKString()
if ( line == null )
break
count++
// print ( line )
}
}
println ( "$count lines in file" )
}
Note 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.
Adventures in Computing / tom@mmto.org