Specifying Object Names

Because naming conventions differ between C and Dylan, Melange attempts to translate the names specified in C declarations into a form more appropriate to Dylan. This involves

In many cases, this default behavior will be precisely what you want. However, Melange provides mechanisms for specifying different translations for some or all of the declarations.

Mapping functions

The translations described above are provided by calls to a built-in "name mapping function" named "minimal-name-mapping-with-structure-prefix". You may specify other mapping functions via a "name-mapper:" option. Our example interface might then look like this:

define interface
   #include "gc.h",
      object-file: "/usr/lib/mindy/gc.o",
      name-mapper: c-to-dylan;
end interface;

Table 4-1. Standard Name Mapping Functions

minimal-name-mapping-with-structure-prefixProvides the translations described above.
minimal-name-mappingSame as above, but excludes the "struct-name$" prefixes.
c-to-dylan

Like minimal-name-mapping, but:

  • Adds hyphens to reinforce "CaseBased" word separation.

  • Adds "get-" prefixes to slot accessors.

identity-name-mappingDoes no translation.

Table 4-1 describes the four standard mapping functions that are provided by Melange. Users may link new mapping functions into Melange. In the Mindy implementation, this is done as follows:

  1. Create a new module which imports module "name-mappers" from library "c-parse".

  2. Define methods on the "map-name" generic function which accepts the following parameters:

    mapper

    a <symbol> which is typically specialized by a singleton to select a specific name mapper method.

    category

    a <symbol> which will always be one of: #"type", #"constant", #"variable", or #"function".

    prefix

    a <string> which is typically prepended to the result string.

    name

    a <string> which supplies the original C name.

    sequence-of-classes

    a sequence of simple names for the classes which logically "contain" the given object. For example, if we were processing the declaration "struct str {int size; char *chars", one of the calls to the mapping function would have with namebound to "size" and classes bound to #["str"].

    and returns a <string> which will be used as the Dylan name for the declaration.

  3. Compile this module and "link" it into Melange by concatenating it to the end of the melange.dbc.

Mapping functions may call "hyphenate-case-breaks" which performs the same "CaseBased separation" as is done by "c-to-dylan". The trivial "identity-name-mapping" described above might be implemented by:

define method map-name
   (mapper == #"identity-name-mapping", category, prefix, name, classes)
=> (result :: <string>)
   name;
end method map-name;

You may specify different name mappers to be applied to the slots of "container types". This capability is described in a later section.

Prefixes

As noted above, the name mapping function is passed a "prefix" argument. By default, it is an empty string, but users may specify a different value by adding a "prefix:" option to the interface definition. For example, we might expand the previous example to:

define interface
   #include "gc.h",
      object-file: "/usr/lib/mindy/gc.o",
      name-mapper: c-to-dylan,
      prefix: "gc-";
end interface;

This would cause Melange to tack "gc-" onto the beginning of every translated symbol. Because the system knows about the "standard" Dylan naming conventions, it can do this intelligently. You would, therefore, get names like "<gc-bool>", "gc-time-to-gc", and "gc-scavenge".

Note that the interpretation of the "prefix" is entirely up to the name mapping routine. Identity-name-mapping, for example, completely ignores the prefix. All of the other standard mapping functions prepend it to the name before adding brackets or dollar signs, but after performing all other transformations.

Facilities for adding "localized" prefixes to slot accessors, enumeration literals, etc. will be described in later sections.

Explicit Renaming

Although the automatic name mapping described above is sufficient for most objects named within a header file, there are cases in which you might wish to explicitly control the name of one or more specific objects. You can do this through a "rename:" option. This options specifies a list of translations between raw C names and Dylan identifiers. For example, we might have:

define interface
   #include "gc.h",
      object-file: "/usr/lib/mindy/gc.o",
      name-mapper: c-to-dylan,
      prefix: "gc-"
      rename: {"struct obj" => <C-Object>, "collect_garbage" => GC};
end interface;

Note that the "target" of the renaming is an ordinary Dylan variable and is therefore case-insensitive. However, the source is an "alien name", which is (like all C code) case sensitive. Alien names should refer to an object, function, or type in exactly the same way you would refer to them in C. We therefore say "struct obj" instead of simply "obj", and might also say "enum foo" or "union bar". Alien names are actually parsed according to the standard lexical conventions of C, so you may use arbitrary spacing and even include comments if you really wish.

Note that "rename:" options supply names for new objects (and types) that are being imported into Dylan. You cannot, therefore, simply rename "bool" to "<Boolean>" to make it equivalent to the existing type -- this would simply result in a name conflict. For these purposes, you would instead use the "equate" and "map" operations, which will be described later. (In fact, if the C declaration had defined a type name "boolean", you might have to explicitly rename it to something else in order to avoid name conflicts with the existing type. Of course, in the above example, the "gc-" prefix would be sufficient to make the name unique.)

Anonymous Types

The alien names described above can also be used to refer to C's so-called "anonymous types". You can therefore refer to "char *", "int [23]", or even "int (*) (char *foo)" (i.e. a pointer to function which takes a string and returns an integer) [At present, function types are not fully supported. You should not depend upon them to work as expected.] . The ability to refer to anonymous types is useful because it allows you to use the "rename" option to provide explicit names for such types. Normally Melange would simply generate a an arbitrary "anonymous" identifier for the type. Without knowing the name of this type, you could not define new operations upon it. However, by saying, for example, "rename: {"char * => <char-ptr>"}", you can provide a convenient handle to use in defining new operations.