6 ms·
This is a fairly common problem when trying to interface other languages with any C API, not just errno. The FFI-library of your language can usually call strai
by Too 7y ago
This is a fairly common problem when trying to interface other languages with any C API, not just errno. The FFI-library of your language can usually call straight into C-functions but then it turns out half of the C API is implemented in macros which can't be called nor converted automatically, so you have to be re-implement them all in the new language.
Funny because usually the reason for having a C API is to provide universal compatibility to be called from any language. If you are doing this, please avoid macros.
- arnsholt 7y agoI ran into that at work a few years back. I was tasked with integrating OpenSSL into the commercial Smalltalk used for the project (last updated in 1999, for more or less good reasons). A priori, not an unreasonable task as the Smalltalk had a pretty good FFI mechanism. Except of course the neverending cavalcade of functions documented in the OpenSSL docs that were actually preprocessor macros. In the end I wrote a Python script that parsed the C headers and generated FFI entry points for normal functions and Smalltalk code for preprocessor macros that weren't simple literals. The result was a small mountain of code, but it made all of OpenSSL available to our application, which was great.
- wruza 7y agoYou’re still lucky it wasn’t like DirectDraw or something WinAPI in general. I had to reinvent entire preprocessors and investigate implicit compiler defaults to be able to wrap that, and still there was LOWORD, MAKE_XYZ, etc, which were official arbitrary code-in-a-header interfaces. Preprocessor is a scourge of an API.
- pjmlp 7y agoHence why since Vista all major new Windows APIs are COM based (now UWP).
- jstimpfle 7y agoCOM is a (distributed) object model. It's mostly about structured object lifetimes. I don't think COM offers any solution for replacing bad use of preprocessor in APIs where we couldn't simply use plain C functions instead.
- pjmlp 7y ago> COM is a (distributed) object model. Not at all, that is why DCOM exists. COM offers a language agnostic component model for interoperability between development stacks on Windows, and a modern OS ABI.
- jstimpfle 7y agoI meant to say inter-process.
- jstimpfle 7y ago> a modern OS ABI. Last I looked at it, it was created in 1993 (enterprise OOP craze?) and was just regular DLLs containing regular code, wrapped in tons of symbols cruft to make a "dynamic classes protocol" work. If you actually have to deal with it directly from a non-managed language, I'm very doubtful that it's nicer than a well-written C header file. What I did when I saw MFC macros and 40 lines of boilerplate to marshal arguments to call into a single entry point, was to RUN, which is probably a good idea unless you want to put tons of supporting tooling and code generation in place.
- arnsholt 7y agoYeah, that sounds even more... fun. It had most of User32 an friends (as of 1999 anyways) already wrapped from the Smalltalk vendor, thankfully.
- jstimpfle 7y agoThe main problem with C macros is their usage when a regular function would do just fine. That goes for a lot of the cruft in the Windows headers. Having said that, while I object to most ideas that "C is missing", I do think that better constant expressions and expression macros (functions on the syntactic-expressions level) would be a usability improvement in APIs compared to (token stream) preprocessor macros. Even though in the presence of the preprocessor, constants and expression macros would be somewhat redundant. One advantage of expression macros is that you do macro expansion after parsing to an AST. You get a valid AST parse with macro expansion if and only if you get a valid parse without expansion. Expression macros are regular AST elements, and are scoped just like regular functions or variables. They don't have a weird textual scoping scheme with conditional compilation or un-/redefinability. Getting rid of that should make it easier to translate them to other languages. Another advantage is that they allow you to actually know which constructs in an interface are part of the API. An expression macro is always supposed to be used directly by the consumer of the API, or at indirectly through another macro that is. That particular problem of the C preprocessor could in fact be solved if headers cleaned up after themselves, but in practice, preprocessor is just to messy so nobody does that.
- youdontknowtho 7y agoIsn't this what rustlang did? I have to admit that I'm coming around on rust. I still think that their fanboy marketing is obnoxious...but I need to get over my penchant for finding reasons to be angry.
- jstimpfle 7y agoI think they have a variety of different kinds of them. I'm not sure I want to go there. There are already benefits to be had by something that looks basically like function-like macros in C, but where the replacement bodies are required to be valid expressions, and that is scoped like other functions and variables, and only expanded after parsing. I did that for my own (experimental) language and it was very easy to implement and use.
- steveklabnik 7y ago
- jstimpfle 7y agoWhen I worked on my own language that links to libc, it turned out that MSVC has the standard streams (stdin etc) defined as macros. That's really annoying since that means one has to write a C wrapper function to expose these streams. It might have been possible to avoid these macros, but that probably puts some constraints on the allocation done inside libc. On the other hand, there are some things you simply cannot do without macros. I usually use function-like macros to forward to other functions with additional arguments (like __FILE__, or #arg, sizeof(arg)) which is extremely convenient and allows to cut out a lot of error-prone boilerplate. From a different language, you can still call the function that is being forwarded to. Now try doing that with C++ templates or whatever...
- jmgao 7y agoHilariously, the C standard requires this. C11 7.21.1.3: "The macros are [...] stderr, stdin, stdout which are expressions of type ‘‘pointer to FILE’’ that point to the FILE objects associated, respectively, with the standard error, input, and output streams."
- ainar-g 7y agoYou probably meant to point to 7.21.1.3, not 7.21.3. I had to look it up myself, and sure enough, that's true[1]. The C Standard is a magical document indeed. [1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
- jmgao 7y agoOops, yes, thanks!
- varjag 7y agoLooking at you, PJSIP!
- pjmlp 7y agoWhich is also kind of ironic, because C as universal compatibility only works on OSes implemented in C, as it is the OS ABI, which granted is the case being discussed here.
- Someone 7y ago"as it is the OS ABI" The C ABI almost never is the OS ABI. On systems with separate address spaces, the OS ABI needs to cross a kernel boundary. In theory, you could use the same ABI for regular function calls, but the overhead wouldn’t make your compiler popular.
- coldtea 7y ago>The FFI-library of your language can usually call straight into C-functions but then it turns out half of the C API is implemented in macros which can't be called nor converted automatically Why is that? Aren't the macros expanded by the preprocessor, and end up as a specific piece of code under a function name in a library? Or does this mean there's no e.g. X function that uses one or more preprocessor macros inside it, but the very X function is but a macro itself (so it's only "callable" by the public API function name, from C code before expansion)?
- buckminster 7y ago> Aren't the macros expanded by the preprocessor, and end up as a specific piece of code under a function name in a library? Sure, but you don't know the name of that function so you can't write the FFI declaration for it. You could reverse engineer it, but this would be platform and compiler specific, so non-portable and unstable.
- yxhuvud 7y agoAnother thing to avoid is functions defined in the headers as `static inline`.