Discussion:
Providing code as headers
(too old to reply)
Malcolm McLean
2017-06-02 14:59:33 UTC
Permalink
Traditionally a C header file is a relatively short interface specification giving structures
and prototypes. However increasingly I'm seeing substantial functions implemented as
a header file, with most of the functions marked as "static". Then there's usually a define
the includer can provide to create a single function in global space.

Has anyone else any views on this? Is it the way to distribute C source?
Pascal J. Bourguignon
2017-06-02 15:41:15 UTC
Permalink
Post by Malcolm McLean
Traditionally a C header file is a relatively short interface specification giving structures
and prototypes. However increasingly I'm seeing substantial functions implemented as
a header file, with most of the functions marked as "static". Then there's usually a define
the includer can provide to create a single function in global space.
Has anyone else any views on this? Is it the way to distribute C source?
Nope, it's very bad.


However, it may occur for two reasons:

1- if the functions are declared inline, having the source code
available will let the compiler optimize the inlined body of the
function, which wouldn't be possible if the body of the function was
compiled away in a separate source file.

2- the body could actually defined in macros, with source level
substitution (in particular, of the types), and the only alternative
being to use void* and type casting at run-time, which in C would be
more risky. (eg. cf. sys/queue.h).


I've never used the new __generic feature in C11, but I've got the
impression that it wouldn't help in this respect, since AFAIK, you have
to provide one different implementation for each generic variant. It's
only a help from the API/user point of view.



I prefer to use void* and type cast, or using a tagged union:

typedef struct object {
int type_tag;
union {
… string;
… number;
… foo;
… bar;
} value;
} object;

but this can be used only if you have access to the whole sources.
--
__Pascal J. Bourguignon
http://www.informatimago.com
s***@casperkitty.com
2017-06-02 20:03:26 UTC
Permalink
Post by Pascal J. Bourguignon
typedef struct object {
int type_tag;
union {
… string;
… number;
… foo;
… bar;
} value;
} object;
but this can be used only if you have access to the whole sources.
Such an approach would also only be usable if either:

1. One doesn't mind allocating for each object sufficient storage to
accommodate the largest object. If most objects are 8 bytes but a
few are 8000, that could be horribly inefficient.

2. One is using a compiler which can guarantee the behavior if code
allocates a region which is smaller than a structure, converts the
address of that region into a pointer to the structure type, and
then uses that pointer to access only those parts of the structure
which would fall within the allocated region.

An ability to avoid wasting storage would for many purposes be more useful
than any optimizations that would be impeded by supporting #2 above, but
I wouldn't trust the authors of gcc or clang to recognize that.
Jorgen Grahn
2017-06-02 18:34:32 UTC
Permalink
Post by Malcolm McLean
Traditionally a C header file is a relatively short interface
specification giving structures and prototypes. However increasingly
I'm seeing substantial functions implemented as a header file, with
most of the functions marked as "static". Then there's usually a
define the includer can provide to create a single function in
global space.
Has anyone else any views on this? Is it the way to distribute C source?
I have never seen this, not in reusable libraries, which is what I
think you're talking about. Do you have an example?

/Jorgen
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
David Kleinecke
2017-06-02 19:33:30 UTC
Permalink
Post by Jorgen Grahn
Post by Malcolm McLean
Traditionally a C header file is a relatively short interface
specification giving structures and prototypes. However increasingly
I'm seeing substantial functions implemented as a header file, with
most of the functions marked as "static". Then there's usually a
define the includer can provide to create a single function in
global space.
Has anyone else any views on this? Is it the way to distribute C source?
I have never seen this, not in reusable libraries, which is what I
think you're talking about. Do you have an example?
I saw an example recently in the gnu standard library source
code (glibc-2.25) but sadly I didn't observe exactly where. I
poked around a little but didn't find it again.
Ian Collins
2017-06-02 21:22:14 UTC
Permalink
Post by Malcolm McLean
Traditionally a C header file is a relatively short interface
specification giving structures and prototypes. However increasingly
I'm seeing substantial functions implemented as a header file, with
most of the functions marked as "static". Then there's usually a
define the includer can provide to create a single function in global
space.
Has anyone else any views on this? Is it the way to distribute C source?
While it is more common in C++ (where header only libraries aren't
uncommon) than C it does have a place in C. If you look into kernel
source, you will often find sort, performance critical routines in
headers. One example is the Linux kernel spin lock API.
--
Ian
David Brown
2017-06-04 11:54:59 UTC
Permalink
Post by Ian Collins
Post by Malcolm McLean
Traditionally a C header file is a relatively short interface
specification giving structures and prototypes. However increasingly
I'm seeing substantial functions implemented as a header file, with
most of the functions marked as "static". Then there's usually a
define the includer can provide to create a single function in global
space.
Has anyone else any views on this? Is it the way to distribute C source?
While it is more common in C++ (where header only libraries aren't
uncommon) than C it does have a place in C. If you look into kernel
source, you will often find sort, performance critical routines in
headers. One example is the Linux kernel spin lock API.
Presumably this is for inline functions? With C++, you might want
non-inline functions in a header (in particular, you'd want them for
templates), but not in C.
Ian Collins
2017-06-04 20:18:32 UTC
Permalink
Post by David Brown
Post by Ian Collins
Post by Malcolm McLean
Traditionally a C header file is a relatively short interface
specification giving structures and prototypes. However increasingly
I'm seeing substantial functions implemented as a header file, with
most of the functions marked as "static". Then there's usually a
define the includer can provide to create a single function in global
space.
Has anyone else any views on this? Is it the way to distribute C source?
While it is more common in C++ (where header only libraries aren't
uncommon) than C it does have a place in C. If you look into kernel
source, you will often find sort, performance critical routines in
headers. One example is the Linux kernel spin lock API.
Presumably this is for inline functions? With C++, you might want
non-inline functions in a header (in particular, you'd want them for
templates), but not in C.
Yes, most of the Linux kernel spin lock API functions are static inline
in the header.
--
Ian
Jorgen Grahn
2017-06-05 15:43:53 UTC
Permalink
Post by Ian Collins
Post by David Brown
Post by Ian Collins
Post by Malcolm McLean
Traditionally a C header file is a relatively short interface
specification giving structures and prototypes. However increasingly
I'm seeing substantial functions implemented as a header file, with
most of the functions marked as "static". Then there's usually a
define the includer can provide to create a single function in global
space.
Has anyone else any views on this? Is it the way to distribute C source?
While it is more common in C++ (where header only libraries aren't
uncommon) than C it does have a place in C. If you look into kernel
source, you will often find sort, performance critical routines in
headers. One example is the Linux kernel spin lock API.
Presumably this is for inline functions? With C++, you might want
non-inline functions in a header (in particular, you'd want them for
templates), but not in C.
Yes, most of the Linux kernel spin lock API functions are static inline
in the header.
Not only those, but header files related to networking too, and
probably other places I haven't looked at.

I've not read them much recently, but I think it's often for:
- inlining the fastpath of some function
- convenience functions for readability
- locking wrappers around a function which needs locking

There are good examples in e.g. include/net/ip.h.

It's rarely a matter of substantial functions, and I find the style
useful. (Still talking about the Linux kernel; haven't seen what
Malcolm describes.)

/Jorgen
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
Malcolm McLean
2017-06-05 16:14:36 UTC
Permalink
Post by Jorgen Grahn
Post by Ian Collins
Post by David Brown
Post by Ian Collins
Post by Malcolm McLean
Traditionally a C header file is a relatively short interface
specification giving structures and prototypes. However increasingly
I'm seeing substantial functions implemented as a header file, with
most of the functions marked as "static". Then there's usually a
define the includer can provide to create a single function in global
space.
Has anyone else any views on this? Is it the way to distribute C source?
While it is more common in C++ (where header only libraries aren't
uncommon) than C it does have a place in C. If you look into kernel
source, you will often find sort, performance critical routines in
headers. One example is the Linux kernel spin lock API.
Presumably this is for inline functions? With C++, you might want
non-inline functions in a header (in particular, you'd want them for
templates), but not in C.
Yes, most of the Linux kernel spin lock API functions are static inline
in the header.
Not only those, but header files related to networking too, and
probably other places I haven't looked at.
- inlining the fastpath of some function
- convenience functions for readability
- locking wrappers around a function which needs locking
There are good examples in e.g. include/net/ip.h.
It's rarely a matter of substantial functions, and I find the style
useful. (Still talking about the Linux kernel; haven't seen what
Malcolm describes.)
The post was prompted when I downloaded some code to use Fortune's
algorithm for creating Voronoi diagrams. It turned out to be C and header
only.

Loading...