12

What are some flaws that drive you nuts in C APIs (including standard libraries, third party libraries, and headers inside of a project) ? The goal is to identify API design pitfalls in C, so people writing new C libraries can learn from mistakes of the past.

Explain why the flaw is bad (preferably with an example), and try to suggest an improvement. Although your solution might not be practical in real life (it's too late to fix strncpy), it should give a heads up for future library-writers.

Although the focus of this question is C APIs, problems that affect your ability to use them in other languages are welcome.

Please give one flaw per answer, so democracy can sort the answers.

Joey Adams
  • 5,615

5 Answers5

5

Functions with inconsistent or illogical return values. Two good examples:

1) Some windows functions that return a HANDLE use NULL/0 for an error (CreateThread), some use INVALID_HANDLE_VALUE/-1 for an error (CreateFile).

2) The POSIX 'time' function returns '(time_t)-1' on error, which is really illogical since 'time_t' can be either a signed or unsigned type.

4

Functions or parameters with non-descriptive or affirmatively confusing names. For example:

1) CreateFile, in the Windows API, doesn't actually create a file, it creates a file handle. It can create a file, just like 'open' can, if asked to through a parameter. This parameter has values called 'CREATE_ALWAYS' and 'CREATE_NEW' whose names don't even hint at their semantics. (Does 'CREATE_ALWAYS' mean it fails if the file exists? Or does it create a new file on top of it? Does 'CREATE_NEW' means it creates a new file always and fails if the file already exists? Or does it create a new file on top of it?)

2) pthread_cond_wait in the POSIX pthreads API, which despite its name, is an unconditional wait.

4

Opaque types that are passed through the interface as type deleted handles. The problem is, of course, that the compiler can't check the user code for correct argument types.

This comes in various forms and flavors, including, but not limited to:

  • void* abuse

  • using int as a resource handle (example: the CDI library)

  • stringly typed arguments

The more distinct types (= cannot be used fully interchangeably) are mapped to the same type deleted type, the worse. Of course, the remedy is simply to provide typesafe opaque pointers along the lines of (C example):

typedef struct Foo Foo;
typedef struct Bar Bar;

Foo* createFoo();
Bar* createBar();

int doSomething(Foo* foo);
void somethingElse(Foo* foo, Bar* bar);

void destroyFoo(Foo* foo);
void destroyBar(Bar* bar);
2

Functions with inconsistent and often cumbersome string returning conventions.

For example, getcwd asks for a user-supplied buffer and its size. This means an application either has to set an arbitrary limit on the current directory length, or do something like this (from CCAN):

 /* *This* is why people hate C. */
len = 32;
cwd = talloc_array(ctx, char, len);
while (!getcwd(cwd, len)) {
    if (errno != ERANGE) {
        talloc_free(cwd);
        return NULL;
    }
    cwd = talloc_realloc(ctx, cwd, char, len *= 2);
}

My solution: return a malloced string. It's simple, robust, and no less efficient. Excepting embedded platforms and older systems, malloc is actually quite fast.

Joey Adams
  • 5,615
1

Functions that take/return compound data types by value, or that use callbacks.

Even worse if said type is a union or contains bit-fields.

From the perspective of a C caller, these are actually OK, but I do not write in C or C++ unless required to, so I am usually calling via an FFI. Most FFIs do not support unions or bit-fields, and some ( such as Haskell and MLton) cannot support structs passed by value. For those that can handle by-value structs, at least Common Lisp and LuaJIT are forced onto slow paths -- Lisp's Common Foreign Function Interface must make a slow call via libffi, and LuaJIT refuses to JIT-compile the code path containing the call. Functions that may call back into the hosts also trigger slow paths on LuaJIT, Java, and Haskell, with LuaJIT not being able to compile such a call.

Demi
  • 826