views:

326

answers:

6

I need to create an API that will allow my customer's developers to use a proprietary C module that will be released as a library (think .lib or .so -- not source).

I'd like to make the header as developer-friendly as possible (so I won't need to be), following best practices and providing comments with descriptions, examples, caveats, etc.

What else should I consider from business, technical, and plain common-sense perspectives?

Thanks!

+2  A: 

Consider actually writing separate documentation. I think man/info pages provide a good example as to what API docs should be like.

Artelius
Or write the documentation into the header in some literate programming system.
dmckee
Excellent point -- we will supply a document that details the API, but man (or .html) pages would be helpful as well. Thank you!
Adam Liss
+3  A: 

The header file itself should be clean and tidy and probably close to minimal. It should point to where the documentation can be found. It probably shouldn't include complete examples (notwithstanding some of my own headers that do that). It should include basic information about the copyright and licence and author details. It should only contain stuff that end users need - nothing that only the developers need. It should be self-contained; it should include any other headers necessary to make it work (so the user can write '#include "your-header.h"' and the code will compile cleanly, even if that's the first or only header they include).

Added: The header should include some basic version information too (file revision number and modification date, and/or the product release version number and release date). This helps people looking at two releases of the software -- and successful software is released more than once.

Added: Adam asked me to expand on "and nothing that only the developers need". That means, for example, that even though the internal functions may use some structure type, if none of the external interfaces uses that structure type, then the public header should not contain a definition of that structure type. It should be in a private header that is not distributed (or is only distributed with the complete source). It should not be polluting the public header. It is tempting to say "but it is only a little bit of wasted space", and it is accurate, but if everyone wastes a little space and time, then the total waste can become expensive.

The key point about the public header is that it should contain everything that the user of the library (set of functions) needs, and nothing that they do not need.

Jonathan Leffler
Can you clarify: 'nothing the developers need' -- I'm assuming you mean _our_ developers; stuff the _customer_ developers need remains, no? I was hoping someone would mention license/legal items. Thanks!
Adam Liss
@Adam: "nothing that only the developers need" does mean the people providing the header. Specifically, the public header should not include any typedefs or enums (or just structs or unions) that are not required by the explicit public interface functions (and global variables, if you have any).
Jonathan Leffler
+4  A: 

One option is to generate the API documentation from the header using (for instance) Doxygen. Obviously you'd still ship the docs alongside the code.

This gives you two advantages:

1) You don't worry so much about whether something should be "in the documentation" or just "in comments in the header", because changing one is just as easy as changing the other. So everything goes in the documentation.

2) Users are less likely to go reading your header file in the first place, because they can be reasonably confident that everything of interest is in the documentation. But even if they're die-hard "I don't trust docs, I read the code" types, then they still see everything you want them to see.

The downside of auto-generated API docs is that they can be a nightmare to search, so IMO it's worth putting additional effort into writing really good "introductory" documentation. This may or may not be part of the generated API docs, depending what you prefer. For a small API, just a list of all the functions in "logical" rather than alphabetical or source order, described according to what they're for rather than what they do, can make it much easier to get into the API docs. By "logical", I mean list the commonly-used functions first, in the order that a client would call them, with alternatives which "do the same kind of thing" (such and send and sendTo for sockets) grouped together. Then list the less-commonly-used functions, and finally the obscure stuff for advanced users and unusual use cases.

One major difficulty of the approach, which can be a show-stopper, is that depending on your organisation you may have a docs team, and it may not be possible for them to edit the header. Best case scenario then is that they copy-edit what you've done and you make the changes and feed them back. Worst case scenario is the whole idea grinds to a halt because "only the docs team are supposed to write customer-facing documentation, and it must be in standard format, and we can't make Doxygen output that format".

As for "what else should you consider" - you've already said you'll follow best practices, so it's hard to add much :-)

Steve Jessop
To date, we're a very manual shop; we're just starting to look into automation. Doxygen is on my list of "utilities to investigate" -- you've just kicked its priority up. No problem between developers vs. tech writers in this case. Thank you!
Adam Liss
+1  A: 

Consider putting the docs online, in addition to whatever ships, and put the URL in the header. This will allow some maintenance programmer, several years down the road, access to the docs, even if the originals have been lost.

KeithB
This isn't appropriate for this particular case, but definitely good advice for a future project -- thank you!
Adam Liss
+3  A: 

Other people mentioned documentation concerns, so I'll stay away from those :-P. Firstly, make sure you have sane include guards. personally I tend to like:

FILENAME_20081110_H_, basically the filename in all caps, then the full date, this is help ensure that it is unique enough, even if there is another header in the tree which has the same name. (For example, you could imagine two config.h's included from 2 different lib directories having guards which use CONFIG_H_ or something like that and thus having a conflict. It doesn't matter what you choose, so long as it is likely to be unique.

Also, if there is any chance this header will be used in a c++ project, please wrap your headers in blocks like this:

#ifdef __cplusplus
extern "C" {
#endif

/* your stuff */

#ifdef __cplusplus
}
#endif

This will save some headaches with name mangling issues and make it so they don't have to wrap the header externally with these.

Evan Teran
We do guard against multiple inclusion -- good tip. We'll follow your future-proofing suggestion, too. Thanks!
Adam Liss
+3  A: 

Please pretty please make sure you don't (re)define symbols that might be defined anywhere else. I don't mean just the standard names, please prefix all symbols declared/defined in public headers with a specific string and avoid any name that anybody else might have ever though about using.

I say this after seeing too much craziness like this in "professional" publicly available headers:

typedef unsigned short uchar;
Krunch
We won't even mention some of the definitions of TRUE, FALSE, and MAYBE we've all seen. Thanks for the good advice!
Adam Liss