[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] extending mask module?



Regarding shared (dynamic) libraries I am have only used Windows DLLs. Unix shared libraries are similar.

Michael George wrote:

The part I was and am still a bit confused about is name clashes. If I understand correctly, python extension modules are supposed to only export the initmodule method in order to avoid name clashes, which is why the docs say everything should be static. But if bitmask is linked in to the module, then won't it's functions be exported? If not, then is it possible for me to call them, or do I need to link bitmask into my code as well? And how is bitmask different than SDL in terms of how it's linked, if at all?

Name clashes were a concern in the past when extension modules were statically linked to the Python interpreter, as was necessary with DOS. I assume no modern operating system requires this, so name clashes are no longer an issue. (See below.) If anyone is still statically linking the Pygame extension modules they haven't complained about name conflicts yet. Besides, all the bitmask names are mangled with a prefix.

As to accessing the bitmask functions it depends on what you want. See below.

There is no difference between bitmask and SDL when it comes to name clashes. SDL function names are resolved by the linker at build time, just as with bitmask. SDL functions appear in the mask namespace just as bitmask functions do. See below.

Sorry, I feel like I'm saying a bunch of nonsense, but I'm having a hard time making sense of all this dynamic linking stuff.

Shared (dynamic) libraries are somewhat like programs. Just as with a program, non-static functions and global variables can be local to the library. But where as a program has a single entry point, a main function, a shared library can have many. These are the exported functions and variables of the library. They form the API. Only these can be directly accessed by other programs or shared libraries. For Windows shared libraries (Dynamic Link Libraries or DLLs) the linker has to be told which non-static functions and global variables get exported. Everything else remains local to the DLL. For Unix shared libraries I believe all non-static functions and global variables are exported by default. I think gcc has ways around this though. But for Python extension modules this is not a problem.

There are two ways to use a shared library. The first is to load the library dynamically during program execution. Explicit system calls are made to load the library and get pointers to its exported functions and data. This is how the Python interpreter loads extension modules. No name conflicts between extension modules are possible in this case. The ctypes package, introduced in Python 2.5, wraps the dynamic load mechanism, so any shared library is accessible from within Python.

The second way to use a shared library is to have the program loader do the linking at startup. An import library, a proxy to the shared library, stands in for the library when building a program. The import library looks just like any static module. Exported functions and data look just like non-static functions and global variables to the program. So name clashes are possible with other non-static names in the program. Exported names are resolved dynamically at program load time rather than statically at build time. A shared library is loaded only once, when the first program that uses it is loaded. Other programs share the library's memory image. This is how mask and other Pygame extension modules link to SDL.

I don't suggest making bitmask a shared library. The only shared libraries Python's distutils knows how to build are extension modules.

What I'm doing now is adding a CObject to the mask module for the PyMask_Type, and then compiling my code against bitmask.h (which I'm leaving as-is) and mask.h (which looks like some of the other header files, and exports a C API with a bunch of #defines). So I'm assuming I'll need to use CObjects to get at stuff in mask.c (the Type in particular), but I can link directly to the stuff in bitmask.

If you are just extending the mask module itself then the bitmask functions will be visible to your code. Just add any new C files to the mask entry in Setup.in. They will be compiled and linked to mask and bitmask automatically. Remember to run config.py after any changes to Setup.in. Name the non-static functions with a prefix like bitmap_. Any new Python functions go into mask.c itself and remain static.

If you are adding a new extension module then the simplest solution is to just include bitmask.c in that module as well. If you are concerned about name clashes then copy the bitmask.c file as bitmask2.c and rename the functions: bitmap2_* maybe. Also unused code can be removed. The third option is to do as you suggest and export pointers to mask's bitmask functions using the PyCObject mechanism.

On an unrelated note, I'm looking at font.h as an example for exporting the api, and I was wondering in the import_pygame_font why the code does a memcpy instead of just changing the PyFONT_C_API pointer. Isn't there a danger of the memcpy stomping on something else, since it's copying 3 things into a 1-element array?

PyFONT_C_API is an array of PYGAMEAPI_FONT_NUMSLOT, or 3, void pointers. The {NULL} initializer is valid for C99 and makes all array entries NULL. For C90 I don't know, but suspect it is invalid.


Lenard Lindstrom wrote:
Hi Michael,

bitmask.c is statically linked to mask.c as an object file. On Windows the distutils package ensures only the initmask function is exported as an entry point into the mask.pyd DLL. Unix dynamic libraries may be more liberal as to what is exported, but Python will only directly call the module initialization function, so no problem.

Setup.in controls extension builds. This is like a make file. Each extension module, like mask, has its own line, or dependency entry. A new C module can be added to an extension module by adding its source file to an extension module entry. Be sure to run config.py afterward to build the Setup file used by setup.py.

Lenard

Michael George wrote:

Greetings,

[snip]


I'd be happy to spend some time and submit a patch, but I'm not quite sure how it should work. Should mask.c access the bitmask.c functions through the exported function table? Should mask.c just #include bitmask.c? Any thoughts?

[from another post in this thread]
>
>  Understanding the build system(s) is a bit daunting.


--
Lenard Lindstrom
<len-l@xxxxxxxxx>