Headers – Working with Multiple Files¶
Pre-waffle: What headers are for¶
Multiple source files can included in one shared libraryAn executable file containing definitions (function/variables/...) but no main method. just by passing
more than one source file to cslug.CSlug
. The namespace for each source
file will be merged (clashes cause a build error). If you want to be able to use
functions from one file in another file then you need a header file.
To demonstrate how to do this we'll be using the following uninspired example:
int do_nothing(int x) {
return x;
}
int uses_do_nothing(int x) {
/* Calls ``do_nothing()`` from ``file1.c``. */
return do_nothing(x);
}
This refuses to compile because there is a reference to do_nothing()
from
file1.c inside file2.c (although some compiler versions let you off with
just a warning):
from cslug import CSlug
slug = CSlug("lib_nothing", "file1.c", "file2.c")
slug.make()
To keep the compiler happy we need to put a function prototype for
do_nothing()
somewhere visible to file2.c. In this simple case it's enough
to just put int do_nothing(int x);
at the top of file2.c but if a third
file also needed do_nothing()
then this would require duplicating the
prototype. The more general solution is to put function prototypes in a header
file which any other file can #include
.
Auto-generating header files¶
Writing header files is boring and generally devolves to copy/paste (bad).
cslug.Header
tries to do it for you. The following writes a header file
containing prototypes for every function from file1.c and file2.c.
from cslug import Header, CSlug
header = Header("my-header.h", "file1.c", "file2.c")
slug = CSlug("lib_nothing", "file1.c", "file2.c", headers=header)
Passing the header to CSlug
means that calling
slug.make()
will implicitly call header.make()
so that you still only have one make()
command
(although you may use header.make()
directly or
header.write()
to experiment with
Header
s without going through a CSlug
).
The resulting header file looks something like:
// Header file generated automatically by cslug.
// Do not modify this file directly as your changes will be overwritten.
#ifndef MY_HEADER_H
#define MY_HEADER_H
// file1.c
int do_nothing(int x);
// file2.c
int uses_do_nothing(int x);
#endif
Now any file which references anything from either file1.c or file2.c need
only #include "my-header.h"
.
Note
You don't need to pass header files (automatically generated or otherwise)
to cslug.CSlug
, although there is no harm in doing so.
Sharing constants between Python and C¶
Header files are also a good place to put constants or enumerations where other
C files can easily see them. With cslug, you may define constants in Python,
using either a class from the enum
package or just a plain dictionary,
then let cslug.Header
propagate it into a header file for you.
import enum
from cslug import Header
class ErrorCode(enum.Enum):
OK = enum.auto()
NOT_OK = enum.auto()
VERY_BAD = enum.auto()
APOCALYPSE = enum.auto()
header = Header("constants.h", defines=ErrorCode) # defines=[ErrorCode] is also ok.
Quickly inspect using:
>>> header.write()
#ifndef CONSTANTS_H
#define CONSTANTS_H
// ErrorCode
#define OK 1
#define NOT_OK 2
#define VERY_BAD 3
#define APOCALYPSE 4
#endif
Now any Python code can access the APOCALYPSE
constant via
ErrorCode.APOCALYPSE
and any C code can #include "constants.h"
and use
APOCALYPSE
directly.
Other relatives of enum.Enum
such as enum.IntEnum
and
enum.IntFlag
as well as dict
s can also be passed to the
defines=
option. Values are cast to strings using str
then
substituted into the header as is (without surrounding quotes). If that isn't
what you want then you may need to pass a dictionary comprehension based on the
enum.Enum.__members__ attribute
instead.
Add #include
s to generated headers¶
If you use types which are defined in other header files such as wchar_t
then you need to #include
those headers to the generated header. For local
headers use:
Header(..., includes='some-header.h')
or for system-wide ones libraries enclose with angle brackets:
Header(..., includes='<stddef.h>')
Pass a list
if you have more than one:
Header(..., includes=['<stddef.h>', '<stdint.h>'])
Add arbitrary code to generated headers¶
This is intentionally not supported. If you want custom code in an automatically
generated header then put your code in a separate file which either
#include
s or is #include
d by the generated one.