www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Wrapping multiple extern (C) declarations in a template

reply "aldanor" <i.s.smirnov gmail.com> writes:
I'm writing bindings to a rather big C library where the return 
values of almost all functions indicate the possibility of an 
error (exception).

Assuming there's a C header, "foo.h" with functions "f1", "f2", 
etc, I want to have a corresponding D module, "foo.d" which would 
provide the "f1", "f2" that wrap the C functions and throw 
exceptions if an error is encountered (which is implemented in an 
errorCheck template which checks the return value and then gets 
the exception info via C API).

My current naive solution looks like this:

/* foo.h */

int f1(...);
int f2(...);

*** foo.d ***

private extern (C) nothrow {
     pragma(mangle, "f1")
     int c_f(...);
     public alias errorCheck!c_f1 f1;

     pragma(mangle, "f2")
     int c_f2(...);
     public alias errorCheck!c_f2 f2;
}

This way, one can "import foo" and use the same API as in C, but 
with proper exception handling.

Since there is almost a thousand declarations, this leads to a 
lot of manual work and boilerplate. I guess the wrapping part 
itself (aliasing the wrapped functions) could be automated via a 
mixin template, but how would one go around repetitive 
pragma(mangle) (which I thought is needed so that private extern 
declarations have different names and don't clash with the 
wrapped ones )?
Dec 13 2014
parent reply "Gary Willoughby" <dev nomad.so> writes:
On Saturday, 13 December 2014 at 16:34:42 UTC, aldanor wrote:
 I'm writing bindings to a rather big C library where the return 
 values of almost all functions indicate the possibility of an 
 error (exception).

 Assuming there's a C header, "foo.h" with functions "f1", "f2", 
 etc, I want to have a corresponding D module, "foo.d" which 
 would provide the "f1", "f2" that wrap the C functions and 
 throw exceptions if an error is encountered (which is 
 implemented in an errorCheck template which checks the return 
 value and then gets the exception info via C API).

 My current naive solution looks like this:

 /* foo.h */

 int f1(...);
 int f2(...);

 *** foo.d ***

 private extern (C) nothrow {
     pragma(mangle, "f1")
     int c_f(...);
     public alias errorCheck!c_f1 f1;

     pragma(mangle, "f2")
     int c_f2(...);
     public alias errorCheck!c_f2 f2;
 }

 This way, one can "import foo" and use the same API as in C, 
 but with proper exception handling.

 Since there is almost a thousand declarations, this leads to a 
 lot of manual work and boilerplate. I guess the wrapping part 
 itself (aliasing the wrapped functions) could be automated via 
 a mixin template, but how would one go around repetitive 
 pragma(mangle) (which I thought is needed so that private 
 extern declarations have different names and don't clash with 
 the wrapped ones )?
Personally i wouldn't go this route. I would create foo.d as a C to D translation only so it can be imported and used like in C. Then i would create another module which imports this to create your new OOP API adding features and excepions, etc. This allows the best of both worlds, keep the C api intact for use in D and create a new clean OOP API for whatever your needs are.
Dec 13 2014
parent reply "aldanor" <i.s.smirnov gmail.com> writes:
 Personally i wouldn't go this route. I would create foo.d as a 
 C to D translation only so it can be imported and used like in 
 C. Then i would create another module which imports this to 
 create your new OOP API adding features and excepions, etc.

 This allows the best of both worlds, keep the C api intact for 
 use in D and create a new clean OOP API for whatever your needs 
 are.
I've actually done it this way initially, as well, however there are certain problems. Let's say there's a "foo.d" that contains raw bindings to "foo.h" (enums, structs, extern variables, function declarations, a whole load of stuff) -- everything but the functions is already fine but all functions need to be wrapped. If I now want to have exactly the same module but with all function declarations wrapped as described above, I have to public import it in another module and then do some template magic. However, it wouldn't be possible to retain the same function names since they've been imported to the namespace (it's then also not possible to extern them as private initially since then you won't be able to import/wrap them in a different module). Hence the idea of mixing the wrapping template in the initial .d header. I guess another way would be to split each header into functions / non-functions part, and private import + wrap the first part and public import the second part into a D-ready module. It just sounds like a whole lot of boilerplate so I was hoping there would be some magic automated way of doing something similar (maybe creating a custom "import" template that "imports" non-functions via aliasing and wraps functions)...
Dec 13 2014
next sibling parent "Gary Willoughby" <dev nomad.so> writes:
On Saturday, 13 December 2014 at 19:03:42 UTC, aldanor wrote:
 Let's say there's a "foo.d" that contains raw bindings to 
 "foo.h" (enums, structs, extern variables, function 
 declarations, a whole load of stuff) -- everything but the 
 functions is already fine but all functions need to be wrapped.

 If I now want to have exactly the same module but with all 
 function declarations wrapped as described above, I have to 
 public import it in another module and then do some template 
 magic. However, it wouldn't be possible to retain the same 
 function names since they've been imported to the namespace 
 (it's then also not possible to extern them as private 
 initially since then you won't be able to import/wrap them in a 
 different module). Hence the idea of mixing the wrapping 
 template in the initial .d header.

 I guess another way would be to split each header into 
 functions / non-functions part, and private import + wrap the 
 first part and public import the second part into a D-ready 
 module. It just sounds like a whole lot of boilerplate so I was 
 hoping there would be some magic automated way of doing 
 something similar (maybe creating a custom "import" template 
 that "imports" non-functions via aliasing and wraps 
 functions)...
I don't think there is an easy way to do this. foo.d contains all the converted C code. Then bar.d *privately* imports foo.d and adds exceptions. I would use bar.d to improve the interface to the library not mearly wrapping functions (even though that's essentially what is happening). foo.d is the C interface. bar.d is your new OOP API complete with exceptions.
Dec 13 2014
prev sibling next sibling parent Mike Parker <aldacron gmail.com> writes:
On 12/14/2014 4:03 AM, aldanor wrote:

  However, it wouldn't be
 possible to retain the same function names since they've been imported
 to the namespace (it's then also not possible to extern them as private
 initially since then you won't be able to import/wrap them in a
 different module). Hence the idea of mixing the wrapping template in the
 initial .d header.
Assuming the C library can be compiled as a shared library, you can load it dynamically. This will allow you to call the function names whatever you want. Declare them all as function pointers, import them privately in your wrapper module and give your wrapper functions names matching the C API.
Dec 13 2014
prev sibling parent "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Saturday, 13 December 2014 at 19:03:42 UTC, aldanor wrote:
 If I now want to have exactly the same module but with all 
 function declarations wrapped as described above, I have to 
 public import it in another module and then do some template 
 magic. However, it wouldn't be possible to retain the same 
 function names since they've been imported to the namespace 
 (it's then also not possible to extern them as private 
 initially since then you won't be able to import/wrap them in a 
 different module). Hence the idea of mixing the wrapping 
 template in the initial .d header.
You can also use `static import`. That way, the imported symbols will only be accessible via their fully-qualified name (e.g. `mypackage.mybindings.foo` instead of `foo`) and won't pollute the module's namespace.
Dec 14 2014