Is StringLiteral intended for handling c strings?

Suppose I have c-library function in thing.c that returns a string. What datatype should I use to represent the return type for a FFI binding in a Mojo program?
char* returnABCD(void) {
// Returns (pointer to a) null-terminated string
return "abcd";
}
char* returnABCD(void) {
// Returns (pointer to a) null-terminated string
return "abcd";
}
Compile to shared library: clang -shared -o libThing.so thing.c Option 1: UInt8 pointer If I specify that the return type is a char/uint8 pointer, then I need to write a function parseCharArrayToString that builds a string by loading each character until a null terminator is reached. This approach works fine.
from sys import ffi

alias c_func_char_ptr = fn() -> Pointer[UInt8]

fn parseCharArrayToString(ptr: Pointer[UInt8]) -> String:
"""Extracts characters from a char array until null terminator.
"""
var count = 0
var c_str: String = ""
while True:
let ascii_code = int(ptr.load(count))
if ascii_code == 0:
break
c_str = c_str + chr(ascii_code)
count += 1
return c_str

fn main():
let lib_path = "./libThing.so"
let lib_handle = ffi.DLHandle(lib_path)
let func_handle = lib_handle.get_function[c_func_char_ptr]('returnABCD')

let text = parseCharArrayToString(func_handle())
print(text)
from sys import ffi

alias c_func_char_ptr = fn() -> Pointer[UInt8]

fn parseCharArrayToString(ptr: Pointer[UInt8]) -> String:
"""Extracts characters from a char array until null terminator.
"""
var count = 0
var c_str: String = ""
while True:
let ascii_code = int(ptr.load(count))
if ascii_code == 0:
break
c_str = c_str + chr(ascii_code)
count += 1
return c_str

fn main():
let lib_path = "./libThing.so"
let lib_handle = ffi.DLHandle(lib_path)
let func_handle = lib_handle.get_function[c_func_char_ptr]('returnABCD')

let text = parseCharArrayToString(func_handle())
print(text)
Option 2: StringLiteral (Broken?) I could instead specify the return type to be a StringLiteral.
from sys import ffi

alias c_func_string_literal = fn() -> StringLiteral

fn main():
let lib_path = "./libThing.so"
let lib_handle = ffi.DLHandle(lib_path)
let func_handle = lib_handle.get_function[c_func_string_literal]('returnABCD')

let text = func_handle()
print(text)
from sys import ffi

alias c_func_string_literal = fn() -> StringLiteral

fn main():
let lib_path = "./libThing.so"
let lib_handle = ffi.DLHandle(lib_path)
let func_handle = lib_handle.get_function[c_func_string_literal]('returnABCD')

let text = func_handle()
print(text)
The value printed by the above program is correct. However, the length of the StringLiteral is wrong.
> print(len(text))
A very big number (e.g. 4930616832) (number changes between runs)
> print(len(text))
A very big number (e.g. 4930616832) (number changes between runs)
Is something wrong with the StringLiteral implementation, or am I misunderstanding how it can be used?
1 Reply
Alex Kirchhoff
Alex Kirchhoff12mo ago
StringLiteral is the type of Mojo string literals, and does not have a stable ABI representation. It should not be used for FFI. Pointer[Int8] is probably the right type for FFI. But after you've got the pointer, there are easier ways to interact with it that don't require copying. If you have a Pointer[Int8] named p, you can easily convert it to a StringRef with StringRef(p). StringRef is easy to interact with and works with many APIs. If you do want to copy it into a String, that becomes trivial as well: String(StringRef(p)).
Want results from more Discord servers?
Add your server