www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is there an easy to call c functions with char** parameters?

reply outersky <outersky gmail.com> writes:
Hi all ,
I'm new to c and d.

Now I don't know how to call c function with char** parameters.
And when I call c function with char* parameters, I need to invoke
std.string.toStringz()
to do some transform.
but about char** ?

thanks .

outersky
Nov 05 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
outersky wrote:
 Hi all ,
 I'm new to c and d.
 
 Now I don't know how to call c function with char** parameters.
 And when I call c function with char* parameters, I need to invoke
 std.string.toStringz()
 to do some transform.
 but about char** ?

It depends a little on what the C function is doing with the char** but in general I expect it is assigning something to the pointer. Maybe it's allocating memory eg. [some.c] func(char **p) { *p = strdup("test"); } void main() { char *p; func(&p); printf("%s", p); } or perhaps it's using it as a continuation like a custom thread safe strtok might do... char *strtok(char *start, char *tok, char **next) { //perform tok *next = upto; //assign next to the place we are upto } in either case something is assigned to the pointer (which is why it needs a pointer to it). If you want to convert the resulting pointer into a D array you might do the following: char[] arr; char *p; func(&p); arr = p[0..strlen(p)]; I don't think this works: char[] arr; func(&arr.ptr); arr.length = strlen(arr.ptr); because arr.length would be 0 and setting a new length would trigger a re-allocation. Which brings me to the problem you need to be aware of in the case where func allocates memory. You should not assign the resulting point to arr.ptr either directly or via the assignment using a slice shown above because when the array tries to reallocate it does so on a memory block it didn't allocate in the first place, and this is very likely to fail. Instead a safe alternative is to wrap the C function call, copy the data, and free the memory allocated in C using whatever method the C library provides eg. void dfunc(ref char[] arr) { char *p; func(&p); arr.length = strlen(p); arr[] = p[0..arr.length]; func_free(&p); } Regan p.s. some casts may be required implmenting the above in D 2.0 due to invariant/const etc. p.p.s if the C function is returning data in a character set which is not plain ASCII or UTF-8 then it may result in invalid UTF-8 being stored in the D array.
Nov 05 2007
parent reply outersky <outersky gmail.com> writes:
Thank you, thank you very much.

Sorry for  unclear expression of my question,

I'm now trying to write a simple program as:

main.d   :

    extern(C) void printargv(char* argv[], int count);
    int main(char[][] argv){
        printargv(cast(char* [])argv,argv.length);
        return 0;
    }

func.c  :

void printargv(char* argv[], int count){
    for(int i=0; i<count; i++){
        printf("%d:%s\n", argv[i]);
    }
}


gcc -g -c -std=gnu99 func.c
dmd -g main.d func.o
./main arg1 arg2

and result in
" core dumped "
:(



Regan Heath д:
 outersky wrote:
 Hi all ,
 I'm new to c and d.

 Now I don't know how to call c function with char** parameters.
 And when I call c function with char* parameters, I need to invoke
 std.string.toStringz()
 to do some transform.
 but about char** ?

It depends a little on what the C function is doing with the char** but in general I expect it is assigning something to the pointer. Maybe it's allocating memory eg. [some.c] func(char **p) { *p = strdup("test"); } void main() { char *p; func(&p); printf("%s", p); } or perhaps it's using it as a continuation like a custom thread safe strtok might do... char *strtok(char *start, char *tok, char **next) { //perform tok *next = upto; //assign next to the place we are upto } in either case something is assigned to the pointer (which is why it needs a pointer to it). If you want to convert the resulting pointer into a D array you might do the following: char[] arr; char *p; func(&p); arr = p[0..strlen(p)]; I don't think this works: char[] arr; func(&arr.ptr); arr.length = strlen(arr.ptr); because arr.length would be 0 and setting a new length would trigger a re-allocation. Which brings me to the problem you need to be aware of in the case where func allocates memory. You should not assign the resulting point to arr.ptr either directly or via the assignment using a slice shown above because when the array tries to reallocate it does so on a memory block it didn't allocate in the first place, and this is very likely to fail. Instead a safe alternative is to wrap the C function call, copy the data, and free the memory allocated in C using whatever method the C library provides eg. void dfunc(ref char[] arr) { char *p; func(&p); arr.length = strlen(p); arr[] = p[0..arr.length]; func_free(&p); } Regan p.s. some casts may be required implmenting the above in D 2.0 due to invariant/const etc. p.p.s if the C function is returning data in a character set which is not plain ASCII or UTF-8 then it may result in invalid UTF-8 being stored in the D array.

Nov 05 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
outersky wrote:
 Thank you, thank you very much.
 
 Sorry for  unclear expression of my question,
 
 I'm now trying to write a simple program as:
 
 main.d   :
 
     extern(C) void printargv(char* argv[], int count);
     int main(char[][] argv){
         printargv(cast(char* [])argv,argv.length);
         return 0;
     }
 
 func.c  :
 
 void printargv(char* argv[], int count){
     for(int i=0; i<count; i++){
         printf("%d:%s\n", argv[i]);
     }
 }
 
 
 gcc -g -c -std=gnu99 func.c
 dmd -g main.d func.o
 ./main arg1 arg2
 
 and result in
 " core dumped "
 :(

Ahh.. I see. The first problem is that the C code: printf("%d:%s\n", argv[i]); needs to be: printf("%d:%s\n", i, argv[i]); :) The next problem you have is that char[][] in D is an array of arrays, meaning that in memory you have an array of structures which resemble this: struct array { void *ptr; int length; } So, if you have: char[][] argv; argv.length = 4; then argv itself looks like [ptr:length] and argv.ptr would point to a memory location which looks like: [ptr:length][ptr:length][ptr:length][ptr:length] and each of those ptr's point to the actual char data. When you call: printargv(cast(char* [])argv,argv.length); you pass the ptr of the argv array to the C function which tries to treat it like a char** and fails miserably. Instead what you actually need to do is build a C style array of char* pointers and pass that, try something like... char **buildCargs(char[][] argv) { char **res; //does (char*).sizeof work? res = malloc(argv.length * (char*).sizeof); foreach(i, arg; argv) res[i] = arg.ptr return res; } and call it like: printargv(buildCargs(argv), argv.length); You'll need to find the import required to use malloc i.e. std.c.stdlib? I don't have a D compiler handy to make sure this actually works, or is even syntactically valid but hopefully it's close enough that you can figure it out. Regan
Nov 05 2007
parent outersky <outersky gmail.com> writes:
haha, it works , I'm so happy  :)

thank you!

outersky


Regan Heath д:
 outersky wrote:
 Thank you, thank you very much.

 Sorry for  unclear expression of my question,

 I'm now trying to write a simple program as:

 main.d   :

     extern(C) void printargv(char* argv[], int count);
     int main(char[][] argv){
         printargv(cast(char* [])argv,argv.length);
         return 0;
     }

 func.c  :

 void printargv(char* argv[], int count){
     for(int i=0; i<count; i++){
         printf("%d:%s\n", argv[i]);
     }
 }


 gcc -g -c -std=gnu99 func.c
 dmd -g main.d func.o
 ./main arg1 arg2

 and result in
 " core dumped "
 :(

Ahh.. I see. The first problem is that the C code: printf("%d:%s\n", argv[i]); needs to be: printf("%d:%s\n", i, argv[i]); :) The next problem you have is that char[][] in D is an array of arrays, meaning that in memory you have an array of structures which resemble this: struct array { void *ptr; int length; } So, if you have: char[][] argv; argv.length = 4; then argv itself looks like [ptr:length] and argv.ptr would point to a memory location which looks like: [ptr:length][ptr:length][ptr:length][ptr:length] and each of those ptr's point to the actual char data. When you call: printargv(cast(char* [])argv,argv.length); you pass the ptr of the argv array to the C function which tries to treat it like a char** and fails miserably. Instead what you actually need to do is build a C style array of char* pointers and pass that, try something like... char **buildCargs(char[][] argv) { char **res; //does (char*).sizeof work? res = malloc(argv.length * (char*).sizeof); foreach(i, arg; argv) res[i] = arg.ptr return res; } and call it like: printargv(buildCargs(argv), argv.length); You'll need to find the import required to use malloc i.e. std.c.stdlib? I don't have a D compiler handy to make sure this actually works, or is even syntactically valid but hopefully it's close enough that you can figure it out. Regan

Nov 05 2007