Writeln c_ptr(c_char)

Hi,

chpl version 1.32.0

Can writeln print the value of a c_ptr(c_char) string? The output of the very simple test case below is:

char_ptr wrote >Hello?< to str.
str = 0x7fd5a00119d0

This looks like writeln prints the address of str, not it's value. I'm new to Chapel and just wondering if I'm missing a writeln option.

Thanks

use CTypes;

// chpl -o char_ptr char_ptr.chpl

// List ".o" and ".h" files for all C functions that will be called from Chapel.
require "char_ptr.o", "char_ptr.h", "str_prnt.o", "str_prnt.h";

// List all C functions that will be called from Chapel.
extern proc char_ptr(ref str : c_ptr(c_char)) : void;
extern proc str_prnt(ref str : c_ptr(c_char)) : void;

proc main() {

    var str : c_ptr(c_char);

    char_ptr(str);
    str_prnt(str);
    writeln("str = ", str);
    }
//
// gcc -c -o char_ptr.o char_ptr.c
#include "char_ptr.h"

void char_ptr(char** str) {

    *str = (char*)malloc(32);
    strcpy(*str, "Hello?");
}
// char_ptr.h

#ifndef __EXTERN_C_CHAR_PTR_H
#define __EXTERN_C_CHAR_PTR_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void char_ptr(char** str);

#endif
//
// gcc -c -o str_prnt.o str_prnt.c
#include "str_prnt.h"

void str_prnt(char** str) {

    printf("char_ptr wrote >%s< to str.\n", *str);
}
// str_prnt.h

#ifndef __EXTERN_C_STR_PRNT_H
#define __EXTERN_C_STR_PRNT_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void str_prnt(char** str);

#endif
1 Like

Hello and welcome to Chapel's discourse!

The behavior you're seeing is expected. writeln prints the value of the pointer, which is just an address. This is the behavior you'd get if you want to print out a C pointer in C with printf("%p", ...).

If you want to print the value stored in the address that pointer points at, you need to dereference it, again similar to what you'd need to do in C. Here's a smaller code that does that using both the .deref() method and accessing the 0th index of the pointer:

use CTypes;

proc main() {
  var myChar = 65:c_char;
  var str = c_ptrTo(myChar);

  writeln("str = ", str);
  writeln("str = ", str.deref());
  writeln("str = ", str[0]);

  var chplStr = string.createCopyingBuffer(str, length=1);
  writeln("chplStr = ", chplStr);
}

If you run this, you'll see that .deref and [0] versions will print out 65, which is the integral value stored in that address. Based on what you have in the rest of the code you posted, I am guessing you want to create a Chapel string from a C buffer. So, the code above also shows the way to obtain an actual Chapel string from that buffer.

Hope this helps,
Engin

1 Like

Thanks Engin,

I looked for a dereference function, but overlooked .deref(). Didn't think to try str[0].

Best,

Gene

1 Like

@geenweb : Tagging onto Engin's response, I'll mention that there has been at least a little bit of discussion about whether Chapel's default way of writing out a c_ptr(c_char) should be more string-y rather than pointer-y by default. For example, this comment touches on the question. If you had strong opinions on the topic, that would be interesting to know, either here, or you could feel free to comment on the issue itself.

Thanks,
-Brad

Thanks Brad,

Yes, I want to print a string, not the first character of the string. So I have a C function to print the c_ptr(c_char) string since I can't do that in Chapel. This of course leads to ugly code such as:

write("rl_file = ");
str_prnt(rl_file);
write("   ir_file = ");
str_prnt(ir_file);
write("   jc_file = ");
str_prnt(jc_file);
writeln("");

FWIW, I'd be pretty worried about making var x: c_ptr(c_char) = ... ; writeln(x); assume that x is a null terminated C string and output it similar to printf("%s", x). The issue is that, in general, c_ptr(c_char) doesn't necessarily mean it's a null-terminated string; just that it is a pointer to some bytes. It might not be null terminated in any reasonable way, and as a result, treating it as a C string could lead to printing out a lot of useless data or even lead to an invalid memory access that causes a core dump.

That said, if you are using Chapel's formatted I/O, I do think it'd be reasonable for us to output a c_ptr(c_char) assuming it is a null-terminated C string, when using the %s format string. So I think that it'd be reasonable for this program to compile and run and output hello:

use CTypes;

proc main() {
  var x: c_ptrConst(c_char) = "hello".c_str();

  writef("%s\n", x);
}

(At present, that program fails with a runtime error about an argument type mismatch for writef).

1 Like