Working with Strong Types

The essence of C programming is the clever and subtle use of pointers. C is a weakly typed language which means that although it has a simple type system there are many cases where pointers to one type of data can be freely passed to functions requiring pointers to some other kind of data. Additionally because C lacks function overloading, the compiler is often much more forgiving of passing function pointers using poorly specified argument type lists function specifications (this is really just the same weak typing problem).

In contrast, C++ is a strongly typed language. C++ has a very rich type system and the compiler expects (requires) the programmer to not abuse this system. It is never neccessary in C++ to abuse types the way it is in C so anything that looks like an implicit type abuse must be explicitly cast in C++. This means that when passing pointers to objects (internal types or user defined classes) to functions written in C there must be an explicit cast of the pointer type unless the object is of the same type as in the functions declaration.

This comes into play in Apache API with the various functions which take pointers to arbitrary typed callback functions with arbitrary arguments (usually passed as pointers to generic argument-less functions) or pointers to arbitrarily typed data (passed as void*). Examples are the configuration directive handler function pointer in the command_rec structure, and the callback function used in the ap_table_do() function.

Typecasting functions uses a very obscure syntax and should always be done with a typedef.

It is probably the case that a true C++ programmer should use one of the new casting mechanisms, like static_cast. But I have not tried that out yet. Old-fashioned casting is not deprecated by the C++ standard but Stroustrup suggests that programmers avoid it.

Example

Source File

// Pay attention only to the casting in this example. The rest just
// puts it in some arbitrary context for clarity.

// Define a type for the config directive handler type. This is based
// on the way the second item in the command_rec struct is declared
// and _not_ on the reality of what is actually passed to the
// function. In reality these function pointers take a variable number of
// arguments based on the value of the other members of the struct (like
// TAKE1 below).
typedef const char *(*CMD_HAND_TYPE) ();
static command_rec fast3lpoad_cmds[] =
{
    {
        "Fast3lpoadDSODir",             /* directive name */
        // Here we cast the generic api function to have the right 
        // type for this struct. This would be done implicitly in C but
        // must be explicit in C++.
        (CMD_HAND_TYPE)ap_set_string_slot, /* built in handler */
        // This is a special shortcut macro, see apache docs, note the
        // need for a void* cast.
        (void *)XtOffsetOf(fast3lpoad_dir_config, dsopath),
        OR_ALL,                          /* where available */
        TAKE1,                           /* arguments */
        "Name of environment variable which holds config file location."
                                         /* directive description */
    },
    // Note the need for type correctness here.
    {NULL, NULL, NULL, 0, cmd_how(0), NULL}
};

int somefunction(request_rec *r) {
    // Define fastenv as an instance of a user defined C++ class.
    map fastenv;

    // Now when we pass the pointer to fastenv we have to cast it to
    // void* implicitly. We don't have to cast the function because
    // it's definition matches that expected by ap_table_do's
    // declaration.
    ap_table_do(env_map_insert_callback,
                (void *)&fastenv,
                r->subprocess_env,
                NULL);
}

static int env_map_insert_callback(void *map,
                                   const char *key,
                                   const char *val) {

    // We have to cast the object that is passed in back to whatever
    // it was declared as above because the void* cast threw away the
    // type information. If you do this cast wrong the compiler will
    // not notice the mistake and you will probably segfault.
     map *fastenv_p =
        (map)(map);
    fastenv_p->insert(make_pair(key, val));
    return TRUE;
}


Zachary C. Miller
Last modified: Mon Sep 20 14:40:53 CDT 1999