/README.md

https://github.com/georgek42/inlinec · Markdown · 68 lines · 57 code · 11 blank · 0 comment · 0 complexity · b67d175b7fbbdff80e77316f5ad69baa MD5 · raw file

  1. # Inline C
  2. Effortlessly write inline C functions in Python source code
  3. ```python
  4. # coding: inlinec
  5. from inlinec import inlinec
  6. @inlinec
  7. def Q_rsqrt(number):
  8. float Q_rsqrt( float number )
  9. {
  10. long i;
  11. float x2, y;
  12. const float threehalfs = 1.5F;
  13. x2 = number * 0.5F;
  14. y = number;
  15. i = * ( long * ) &y; // evil floating point bit level hacking
  16. i = 0x5f3759df - ( i >> 1 ); // what the fuck?
  17. y = * ( float * ) &i;
  18. y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
  19. return y;
  20. }
  21. print(Q_rsqrt(1.234))
  22. ```
  23. Inlinec supports gnu-specific c extensions, so you're likely to have reasonable success #includeing glibc headers.
  24. Inspired by [Pyxl](https://github.com/pyxl4/pyxl4)
  25. # How does this work?
  26. Python has a mechanism for creating custom codecs, which given an input token stream, produce an output token stream. Inlinec consumes the entire token stream, runs a fault-tolerant parser on it ([parso](https://github.com/davidhalter/parso)), finds which function nodes are annotated with an `@inlinec` decorator, creates a `ctypes` wrapper for the content of the function, and replaces the function body with a call to the ctypes wrapper. The import for the wrapper is lifted to the top of the file. Once this transformation has been made, the source code is re-tokenized and the Python interpreter only sees the transformed source.
  27. So a function like this:
  28. ```python
  29. @inlinec
  30. def test():
  31. #include<stdio.h>
  32. void test() {
  33. printf("Hello, world");
  34. }
  35. ```
  36. Gets turned into:
  37. ```python
  38. from test_8281231239129310 import lib as test_8281231239129310_lib, ffii as test_8281231239129310_ffi
  39. @inlinec
  40. def test():
  41. return test_8281231239129310_lib.test()
  42. ```
  43. In theory, this allows inline c functions to be called with a one-time compilation overhead and the same performance characteristics as ctypes -- the underlying FFI library.
  44. # Limitations
  45. Note: This is just a proof of concept
  46. * Passing pointers to C functions does not currently work (aside from strings)
  47. * Shells out to `gcc -E` to preprocess the source code
  48. * Compilation is not cached and takes a long time every run
  49. * Compilation pollutes the current directory with .so, .o, .c files
  50. * The source file is parsed multiple times unnecessarily
  51. * Many more
  52. # Installation
  53. Inlinec requires a C compiler to be installed on the system (tested with GCC and Clang), as well as the python development libraries to be installed (python3-dev).
  54. To play around with it in a container you can use the provided Dockerfile, just run docker build, exec into a shell in the container, and you have a working installation of inlinec.
  55. ```bash
  56. > docker build -t inlinec . && docker run -it inlinec bash
  57. ```