ImportC can now automatically run the preprocessor

Adam D Ruppe destructionator at gmail.com
Mon May 16 01:41:17 UTC 2022


On Monday, 16 May 2022 at 01:05:33 UTC, Walter Bright wrote:
> But what is missing is a compelling use case for it?

It is the only way I've seen that would let you ACTUALLY use full 
C APIs.

Take a look at some of these macros a quick grep through my 
system include directory:

pqStubs.h:#define PQcmdTuples (pqStubs->PQcmdTuplesPtr)

curses.h:#define getcurx(win)           (NCURSES_OK_ADDR(win) ? 
(win)->_curx : ERR)

time.h:# define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)


You can convert many of these from C to D with some string 
manipulations. replace("->", ".") and wrap it in a template 
function and you will go a long way. (This is what dstep does, 
and it actually works about 95% of the time.)

But what about this one?

/* Evaluate to actual length of the `sockaddr_un' structure.  */
# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 
0)->sun_path)        \
                       + strlen ((ptr)->sun_path))



The casts get more and more complicated. To convert this starts 
to require a C parser to convert that code back to D on an ast 
level... and the C parser is exactly what ImportC is, and D's 
facility for parsing a string and converting it to a D ast is 
what `mixin` does.


So combining them thus becomes MixinC.



There's one in this directory I saw before but I don't remember 
where it is, that has something like

#define item child.item

And you'd use it in C like:

yourstruct.item


So it is basically an `alias this` style forwarder done as a C 
macro. This is extremely difficult to express in any kind of D 
conversion, and dpp's approach is very likely to mangle code 
since it lacks semantic awareness.

MixinC bypasses this - it lets it continue be expressed in C, but 
it is limited to a single string literal, so it doesn't allow the 
preprocess to destroy unrelated D code. It is the exact same 
logic as `mixin` being hygienic as it is, just using C strings 
instead of D strings.

In some of these cases, you can express the macro in D directly 
as:

auto SUN_LEN(T)(T ptr) {
  return mixinC(`((size_t) (((struct sockaddr_un *) 0)->sun_path)
                       + strlen ((ptr)->sun_path))`);
}



Or if this isn't done, user code can still:

`size_t s = mixinC("SUN_LEN(my_ptr)");`


Which is syntatically slightly verbose, but that's a cost worth 
paying to maintain the syntax hygiene.... and it works with 
*arbitrary macros* (yes, even ones like `#define BEGIN {`, 
assuming you put the BEGIN and END pieces in the same string, to 
form a valid AST node).


The C preprocessor is a string macro system. D's mixin is a 
string to code system. They can work well together!




If your goal is for system headers to Just Work, and it doesn't 
include some of these macros that are part of the defined API, it 
is going to fall short. The point of the MixinC concept is to 
bridge that gap, giving a solution to the preprocessor macro 
problem.

And besides, notice that, if done right, this can also:

* Enable CTFE to generate C code as well as D code. (You likely 
could write a C preprocessor in D, run it in CTFE, and mixin the 
result!)

* Enable deeper introspection into a complete C api, including 
defined macros and version symbols, allowing custom expressions 
of those to be used in D.



There's still some things ImportC with MixinC would need design 
work on - the `import` namespace is still a mess (I'll write a 
blog about this at some point too, it is past bed time right now) 
- but making all this work would really feel like actually 
unlocking the power of tearing down the barriers between C and D.

ImportC as it is now is just "meh, it'd save me 15 minutes every 
6 months".

ImportC as it could be might unlock a whole new world of C/D 
metaprogramming.

Mr. Bright, TEAR DOWN THIS WALL


More information about the Digitalmars-d mailing list