Tcl C API Design Principles

Difference between version 0 and 1 - Previous - Next
This page attempts to collect some principles used in the Tcl C API.

''Always read the documentation instead of relying purely on the principles here.''


**Naming**

Tcl's public API symbols start with `Tcl_` or `TCL_`. We will ''never'' warn anyone about creating new symbols in that space.

**Argument order**
   1. A ''passed-back'' [`ClientData`] comes first, if needed.
   2. The interpreter comes next, if needed for context or error reporting (''except'' if this makes things confusing).
   3. Out parameters come last.
   4. When an array of values is passed in or out, the length of the array comes in the argument immediately preceding it.
   5. A value to be modified will typically be the first argument after the interpreter, but not always. (Note that changing the reference count of a Tcl_Obj is not considered to be a modification for this.)

**Result types with failures**

If a function can fail, it either returns a [Tcl result code] (TCL_OK, TCL_ERROR, etc.) or a `NULL`able pointer. In the latter case, it ''always documents'' that this is the case. Almost all cases that produce errors will take an interpreter that will be used to hold an error message; if the interpreter is not also needed for context, it will be legal to pass a `NULL` in its place so that no error message is generated (though always at the cost of losing detail of what the failure was).

Basic memory allocation functions can never fail (they panic instead) ''unless'' they contain “`Attempt`” in their name; the attempting allocators _can_ fail, and their use is encouraged when dealing with large amounts of data (such as big image buffers).

Lookup functions often return a [Tcl_Obj]* instead of a Tcl result code. Their failure result is always a `NULL`, which can include “there is no such thing available”. A Tcl_Obj ''never'' encodes a `NULL`. (Logically, `NULL` is an ''absence'' of value, and does not belong to the space of strings; since [everything is a string] in Tcl, `NULL` is consequently not in Tcl.

**Value ownership**

Non-Tcl_Obj arguments or results are never transferred ownership unless the function is explicitly documented to do so. (Typically, ownership transferring functions are internal to Tcl's implementation.)

Tcl_Obj values are generally shared ownership. [Tcl_IsShared] will say more specifically if a particular object is shared. The allocation functions return unshared _zero-ref-count_ objects. Functions that modify an object require that it is unshared. It's usually obvious which functions are doing modification and which are taking ownership, but it is not always; in particular, some functions ''sometimes'' take ownership. 

***Example: Tcl_DictObjPut***

Let's consider a real example: [`Tcl_DictObjPut`].

======c
int Tcl_DictObjPut(interp, dictPtr, keyPtr, valuePtr);
======

This returns a Tcl result code and takes a (`NULL`able) interpreter; it can fail, and it can be told to fail quietly.

The `dictPtr` argument is the value being modified: it must be unshared (refcount 0 or 1; zero means newly-allocated, one means only one owner). Note that with a newly-allocated value, you can be sure from your own code whether things will fail or not; the value has not left the custody of your C code yet.

The `valuePtr` is always going to have its ownership taken by the dictionary unless there is an error. If you know that `dictPtr` is definitely a valid dictionary, the value is always going to be taken control of, and it is safe to pass a zero refcount Tcl_Obj here. If failure is a possibility (e.g., the dictionary has come from somewhere where ''anything'' could have treated it as a non-dictionary, and it has not yet been successfully used as a dictionary by some dict-API function since then) then you must make sure that you increment the reference count of the value before passing it in, and decrement it afterwards.

The `keyPtr` is more complex. It ''might'' have ownership taken or it might not; it depends on whether the key already existed in the dictionary (this is something you can often know in your own code). If you do not know for sure whether the key exists, the key must be passed in with a reference count of at least 1; it is ''only'' safe to use a zero refcount key when you know that the dictionary is a real dictionary ''and'' you know that the key is absent. This typically restricts doing this to initial building of the dictionary.