Thread overview
Easiest way to use Linux system C files / tiny C libraries
Mar 29, 2019
Chris Katko
Mar 29, 2019
Adam D. Ruppe
Mar 29, 2019
H. S. Teoh
Apr 07, 2019
James Blachly
Mar 30, 2019
Kagamin
March 29, 2019
What's the easiest way to use POSIX and Linux-specific C include files?

I know you can write a wrapper but it seems like half the time these files include 20 files which include 20 files which use strange enums, arrays, etc that don't clearly have answers on how to wrap them.

Is there something I'm missing?

For example, right now, the most recent problem I've had related to this is wanting to use the libprocps-dev library. All it does is expose the /proc/ process data in an easy-to-use format.

I "could" write my own in D but then once again, I've not solved my re-occurring problem of "what if I DO need a C library."

Do I need to implement every piece of a C header / library, or can I get away with a tiny subset that I'm actually using?

I don't know. I know this is all vague. But I've run into this problem multiple times and every time, after hours of googling, never gotten anywhere.

Thanks.
March 29, 2019
On Friday, 29 March 2019 at 22:48:47 UTC, Chris Katko wrote:
> What's the easiest way to use POSIX and Linux-specific C include files?

For standard ones, they are pre-done for you under  the `core.sys.posix` D package namespace (or `core.sys.linux` for Linux-specific ones).

For ones not in the standard... it depends. I don't know the libprocps one, but I can say in general:

> Do I need to implement every piece of a C header / library, or can I get away with a tiny subset that I'm actually using?

I say do the bare minimum that works for you. Just the functions, structs, and values you use. And if you are using structs exclusively via pointers, you don't even need their definitions; you can cheat and use void* instead.
March 29, 2019
On Fri, Mar 29, 2019 at 10:48:47PM +0000, Chris Katko via Digitalmars-d-learn wrote:
> What's the easiest way to use POSIX and Linux-specific C include files?

That depends on what you're expecting and what you're willing to do yourself.

If you want a nicely-packaged, black-box way of using C libraries from D, perhaps what you want is:

	https://github.com/jacob-carlborg/dstep


> I know you can write a wrapper but it seems like half the time these files include 20 files which include 20 files which use strange enums, arrays, etc that don't clearly have answers on how to wrap them.

What exactly do you mean by "strange enums, arrays, etc"?  Giving a specific example would help us identify what it is you're having trouble with, and how to help you.

Keep in mind that should you encounter #include's of standard C library
headers, or standard OS headers (like the various standard Posix
#include files), these have already been translated to D as core.*,
for example, core.stdc.* for the standard C library, and
core.sys.posix.* for POSIX headers, and core.sys.linux.* for
Linux-specific headers.  All it takes is to import the relevant
module(s), which have exactly the same name as their C counterparts, and
you're ready to go.

Also, a small number of C libraries may already have D bindings available; check code.dlang.org to see if someone has already done the hard work for you.


[...]
> I "could" write my own in D but then once again, I've not solved my re-occurring problem of "what if I DO need a C library."
> 
> Do I need to implement every piece of a C header / library, or can I get away with a tiny subset that I'm actually using?

If dstep doesn't do the job for you, or if for whatever reason you need manual control over exactly how the C header(s) are translated, you can follow my approach of "absolute minimum declarations to get the thing to compile".

Basically, a C library doesn't really care what declarations you use on
your end, as long as (1) the runtime C symbol is invoked with (2) the
right argument types and values.  Remember that C doesn't have mangling
issues unlike C++, so you don't even need to use the same type names
that the C header uses, as long as your declarations are
binary-compatible with the C declarations (though generally I'd stick
with the same / similar names in order to avoid confusion).  So for (1),
all you need is an extern(C) declaration with the C function name and
arguments of the right (or equivalent) type(s).

For (2), usually an extern(C) struct declaration will get you there. Note that not all struct declarations actually need to be translated; many C libraries take arguments via pointers, so if you don't need to care about the struct contents or layout, you can get away with just an empty forward declaration:

	extern(C) {
		struct SomeLibraryStruct;
		int SomeLibraryFunction(SomeLibraryStruct *arg);
	}

For translating macros, it depends on the intent of the macro. Generally, they tend to fall into these categories:

- Poor man's implementation of compile-time constants: use enum, e.g.:

	/* C code */
	#define MAX_OBJ_SIZE	255

	// D equivalent
	enum MAX_OBJ_SIZE = 255;

- Poor man's function name aliasing, e.g.:

	/* C code */
	#define my_func(x,y)	_my_func_impl((x), (y));

	// D equivalent
	alias my_func = _my_func_impl;

- Function call wrappers, e.g.:

	/* C code */
	#define my_func(x,y)	_my_func_impl((y), (x));

	// D equivalent (you'll have to look up the actual types of x
	// and y and substitute them for 'int' below)
	auto my_func(int x, int y) { return _my_func_impl(y, x); }

- Inline functions:

	/* C code */
	#define error_log(msg)	\
		do { fprintf(stderr, "%s\n", (msg)); } while(0);

	// D equivalent: just use a regular function
	void error_log(string msg) { stderr.writefln("%s\n", msg); }

- Syntax hacks: use mixin templates, or better yet, don't bother, just
  write out what you mean in real syntax.


For translating C enums, generally my advice is: don't use an actual D named enum, but only use `enum` in the sense of a manifest constant, because that's what the C semantics are, and sometimes the library API will expect you to use it that way. For example, I often see something like this:

	/* C */
	enum {
		BLAH = 1,
		BLEH = 2,
		... /* ad nauseaum */
	} my_enum_t;

	/* Obviously, myfunc really wants my_enum_t, but the authors
	 * were lazy and C doesn't care */
	void myfunc(int my_enum);

	// D equivalent: don't even bother with the whole my_enum_t
	// nonsense.
	enum BLAH = 1;
	enum BLEH = 2;
	void myfunc(int my_enum);

If you want some measure of type safety, you can of course use D enums (where the compiler will actually tell you if you're not calling myfunc right).  But that depends on the C API not freely mixing different enums together, which I've seen happen on various occasions, and it also makes the D code more verbose because you have to explicitly name my_enum_t.BLAH instead of just BLAH everywhere.  You can use with() to work around the latter.


The above is the approach I've used to translate the X11 headers in one of my projects: I really did not care to translate *everything*, as I only needed a small subset of the entire X11 protocol, so I just started with the single function call that I needed.  Compile that.  Welp, it failed to compile (obviously) because of missing types and what-not.  Go to the C header, find the definitions of said types.  Copy and paste, and translate to D.  Then compile again.  Nope, still no good: said type definitions depend on other definitions.  So go back to the C header, and translate those definitions too. Repeat until it compiles (and produces the right runtime results).

The first function you translate in this way will be rather painful because it's likely to pull in a whole bunch of declarations that need to be translated. But after a few more function calls you'll have translated a good chunk of the core declarations needed by most API calls, so it should be relatively easy to pull in just the one or two missing declarations.  But if you're anticipating to use only a small subset of the full library API, this method may be faster than trying to grapple with translating the entire header file, including obscure / difficult parts that you may not end up actually using.


> I don't know. I know this is all vague. But I've run into this problem multiple times and every time, after hours of googling, never gotten anywhere.
[...]

Somebody should write up a guide on how to translate C headers into D. The basic principles are pretty obvious, and unless you're unlucky and end up dealing with a library with major macro hackery or particularly obscure parts of C, it should be a relatively mechanical process.

Some gotchas that may not be immediately obvious:

- C arrays are NOT compatible with D arrays; in function parameters,
  just use a pointer type instead (because that's what it essentially is
  in C). E.g.:

  	/* C code */
	int func(char *argv[]);

	// D equivalent:
	int func(char** argv);

  But if the type is used for declaring (usually global) variables, it
  should be translated as a static array instead:

	/* C */
  	char *argv[10];

  should be translated as:

  	// D
	char*[10] argv;

  If you find the same C type declaration being used for both, you may
  need to split the definition so that it will do the right thing in D,
  e.g.:

  	/* C */
	typedef char *mytype[10];
	mytype global_var;
	int myfunc(mytype data);

	// D: N.B., declare two different types for this purpose
	typedef mytype_param = char**;
	typedef mytype_var = char*[10];
	mytype_var global_var;
	int myfunc(mytype_param data);

- Function pointers should be translated as function(...). Do not use
  delegate(...) because that only exists in D.  C allows nastiness like:

  	/* C */
	typedef int (*CALLBACK)(int, char *);
	#define SPECIAL_FUNCPTR	((void*)123)

  which D doesn't allow; to replicate such a declaration, you to cast
  to the actual target type:

  	// D
	alias CALLBACK = int function(int, char*);
	enum SPECIAL_FUNCPTR = cast(CALLBACK) 123;

  If the same SPECIAL_FUNCPTR is used for multiple, incompatible
  function pointer types, you may need to declare multiple constants for
  it.

  Alternatively, if it's really an integer value in disguise (I've seen
  this in real-life, e.g. in the OpenGL/ES API), you could instead opt
  for calling the type what it is, rather than what the C declaration
  pretends it to be. E.g.:

	/* C */
  	void whyOhWhy(void* ptr);
	/* Intended usage: */
	whyOhwhy((void*) 123);
	whyOhwhy((void*) 456);

	// D
	static assert(size_t.sizeof == void*.sizeof);
	void whyOhWhy(size_t ptr);	// stop lying to ourselves
	// Actual usage:
	whyOhWhy(123);
	whyOhWhy(456);


There are probably other similar gotchas, but these are the ones off the top of my head.  Feel free to ask if you're having trouble correctly translating something from C.  If all else fails, run the header through a C processor and inspect the output to find out what the *real* definition of something is, if the original C source is so obfuscated you can't easily tell otherwise.

Finally, if you translated a particularly useful, popular, or fiendishly difficult-to-translate C header, do consider sharing it with the rest of us so that we don't have to repeat your ordeal.  Post it on code.dlang.org or something like that.


T

-- 
Bare foot: (n.) A device for locating thumb tacks on the floor.
March 30, 2019
See also http://code.dlang.org/packages/dpp
April 07, 2019
On 3/29/19 7:52 PM, H. S. Teoh wrote:
> On Fri, Mar 29, 2019 at 10:48:47PM +0000, Chris Katko via Digitalmars-d-learn wrote:
...> There are probably other similar gotchas, but these are the ones off the
> top of my head.  Feel free to ask if you're having trouble correctly
> translating something from C.  If all else fails, run the header through
> a C processor and inspect the output to find out what the *real*
> definition of something is, if the original C source is so obfuscated
> you can't easily tell otherwise.
> 

Sorry I'm late to the party but another one that we have come across quite a bit is in function signatures:

C:
const char *stringparam

D:
const(char)* stringparam


Best wishes