Jump to page: 1 2 3
Thread overview
Can we use "ImportC" used yet?
Oct 15, 2021
rempas
Oct 15, 2021
jfondren
Oct 15, 2021
jfondren
Oct 16, 2021
rempas
Oct 16, 2021
jfondren
Oct 16, 2021
rempas
Oct 16, 2021
jfondren
Oct 16, 2021
rempas
Oct 17, 2021
data pulverizer
Oct 17, 2021
jfondren
Oct 17, 2021
data pulverizer
Oct 17, 2021
jfondren
Oct 17, 2021
data pulverizer
Oct 17, 2021
data pulverizer
Oct 17, 2021
data pulverizer
Oct 17, 2021
data pulverizer
Oct 17, 2021
Imperatorn
Oct 21, 2021
data pulverizer
Oct 21, 2021
Imperatorn
Oct 21, 2021
jfondren
Oct 22, 2021
data pulverizer
Oct 22, 2021
Dave P.
Oct 23, 2021
data pulverizer
Oct 23, 2021
data pulverizer
Oct 24, 2021
data pulverizer
Oct 24, 2021
data pulverizer
October 15, 2021

Cause I can't find an option in the latest DMD release and because the ImportC page seems to be incomplete (even saying it's under construct), I'm wondering if ImportC exists even as prototype but it's hidden or if it's not available at all. Anyone knows?

October 15, 2021

On Friday, 15 October 2021 at 18:39:10 UTC, rempas wrote:

>

Cause I can't find an option in the latest DMD release and because the ImportC page seems to be incomplete (even saying it's under construct), I'm wondering if ImportC exists even as prototype but it's hidden or if it's not available at all. Anyone knows?

There's no option, you just use a normal import statement when the module is named .c instead of .d

I say 'just' but typical C uses the C preprocessor and can't be imported as-is.

Here's an example in three files:

// fstat.d
void main() {
    import std.stdio : writeln;
    import sys_stat : stat_t, fstat;

    stat_t buf;
    writeln(fstat(0, &buf));
    writeln(buf);
}
// sys_stat_wrapper.c
#define __restrict restrict
#include <sys/stat.h>
typedef struct stat stat_t;
# Makefile
fstat: fstat.d sys_stat.c
	dmd $<

sys_stat.c: sys_stat_wrapper.c
	gcc -E -P $< > $@

this is importing sys_stat.c , making space on the stack for a stat_t (what's that?), calling fstat on fd 0, and then writing the output, which might look like

0
stat(27, 11, 1, 8592, 1000, 5, 0, 34824, 0, 1024, 0, timespec(1634329152, 581807916), timespec(1634329152, 581807916), timespec(1634272061, 581807916), [0, 0, 0])
9

the d programmer did not have to carefully extern (C) an fstat function, nor define a struct, nor care about issues like an evil platform padding its struct with some bytes and overwriting the stack unless the d programmer accounts for that. This is all nice. But you still had to write those three lines of C, to smuggle struct stat into d and to suppress non-standard __restrict. And you still need to invoke the C preprocessor as a build step.

The next inconvenient thing is: what about when you want a c #define in d? Say, fstat's potential errors. You have to smuggle those as well, and because the C preprocessor fights you, you have to not just stuff those people in a box but also prepare new names for them. (And if platforms vary in errors? More build system work.)

// sys_stat_wrapper.c, continued
#include <errno.h>
enum errors {
    ebadf = EBADF,
    eio = EIO,
    eoverflow = EOVERFLOW,
};
// fstat.d, continued
    import sys_stat : errors, ebadf;
    writeln(ebadf);
    writeln(errors.eoverflow);
}

Of course you can rename when importing as usual, or have a separate .d module that cleans this interface up where the C preprocessor can't interfere.

For function-like #defines, perhaps you'll want to write a C function that uses it.

In conclusion, ImportC exists and you can use it, and complications like smuggling structs are discussed in that page. If you're going to wrap a new C library especially, it can take on 99% the tedium for you. If you're going to burn down an importc-by-hand work that you have already and replace it with ImportC, it might be better to wait for dub and gdc to catch up. As annoying as it might be to have C constants in your code, they do compile with fewer build steps.

October 15, 2021

On Friday, 15 October 2021 at 20:45:35 UTC, jfondren wrote:

>
0
stat(27, 11, 1, 8592, 1000, 5, 0, 34824, 0, 1024, 0, timespec(1634329152, 581807916), timespec(1634329152, 581807916), timespec(1634272061, 581807916), [0, 0, 0])
9

the d programmer did not have to carefully extern (C) an fstat function, nor define a struct, nor care about issues like an evil platform padding its struct with some bytes and overwriting the stack unless the d programmer accounts for that.

Speaking of padding the struct, what's what [0, 0, 0] at the end of my stat() output? I hadn't noticed it before.

    __syscall_slong_t __glibc_reserved[3];

yeah, core.sys.posix.sys.stat doesn't account for that.

October 16, 2021

On Friday, 15 October 2021 at 20:45:35 UTC, jfondren wrote:

>

There's no option, you just use a normal import statement when the module is named .c instead of .d

I say 'just' but typical C uses the C preprocessor and can't be imported as-is.

[ ... ]

First of all, I see you answering questions (not only mine) very often so thanks ;). Now, I tried to do what you said but I'm getting an error. Now I'm just give you the code so you can also check it (if you want of course) and see what's going on. Let's see:

// Filename: test.d
import test_c;

void main() {
  hello_world();
}

// Filename: test_og.c
#include <stdio.h>
#include <stdlib.h>

void hello_world() {
  puts("Hello world!!!");
}

After that, I'm using: gcc -E -P test_og.c > test_c.c to preprocess just like you shown and then I'm using the final command with DMD: dmd test.d test_c.c and I'm getting the following error message:

/usr/include/stdio.h(246): Error: found `__filename` when expecting `,`
/usr/include/stdio.h(247): Error: found `__modes` when expecting `,`
/usr/include/stdio.h(252): Error: found `__filename` when expecting `,`
/usr/include/stdio.h(253): Error: found `__modes` when expecting `,`
/usr/include/stdio.h(254): Error: found `__stream` when expecting `,`
/usr/include/stdio.h(304): Error: found `__stream` when expecting `,`
/usr/include/stdio.h(304): Error: found `__buf` when expecting `,`
/usr/include/stdio.h(308): Error: found `__stream` when expecting `,`
/usr/include/stdio.h(308): Error: found `__buf` when expecting `,`
/usr/include/stdio.h(314): Error: found `__stream` when expecting `,`
/usr/include/stdio.h(314): Error: found `__buf` when expecting `,`
/usr/include/stdio.h(326): Error: found `__stream` when expecting `,`
/usr/include/stdio.h(327): Error: found `__format` when expecting `,`
/usr/include/stdio.h(332): Error: found `__format` when expecting `,`
/usr/include/stdio.h(334): Error: found `__s` when expecting `,`
/usr/include/stdio.h(335): Error: found `__format` when expecting `,`
/usr/include/stdio.h(341): Error: found `__s` when expecting `,`
/usr/include/stdio.h(341): Error: found `__format` when expecting `,`
/usr/include/stdio.h(347): Error: found `__format` when expecting `,`
/usr/include/stdio.h(349): Error: found `__s` when expecting `,`

Checking the actual source code, it seems like a parsing error. Any ideas?

>

The next inconvenient thing is: what about when you want a c #define in d? Say, fstat's potential errors. You have to smuggle those as well, and because the C preprocessor fights you, you have to not just stuff those people in a box but also prepare new names for them. (And if platforms vary in errors? More build system work.)

// sys_stat_wrapper.c, continued
#include <errno.h>
enum errors {
    ebadf = EBADF,
    eio = EIO,
    eoverflow = EOVERFLOW,
};
// fstat.d, continued
    import sys_stat : errors, ebadf;
    writeln(ebadf);
    writeln(errors.eoverflow);
}

Of course you can rename when importing as usual, or have a separate .d module that cleans this interface up where the C preprocessor can't interfere.

For function-like #defines, perhaps you'll want to write a C function that uses it.

In conclusion, ImportC exists and you can use it, and complications like smuggling structs are discussed in that page. If you're going to wrap a new C library especially, it can take on 99% the tedium for you. If you're going to burn down an importc-by-hand work that you have already and replace it with ImportC, it might be better to wait for dub and gdc to catch up. As annoying as it might be to have C constants in your code, they do compile with fewer build steps.

Agree, having to do it manually will just be a pain in the ass...

October 16, 2021

On Saturday, 16 October 2021 at 06:39:46 UTC, rempas wrote:

>
// Filename: test.d
import test_c;

void main() {
  hello_world();
}

// Filename: test_og.c
#include <stdio.h>
#include <stdlib.h>

void hello_world() {
  puts("Hello world!!!");
}

After that, I'm using: gcc -E -P test_og.c > test_c.c to preprocess just like you shown and then I'm using the final command with DMD: dmd test.d test_c.c and I'm getting the following error message:

/usr/include/stdio.h(246): Error: found `__filename` when expecting `,`

These are __restrict errors again, but then I get a bunch of others as well. This test_og.c works (while obviously breaking some bswap functions):

#define __restrict restrict
#define __asm__ asm
#define __extension__
#define __inline
#define __builtin_bswap16
#define __builtin_bswap32
#define __builtin_bswap64
#include <stdio.h>
#include <stdlib.h>

void hello_world() {
      puts("Hello world!!!");
}

It would be less annoying to compile the original test_og.o with gcc and then link it in.

October 16, 2021

On Saturday, 16 October 2021 at 07:09:16 UTC, jfondren wrote:

>

This test_og.c works (while obviously breaking some bswap functions):

I don't know if I should have known that but what is "bswap"?

>

It would be less annoying to compile the original test_og.o with gcc and then link it in.

If I understand correctly you mean compile the original file with gcc (gcc test_og.c -o test_og.o) and then link it with DMD (dmd test.d test_og.o)? Then what's the point of doing that? Isn't this how we did that all this time and why ImportC was created so we don't have to manually do the bindings? I'm confused...

October 16, 2021

On Saturday, 16 October 2021 at 08:19:41 UTC, rempas wrote:

>

On Saturday, 16 October 2021 at 07:09:16 UTC, jfondren wrote:

>

This test_og.c works (while obviously breaking some bswap functions):

I don't know if I should have known that but what is "bswap"?

I came up with those #defines by looking at test_c.c as d complained about it. It includes these functions in the final result:

static __uint16_t
__bswap_16 (__uint16_t __bsx)
{
  return (__bsx);
}
static __uint32_t
__bswap_32 (__uint32_t __bsx)
{
  return (__bsx);
}
 static __uint64_t
__bswap_64 (__uint64_t __bsx)
{
  return (__bsx);
}

initially those were defined in terms of compiler intrinsics that d doesn't know about, and since they're not needed for your use, I fixed this in the direction of making them no-ops.

They're part of stdlib.h, probably. What they do is endian swaps, like the stuff in std.bitmanip

> >

It would be less annoying to compile the original test_og.o with gcc and then link it in.

If I understand correctly you mean compile the original file with gcc (gcc test_og.c -o test_og.o) and then link it with DMD (dmd test.d test_og.o)? Then what's the point of doing that? Isn't this how we did that all this time and why ImportC was created so we don't have to manually do the bindings? I'm confused...

importC is a new option, but it doesn't make old options go away, and in this specific case an older option would've been less trouble. That's all I'm saying.

October 16, 2021

On Saturday, 16 October 2021 at 11:03:06 UTC, jfondren wrote:

>

I came up with those #defines by looking at test_c.c as d complained about it. It includes these functions in the final result:

static __uint16_t
__bswap_16 (__uint16_t __bsx)
{
  return (__bsx);
}
static __uint32_t
__bswap_32 (__uint32_t __bsx)
{
  return (__bsx);
}
 static __uint64_t
__bswap_64 (__uint64_t __bsx)
{
  return (__bsx);
}

initially those were defined in terms of compiler intrinsics that d doesn't know about, and since they're not needed for your use, I fixed this in the direction of making them no-ops.

They're part of stdlib.h, probably. What they do is endian swaps, like the stuff in std.bitmanip

Cool! It now makes sense now.

>

importC is a new option, but it doesn't make old options go away, and in this specific case an older option would've been less trouble. That's all I'm saying.

Yeah agree, new options are a lot of times not there to make old options obsolete and not used. Tho in this case, I think ImportC will always makes sense against the old way of doing things. Even if you have to do a little bit of work, it will still be nothing compared if you had to make manual binding yourself...

Thanks a lot for the help, have an amazing day!!

October 17, 2021

On Saturday, 16 October 2021 at 08:19:41 UTC, rempas wrote:

>

On Saturday, 16 October 2021 at 07:09:16 UTC, jfondren wrote:
If I understand correctly you mean compile the original file with gcc (gcc test_og.c -o test_og.o) and then link it with DMD (dmd test.d test_og.o)? ...

While we're on this subject, I've been having similar issues now tried compiling @rempas's example file with:

gcc test_og.c -c -o test_og.o
dmd test.d test_og.o

and get the response:

test_og.c(1): Error: identifier or `(` expected
test_og.c(6): Error: identifier or `(` expected

Platform is Ubuntu 20.04 gcc version 9.30 and dmd version v2.098.0. At first I tried compiling a script with fftw3 and thought it was something wrong with my script but when I tried it with your example, the same thing happened.

October 17, 2021

On Sunday, 17 October 2021 at 02:45:03 UTC, data pulverizer wrote:

>

While we're on this subject, I've been having similar issues now tried compiling @rempas's example file with:

gcc test_og.c -c -o test_og.o
dmd test.d test_og.o

and get the response:

test_og.c(1): Error: identifier or `(` expected
test_og.c(6): Error: identifier or `(` expected

You're not doing this with the files in the thread, or you'd get

test.d(1): Error: module `test_c` is in file 'test_c.d' which cannot be read

as test.d is still importing test_c.c, a different file from test_og.o

I get your error if the original test_og.c is used but test.d is modified to import test_og.c:

import test_og;

void main() {
      hello_world();
}

In this case, your compilation to test_og.o doesn't matter; D is still trying to import test_og.c, and since that file has CPP #include directives which importC doesn't support, it's erroring out on those.

To link in a C object rather than use importC, you'd need this test.d:

extern (C) void hello_world();

void main() {
      hello_world();
}

With which:

$ gcc test_og.c -c -o test_og.o
$ dmd test.d test_og.o
$ ./test
Hello world!!!
« First   ‹ Prev
1 2 3