More on C strings and interoperability

Hello,

I think I’m being obtuse about this, in which case please forgive me. I still can’t figer out how to send an array of strings to C. This is my latest attempt.

twostring.chpl:

use CTypes;

extern proc myprint(str:c_ptrConst(c_char));
extern proc loopprint(str:c_ptrConst(c_ptrConst(c_char)), c_int);

// Make an array of strings
var ss: [0..1] string = ["this", "that"];

// string literal is automatically converted to a c_ptrConst(c_char)
myprint("hello");

// a string variable must be converted with .c_str()
var s = "goodbye";
myprint(s.c_str());

// This sends each string in ss to myprint(), one by one.
for i in 0..1 {
  myprint(ss[i].c_str());
}

var sc: [0..1] c_ptrConst(c_char);
for i in 0..1 {
  sc[i] = ss[i].c_str();
}

loopprint(sc,2);		/* Won't compile */

twostring.h

#include "twostring.h"
void myprint(const char* str) {
  printf("%s\n", str);
}

void loopprint(const char* str[], int n) {
  for (int i = 0; i < n; ++i) {
    myprint(str[i]);
  }
}

twostring.h

#include<stdio.h>
void myprint(const char* str);
void loopprint(const char* str, int n);

Compile:

chpl twostring.chpl twostring.h twostring.c

Result:

twostring.chpl:25: error: unresolved call 'loopprint([domain(1,int(64),one)] c_ptrConst(int(8)), 2)'
twostring.chpl:4: note: this candidate did not match: loopprint(str: c_ptrConst(c_ptrConst(c_char)), c_int)
twostring.chpl:25: note: because actual argument #1 with type '[domain(1,int(64),one)] c_ptrConst(int(8))'
twostring.chpl:4: note: is passed to formal 'str: c_ptrConst(c_ptrConst(int(8)))'

I think something is wrong with how I define the array “sc” in twostring.chpl or with how I fill it, or both.

I appreciate any pointers (blech, sorry!) towards getting this correct.

Thanks,

Roger

1 Like

I have no experience with string arrays and so just guessing, but is it useful to pass a C pointer explicitly with c_ptrTo()...? (I've also added c_int for the second argument)

loopprint(c_ptrTo(sc), 2:c_int);
1 Like

Yes! That works.

Many thanks,
Roger

1 Like

Thanks @rmason for the question and @tbzy for the answer.

Note that this is not about strings. You can't pass Chapel arrays to extern C functions that expect a pointer in their extern definition. That would require compiler coercing a Chapel array to a C pointer, which is not well-defined in the case of distributed arrays. Though I don't remember any particular discussion about it myself, I think that's a conscious decision -- the user needs to do something explicit to ensure correctness.

That being said, flipping through documentation, I see that you can define your extern proc to take a Chapel array, which can hint the compiler to do that coercion but a bit differently. You might want to check this out: https://chapel-lang.org/docs/technotes/extern.html#array-arguments

1 Like