Products > Programming

Is a C struct flatten in memory (Python wrap for a C lib)?

<< < (3/3)

Nominal Animal:

--- Quote from: RoGeorge on January 16, 2022, 09:05:47 pm ---TL;DR - by serendipity I'm running FreeBSD, and your example was no problem for FreeBSD, the compile line was the same.  GCC was not found at first try, so I typed "pkg install gcc", then it worked.  :D
--- End quote ---
:-+

You can also replace gcc with clang, or even cc (the default C compiler), and it should work just fine on all systems using ELF dynamic libraries and a gcc/clang-like interface for specifying the library name: Linux, Android, FreeBSD, OpenBSD, most other BSD variants, and even MacOS.  (The -Wl,-soname,libname.so records the library name into the generated binary; as if one passed -soname libname.so to the ld linker.  It is not crucial, only recommended.)

In my Makefiles that generate dynamic libraries, I use the recipe
    libname.so: sources-or-object-files-used
        $(CC) $(CFLAGS) -fPIC -shared $^ -Wl,-soname,$@ $(LDFLAGS) -o $@
and it almost always suffices.  (If I recall correctly, symbol visibility may in some cases require some tricks; -rdynamic adds all symbols to the dynamic symbol table, and can be useful.)  Sometimes the target is libname.so.$(VERSION), though, although I like to set the name and symbolic links (from major and default versions) at install time instead.


The key point here is that in most cases, the original dynamic libraries with C bindings do not need any changes or any additions to be usable from Python, and the built-in ctypes interface is actually pretty easy to use to do all the interfacing from Python.

You should, of course, write a Python module (with minimal interface code, as shown in my example), to Pythonize the interface, preferably hiding any OS-specific quirks.  That way, one can later wrap the module (in if sys.platform == "platform": for each OS –– "aix", "linux", "freebsd", "win32", "cygwin", or "darwin" –– and then do the OS-specific stuff) if OS-specific quirks are needed.

The ctypes approach does mean that the Python code needs to describe the C interfaces it wants to use, because (on ELF-based systems) the dynamic libraries do not describe them, only the symbol name.  GObject introspection files (gir packages) do contain those, and the Python gi module can use an import them.  This is why you only need e.g.
    import gi
    gi.require_version("Gtk", "3.0")
    from gi.repository import Gtk
to use Gtk+3.0 in Python, without having to deal with ctypes directly.  (In Debian derivatives, the library itself is package libgtk-3-0, and the introspection files are in gir1.2-gtk-3.0, usually installed at /usr/lib/hwarch/girepository-1.0/library.typelib.  My system has 130 for them installed.)

Since lxi-tools already uses Gtk, I do believe it would make sense to just add GObject introspection to liblxi.  I do see you've already started on the Python bindings two weeks ago, but maintaining it separately from the C sources is more work than just letting the introspection tools do it automatically.  This does add one more build dependency, on gobject-introspection, but it is only a build-time dependency, not a run-time dependency.  How this is done in real life, is described at https://gi.readthedocs.io/en/latest/.  Essentially, when the dynamic liblxi library is compiled, g-ir-scanner is run to extract the type information from the C sources and header files, which is then compiled into the typelib file, Liblxi-1.13.typelib, using g-ir-compiler.  The typelib file is packaged separately (gir1.2-liblxi-1.13), providing the GObject introspection.  When that is installed, to use liblx1, one would just do
    import gi
    gi.require_version("Liblxi", "1.13")
    from gi.repository import Liblxi
The obvious benefit of this approach is automation.  The downside is that if the bindings needed Python logic (currently conversion to/from ASCII), those would still have to be implemented as a Python module... but changes and additions to the C library interface would be immediately reflected in the GI typelib files, and accessible from Python.

Now, I do not have any Test Equipment that could use LXI, so I cannot really tell if a separate Python interface module or GObject introspection files make more sense.
My own approach would be to test both first on my own machine (which I cannot do, lacking any LXI equipment!), and if the GI works without issues as-is, then contact the project maintainers/contributors and suggest the change in the form of a suggested patch and an example.

I do not normally do GI on libraries that have no connection to GObject (because, uh, no reason), but GTK+ is a GObject-based library, so it does kinda sorta make sense here.
GI is most useful when the library interface is still evolving, as it keeps the foreign language interface –– not just Python, mind you! –– in sync with the C implementation without explicit developer effort, only testing needed to make sure nothing breaks accidentally.  A simple way to think about it is to consider the GI Python module a way to automate what the ctypes module does, but using information gathered from the actual C sources.

Navigation

[0] Message Index

[*] Previous page

There was an error while thanking
Thanking...
Go to full version