names
Tuesday, 26 April 2011
Here we describe naming conventions in C and C++ used in thorn, and
the use of y to mean thorn. While this is fairly boring, it's useful to
start with rules about symbols used.
y
Tuesday, 26 April 2011
Glyph y sometimes substitutes for glyph þ (thorn)
which once had a sound like th. For example, phrase ye olde
(cf wikipedia) uses y as
a substitute for the original thorn. Accordingly, y is just a convenient
replacement for thorn þ, and is sometimes used as a prefix.
virtual
Wednesday, 04 May 2011
The following pure virtual C++ class illustrates translation into equivalent C. Perhaps
you note absence of a virtual destructor, even though this would make sense. The C
version won't have a virtual destructor, and the C++ version typically never needs
to destroy an instance using the base class type. So a virtual destructor is unnecessary.
The C version below is a mechanical translation though done by hand. I think the above, but then I write the following instead when working in C. Obviously the C version is more verbose. A function pointer typedef is unnecessary. I simply prefer it. When only one method is virtual, the function pointer field appears inline in a base class. Three or more virtual methods typcially go in a separate vtable struct, which is rather more involved.
namespace cy { // gratuitous namespace
class Ear { // listener for key/val notifications
public:
virtual void notify(int32_t key, int32_t val) = 0;
};
}; // namespace cy
I use shorter name ear instead of more conventional name listener, because
brevity is especially helpful after conversion to C. This also shows my preference for
very short names—three or four letters is always better than seven or eight.
The C version below is a mechanical translation though done by hand. I think the above, but then I write the following instead when working in C. Obviously the C version is more verbose. A function pointer typedef is unnecessary. I simply prefer it. When only one method is virtual, the function pointer field appears inline in a base class. Three or more virtual methods typcially go in a separate vtable struct, which is rather more involved.
#ifndef cy_ear_S
#define cy_ear_S 1
typedef struct cy_ear_ cy_ear; /* forward declaration */
#endif
typedef void (*cy_ear_notify_fn)(cy_ear* e, int32_t key, int32_t val);
struct cy_ear_ { /* listener for key/val notifications */
cy_ear_notify_fn ear_notify_fn; /* virtual notify() */
};
static inline void cy_ear_notify(cy_ear* e, int32_t key, int32_t val) {
(*e->ear_notify_fn)(e, key, val);
}
C does not require forward declarations for structs if you don't mind using the
struct keyword every time you name the type. But I do mind. I prefer
a C++ style where a keyword before type name is unnecessarily verbose. And I'm
willing to suffer forward declaration boilerplate to get it. (It costs me no
attention, and cut and paste costs almost nothing.)
lowercase
Wednesday, 04 May 2011
C symbols in thorn are typically lowercase with underscores. This style is well
accepted by current peers at work. (It's expected and no one complains.)
But I use uppercase suffixes in defines. Here's an example:
#define cy_ear_MAX 128That may look strange compared to all uppercase, which is more conventional for defined constants. I'm sure you won't get confused. I like consistent namespace prefixes, and a class (or struct) is a namespace as far as I'm concerned.
concatenation
Wednesday, 04 May 2011
C function names can suffer from ambiguity caused by running all parts together
in a row with no clear separation between namespace, class name, method name,
and argument types. Consider the following example of overloaded constructors.
If instead of iov the type name was fancy_iov, the first constructor would be:
#ifndef cy_iov_S
#define cy_iov_S 1
typedef struct cy_iov_ cy_iov; /* forward declaration */
#endif
struct cy_iov_ {
cy_u8* iov_base; /* 1st byte in memory fragment */
cy_u32 iov_len; /* length of fragment in bytes */
};
static inline void cy_iov_ctor(cy_iov* v, const void* p, cy_u32 n) {
v->iov_base = (cy_u8*) p;
v->iov_len = n;
}
static inline void cy_iov_ctor_cstr(cy_iov* v, const char* cstr) {
v->iov_base = (cy_u8*) cstr;
v->iov_len = (cstr)? strlen(cstr) : 0;
}
Here you see ctor() means constructor, and overloading requires different names,
typically by adding a suffix denoting argument types. The conventional name of thorn
destructors in C is bye() instead of dtor() because that would look too similar
to ctor().
If instead of iov the type name was fancy_iov, the first constructor would be:
static inline void
cy_fancy_iov_ctor(cy_fancy_iov* v, const void* p, cy_u32 n) {
v->iov_base = (cy_u8*) p;
v->iov_len = n;
}
As you can see, nothing marks end of noun and start of verb, assuming types are usually
nouns while methods are verbs. But the type of the first argument indirectly shows you
how much of the method name is type prefix. You might dislike this style of naming
C methods. I find it verbose myself, but it's not too horrible, and seems clear.
comically short names
Sunday, 08 May 2011
Thorn sometimes uses extremely short names, but usually just in alternative C++ class
names. Very few C types consist of just one or two letters. Consider queues:
queues
Since queue is pronounced the same as q, thorn uses just one letter. And qe means queue element: a member inside a queue.
typedef struct cy_qe_ { /* queue element */
cy_qe* qe_next;
cy_qe* qe_prev;
cy_q* qe_q;
} cy_qe;
typedef struct cy_q_ { /* queue: double-ended linked list of elements */
cy_qe q_head;
const char* q_name;
cy_u32 q_size;
} cy_q;
These structs are used often in thorn: everywhere a list is maintained, and some
parts have a lot of lists. It helps to learn q means queue early. As a suffix,
a q at the end of a symbol means it is some kind of queue. For example,
runq means run queue.