/*
 * The code generator module for SIP.
 *
 * Copyright (c) 2007
 * 	Riverbank Computing Limited <info@riverbankcomputing.co.uk>
 * 
 * This file is part of SIP.
 * 
 * This copy of SIP is licensed for use under the terms of the SIP License
 * Agreement.  See the file LICENSE for more details.
 * 
 * SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */


#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include "sip.h"


/*
 * These must match the values of SIP_TYPE_FLAGS_SHIFT and SIP_TYPE_FLAGS_MASK
 * in siplib/sip.h.
 */
#define TYPE_FLAGS_SHIFT        8
#define TYPE_FLAGS_MASK         0x0f00


/* Control what generateSingleArg() actually generates. */
typedef enum {
    Call,
    Declaration,
    Definition
} funcArgType;


/* An entry in the sorted array of methods. */
typedef struct {
    memberDef *md;                      /* The method. */
    int is_static;                      /* Set if all overloads are static. */
} sortedMethTab;


static int currentLineNr;               /* Current output line number. */
static char *currentFileName;           /* Current output file name. */
static int previousLineNr;              /* Previous output line number. */
static char *previousFileName;          /* Previous output file name. */
static int exceptions;                  /* Set if exceptions are enabled. */
static int tracing;                     /* Set if tracing is enabled. */
static int generating_c;                /* Set if generating C. */
static int release_gil;                 /* Set if always releasing the GIL. */
static const char *prcode_last = NULL;  /* The last prcode format string. */
static int prcode_xml = FALSE;          /* Set if prcode is XML aware. */


static void generateDocumentation(sipSpec *, char *);
static void generateBuildFile(sipSpec *, char *, char *, int);
static void generateInternalAPIHeader(sipSpec *, char *, stringList *);
static void generateCpp(sipSpec *, char *, char *, int *);
static void generateIfaceCpp(sipSpec *, ifaceFileDef *, char *, char *,
        FILE *);
static void generateMappedTypeCpp(mappedTypeDef *, FILE *);
static void generateImportedMappedTypeHeader(mappedTypeDef *mtd, sipSpec *pt,
        FILE *fp);
static void generateMappedTypeHeader(mappedTypeDef *, int, FILE *);
static void generateClassCpp(classDef *cd, sipSpec *pt, FILE *fp);
static void generateImportedClassHeader(classDef *cd, sipSpec *pt, FILE *fp);
static void generateClassTableEntries(sipSpec *pt, nodeDef *nd, FILE *fp);
static void generateClassHeader(classDef *, int, sipSpec *, FILE *);
static void generateClassFunctions(sipSpec *, classDef *, FILE *);
static void generateShadowCode(sipSpec *, classDef *, FILE *);
static void generateFunction(sipSpec *, memberDef *, overDef *, classDef *,
        classDef *, FILE *);
static void generateFunctionBody(sipSpec *, overDef *, classDef *, classDef *,
        int deref, FILE *);
static void generateTypeDefinition(sipSpec *pt, classDef *cd, FILE *fp);
static void generateTypeInit(sipSpec *, classDef *, FILE *);
static void generateCppCodeBlock(codeBlock *, FILE *);
static void generateUsedIncludes(ifaceFileList *, int, FILE *);
static void generateIfaceHeader(sipSpec *, ifaceFileDef *, char *);
static void generateShadowClassDeclaration(sipSpec *, classDef *, FILE *);
static int hasConvertToCode(argDef *ad);
static void deleteTemps(signatureDef *sd, FILE *fp);
static void gc_ellipsis(signatureDef *sd, FILE *fp);
static void generateArgs(signatureDef *, funcArgType, FILE *);
static void generateVariable(argDef *, int, FILE *);
static void generateNamedValueType(argDef *, char *, FILE *);
static void generateSingleArg(argDef *, int, funcArgType, FILE *);
static void generateBaseType(argDef *, FILE *);
static void generateNamedBaseType(argDef *, char *, FILE *);
static void generateTupleBuilder(signatureDef *, FILE *);
static void generateEmitters(sipSpec *pt, classDef *cd, FILE *fp);
static void generateEmitter(sipSpec *, classDef *, visibleList *, FILE *);
static void generateVirtualHandler(sipSpec *, virtHandlerDef *, FILE *);
static void generateVirtHandlerErrorReturn(argDef *res, FILE *fp);
static void generateVirtualCatcher(sipSpec *, classDef *, int, virtOverDef *,
        FILE *);
static void generateUnambiguousClass(classDef *cd, classDef *scope, FILE *fp);
static void generateProtectedEnums(sipSpec *, classDef *, FILE *);
static void generateProtectedDeclarations(classDef *, FILE *);
static void generateProtectedDefinitions(classDef *, FILE *);
static void generateProtectedCallArgs(overDef *od, FILE *fp);
static void generateConstructorCall(classDef *, ctorDef *, int, FILE *);
static void generateHandleResult(overDef *, int, char *, FILE *);
static void generateOrdinaryFunction(sipSpec *, classDef *, memberDef *,
        FILE *);
static void generateSimpleFunctionCall(fcallDef *, FILE *);
static void generateFunctionCall(classDef *cd, classDef *ocd, overDef *od,
        int deref, FILE *fp);
static void generateCppFunctionCall(classDef *cd, classDef *ocd, overDef *od,
        FILE *fp);
static void generateSlotArg(signatureDef *sd, int argnr, FILE *fp);
static void generateBinarySlotCall(overDef *od, char *op, int deref, FILE *fp);
static void generateNumberSlotCall(overDef *od, char *op, FILE *fp);
static void generateVariableHandler(varDef *, FILE *);
static int generateObjToCppConversion(argDef *, FILE *);
static void generateVarClassConversion(varDef *, FILE *);
static void generateVarMember(varDef *vd, FILE *fp);
static int generateVoidPointers(sipSpec *, classDef *, FILE *);
static int generateChars(sipSpec *, classDef *, FILE *);
static int generateStrings(sipSpec *, classDef *, FILE *);
static sortedMethTab *createFunctionTable(classDef *, int *);
static sortedMethTab *createMethodTable(classDef *, int *);
static int generateMethodTable(classDef *, FILE *);
static void generateEnumMacros(sipSpec *pt, classDef *cd, FILE *fp);
static int generateEnumMemberTable(sipSpec *, classDef *, FILE *);
static int generateInts(sipSpec *, classDef *, FILE *);
static int generateLongs(sipSpec *, classDef *, FILE *);
static int generateUnsignedLongs(sipSpec *, classDef *, FILE *);
static int generateLongLongs(sipSpec *, classDef *, FILE *);
static int generateUnsignedLongLongs(sipSpec *, classDef *, FILE *);
static int generateVariableType(sipSpec *pt, classDef *cd, argType atype,
        const char *eng, const char *s1, const char *s2, FILE *fp);
static int generateDoubles(sipSpec *, classDef *, FILE *);
static int generateEnums(sipSpec *, classDef *, FILE *);
static int generateClasses(sipSpec *, classDef *, FILE *);
static void generateEnumsInline(sipSpec *, FILE *);
static void generateClassesInline(sipSpec *, FILE *);
static void generateAccessFunctions(sipSpec *, classDef *, FILE *);
static void generateConvertToDefinitions(mappedTypeDef *, classDef *, FILE *);
static void generateEncodedClass(sipSpec *, classDef *, int, FILE *);
static int generateArgParser(sipSpec *, signatureDef *, classDef *, ctorDef *,
        overDef *, int, FILE *);
static void generateTry(throwArgs *, FILE *);
static void generateCatch(throwArgs *ta, signatureDef *sd, FILE *fp);
static void generateThrowSpecifier(throwArgs *, FILE *);
static void generateSlot(sipSpec *pt, classDef *cd, enumDef *ed, memberDef *md,
        FILE *fp);
static void generateCastZero(argDef *ad, FILE *fp);
static void generateCallDefaultCtor(ctorDef *ct, FILE *fp);
static int countVirtuals(classDef *);
static int skipOverload(overDef *, memberDef *, classDef *, classDef *, int);
static int compareMethTab(const void *, const void *);
static int compareEnumMembers(const void *, const void *);
static char *getSubFormatChar(char, argDef *);
static char *createIfaceFileName(char *, ifaceFileDef *, char *);
static FILE *createCompilationUnit(sipSpec *pt, char *fname,
        char *description);
static FILE *createFile(sipSpec *, char *, char *);
static void closeFile(FILE *);
static void prScopedName(FILE *fp, scopedNameDef *snd, char *sep);
static void prTypeName(FILE *, argDef *, int);
static void prScopedClassName(FILE *, classDef *, char *);
static int isZeroArgSlot(memberDef *md);
static int isMultiArgSlot(memberDef *md);
static int isIntArgSlot(memberDef *md);
static int isInplaceNumberSlot(memberDef *md);
static int isInplaceSequenceSlot(memberDef *md);
static int needErrorFlag(codeBlock *cb);
static int needNewInstance(argDef *ad);
static int needDealloc(classDef *cd);
static char getBuildResultFormat(argDef *ad);
static const char *getParseResultFormat(argDef *ad, int isres, int xfervh);
static void generateParseResultExtraArgs(argDef *ad, int isres, FILE *fp);
static char *makePartName(char *codeDir, char *mname, int part,
        char *srcSuffix);
static void normaliseArgs(signatureDef *);
static void restoreArgs(signatureDef *);
static const char *slotName(slotType st);
static void ints_intro(classDef *cd, FILE *fp);
static const char *argName(const char *name, codeBlock *cb);
static int usedInCode(codeBlock *code, const char *str);
static void generateDefaultValue(argDef *ad, int argnr, FILE *fp);
static void generateClassFromVoid(classDef *cd, const char *cname,
        const char *vname, FILE *fp);
static void generateMappedTypeFromVoid(mappedTypeDef *mtd, const char *cname,
        const char *vname, FILE *fp);
static int generateSubClassConvertors(sipSpec *pt, FILE *fp);
static void generateRegisterMetaType(classDef *cd, FILE *fp);


/*
 * Generate the code from a specification.
 */
void generateCode(sipSpec *pt, char *codeDir, char *buildfile, char *docFile,
        char *srcSuffix, int except, int trace, int releaseGIL, int parts,
        stringList *xsl)
{
    exceptions = except;
    tracing = trace;
    release_gil = releaseGIL;
    generating_c = pt->genc;

    if (srcSuffix == NULL)
        srcSuffix = (generating_c ? ".c" : ".cpp");

    /* Generate the documentation. */
    if (docFile != NULL)
        generateDocumentation(pt,docFile);

    /* Generate the code. */
    if (codeDir != NULL)
    {
        generateCpp(pt,codeDir,srcSuffix,&parts);
        generateInternalAPIHeader(pt,codeDir,xsl);
    }

    /* Generate the build file. */
    if (buildfile != NULL)
        generateBuildFile(pt,buildfile,srcSuffix,parts);
}


/*
 * Generate the documentation.
 */
static void generateDocumentation(sipSpec *pt, char *docFile)
{
    FILE *fp;
    codeBlock *cb;

    fp = createFile(pt, docFile, NULL);

    for (cb = pt->docs; cb != NULL; cb = cb->next)
        fputs(cb->frag, fp);

    closeFile(fp);
}


/*
 * Generate the build file.
 */
static void generateBuildFile(sipSpec *pt, char *buildFile, char *srcSuffix,
        int parts)
{
    char *mname = pt->module->name;
    ifaceFileDef *iff;
    FILE *fp;

    fp = createFile(pt, buildFile, NULL);

    prcode(fp, "target = %s\nsources = ", mname);

    if (parts)
    {
        int p;

        for (p = 0; p < parts; ++p)
        {
            if (p > 0)
                prcode(fp, " ");

            prcode(fp, "sip%spart%d%s", mname, p, srcSuffix);
        }
    }
    else
    {
        prcode(fp, "sip%scmodule%s", mname, srcSuffix);

        for (iff = pt->ifacefiles; iff != NULL; iff = iff->next)
        {
            if (iff->module != pt->module)
                continue;

            if (iff->type == exception_iface)
                continue;

            prcode(fp, " sip%s%F%s", mname, iff->fqcname, srcSuffix);
        }
    }

    prcode(fp, "\nheaders = sipAPI%s.h", mname);

    for (iff = pt->ifacefiles; iff != NULL; iff = iff->next)
    {
        char *imname;

        imname = (iff->module == pt->module ? mname : iff->module->name);

        prcode(fp, " sip%s%F.h", imname, iff->fqcname);
    }

    prcode(fp, "\n");

    closeFile(fp);
}


/*
 * Generate an expression in C++.
 */
void generateExpression(valueDef *vd, FILE *fp)
{
    while (vd != NULL)
    {
        if (vd->vunop != '\0')
            prcode(fp,"%c",vd->vunop);

        switch (vd->vtype)
        {
        case qchar_value:
            prcode(fp,"'%c'",vd->u.vqchar);
            break;

        case string_value:
            prcode(fp,"\"%s\"",vd->u.vstr);
            break;

        case numeric_value:
            prcode(fp,"%l",vd->u.vnum);
            break;

        case real_value:
            prcode(fp,"%g",vd->u.vreal);
            break;

        case scoped_value:
            if (prcode_xml)
                prScopedName(fp, vd->u.vscp, ".");
            else
                prcode(fp, "%S", vd->u.vscp);

            break;

        case fcall_value:
            generateSimpleFunctionCall(vd->u.fcd,fp);
            break;
        }
 
        if (vd->vbinop != '\0')
            prcode(fp," %c ",vd->vbinop);
 
        vd = vd->next;
    }
}


/*
 * Generate the C++ internal module API header file.
 */
static void generateInternalAPIHeader(sipSpec *pt,char *codeDir,stringList *xsl)
{
    char *hfile, *mname = pt->module->name;
    int noIntro;
    FILE *fp;
    nameDef *nd;
    moduleDef *mod;
    moduleListDef *mld;

    hfile = concat(codeDir,"/sipAPI",mname,".h",NULL);
    fp = createFile(pt,hfile,"Internal module API header file.");

    /* Include files. */

    prcode(fp,
"\n"
"#ifndef _%sAPI_H\n"
"#define _%sAPI_H\n"
"\n"
"\n"
"#include <sip.h>\n"
        ,mname
        ,mname);

    if (optRegisterTypes(pt))
        prcode(fp,
"\n"
"#include <QMetaType>\n"
            );

    /* Define the enabled features. */
    noIntro = TRUE;

    for (mod = pt->modules; mod != NULL; mod = mod->next)
    {
        qualDef *qd;

        for (qd = mod->qualifiers; qd != NULL; qd = qd->next)
            if (qd->qtype == feature_qualifier && !excludedFeature(xsl,qd))
            {
                if (noIntro)
                {
                    prcode(fp,
"\n"
"\n"
"/* These are the features that are enabled. */\n"
                        );

                    noIntro = FALSE;
                }

                prcode(fp,
"#define SIP_FEATURE_%s\n"
                    ,qd->name);
            }
    }

    generateCppCodeBlock(pt->exphdrcode,fp);
    generateCppCodeBlock(pt->hdrcode,fp);

    /* Shortcuts that hide the messy detail of the APIs. */
    noIntro = TRUE;

    for (nd = pt->namecache; nd != NULL; nd = nd->next)
    {
        if (!isClassName(nd))
            continue;

        if (noIntro)
        {
            prcode(fp,
"\n"
"\n"
"/*\n"
" * Convenient names to refer to the names of classes defined in this module.\n"
" * These are part of the public API.\n"
" */\n"
"\n"
                );

            noIntro = FALSE;
        }

        prcode(fp,
"#define sipName_%s  %N\n"
            ,nd->text,nd);
    }

    prcode(fp,
"\n"
"\n"
"/* Convenient names to call the SIP API. */\n"
"#define sipConvertFromSliceObject(o,len,start,stop,step,slen)   PySlice_GetIndicesEx((PySliceObject *)(o),(len),(start),(stop),(step),(slen))\n"
"#define sipIsSubClassInstance(o,wt) PyObject_TypeCheck((o),(PyTypeObject *)(wt))\n"
"\n"
"#define sipMapStringToClass         sipAPI_%s->api_map_string_to_class\n"
"#define sipMapIntToClass            sipAPI_%s->api_map_int_to_class\n"
"#define sipMalloc                   sipAPI_%s->api_malloc\n"
"#define sipFree                     sipAPI_%s->api_free\n"
"#define sipBuildResult              sipAPI_%s->api_build_result\n"
"#define sipCallMethod               sipAPI_%s->api_call_method\n"
"#define sipParseResult              sipAPI_%s->api_parse_result\n"
"#define sipParseArgs                sipAPI_%s->api_parse_args\n"
"#define sipParsePair                sipAPI_%s->api_parse_pair\n"
"#define sipCommonCtor               sipAPI_%s->api_common_ctor\n"
"#define sipCommonDtor               sipAPI_%s->api_common_dtor\n"
"#define sipConvertFromSequenceIndex sipAPI_%s->api_convert_from_sequence_index\n"
"#define sipConvertFromVoidPtr       sipAPI_%s->api_convert_from_void_ptr\n"
"#define sipConvertToCpp             sipAPI_%s->api_convert_to_cpp\n"
"#define sipConvertToVoidPtr         sipAPI_%s->api_convert_to_void_ptr\n"
"#define sipNoFunction               sipAPI_%s->api_no_function\n"
"#define sipNoMethod                 sipAPI_%s->api_no_method\n"
"#define sipAbstractMethod           sipAPI_%s->api_abstract_method\n"
"#define sipBadClass                 sipAPI_%s->api_bad_class\n"
"#define sipBadSetType               sipAPI_%s->api_bad_set_type\n"
"#define sipBadCatcherResult         sipAPI_%s->api_bad_catcher_result\n"
"#define sipBadOperatorArg           sipAPI_%s->api_bad_operator_arg\n"
"#define sipTrace                    sipAPI_%s->api_trace\n"
"#define sipTransfer                 sipAPI_%s->api_transfer\n"
"#define sipTransferBack             sipAPI_%s->api_transfer_back\n"
"#define sipTransferTo               sipAPI_%s->api_transfer_to\n"
"#define sipWrapper_Check            sipAPI_%s->api_wrapper_check\n"
"#define sipGetWrapper               sipAPI_%s->api_get_wrapper\n"
"#define sipGetCppPtr                sipAPI_%s->api_get_cpp_ptr\n"
"#define sipGetComplexCppPtr         sipAPI_%s->api_get_complex_cpp_ptr\n"
"#define sipIsPyMethod               sipAPI_%s->api_is_py_method\n"
"#define sipCallHook                 sipAPI_%s->api_call_hook\n"
"#define sipStartThread              sipAPI_%s->api_start_thread\n"
"#define sipEndThread                sipAPI_%s->api_end_thread\n"
"#define sipEmitSignal               sipAPI_%s->api_emit_signal\n"
"#define sipConnectRx                sipAPI_%s->api_connect_rx\n"
"#define sipDisconnectRx             sipAPI_%s->api_disconnect_rx\n"
"#define sipGetSender                sipAPI_%s->api_get_sender\n"
"#define sipRaiseUnknownException    sipAPI_%s->api_raise_unknown_exception\n"
"#define sipRaiseClassException      sipAPI_%s->api_raise_class_exception\n"
"#define sipRaiseSubClassException   sipAPI_%s->api_raise_sub_class_exception\n"
"#define sipBadLengthForSlice        sipAPI_%s->api_bad_length_for_slice\n"
"#define sipClassName                sipAPI_%s->api_class_name\n"
"#define sipAddClassInstance         sipAPI_%s->api_add_class_instance\n"
"#define sipAddMappedTypeInstance    sipAPI_%s->api_add_mapped_type_instance\n"
"#define sipAddEnumInstance          sipAPI_%s->api_add_enum_instance\n"
"#define sipConvertFromNamedEnum     sipAPI_%s->api_convert_from_named_enum\n"
"#define sipGetAddress               sipAPI_%s->api_get_address\n"
"#define sipFreeConnection           sipAPI_%s->api_free_connection\n"
"#define sipEmitToSlot               sipAPI_%s->api_emit_to_slot\n"
"#define sipSameConnection           sipAPI_%s->api_same_connection\n"
"#define sipPySlotExtend             sipAPI_%s->api_pyslot_extend\n"
"#define sipConvertRx                sipAPI_%s->api_convert_rx\n"
"#define sipAddDelayedDtor           sipAPI_%s->api_add_delayed_dtor\n"
"#define sipCanConvertToInstance     sipAPI_%s->api_can_convert_to_instance\n"
"#define sipCanConvertToMappedType   sipAPI_%s->api_can_convert_to_mapped_type\n"
"#define sipConvertToInstance        sipAPI_%s->api_convert_to_instance\n"
"#define sipConvertToMappedType      sipAPI_%s->api_convert_to_mapped_type\n"
"#define sipForceConvertToInstance   sipAPI_%s->api_force_convert_to_instance\n"
"#define sipForceConvertToMappedType sipAPI_%s->api_force_convert_to_mapped_type\n"
"#define sipReleaseInstance          sipAPI_%s->api_release_instance\n"
"#define sipReleaseMappedType        sipAPI_%s->api_release_mapped_type\n"
"#define sipConvertFromInstance      sipAPI_%s->api_convert_from_instance\n"
"#define sipConvertFromNewInstance   sipAPI_%s->api_convert_from_new_instance\n"
"#define sipConvertFromMappedType    sipAPI_%s->api_convert_from_mapped_type\n"
"#define sipGetState                 sipAPI_%s->api_get_state\n"
"#define sipFindMappedType           sipAPI_%s->api_find_mapped_type\n"
"#define sipLong_AsUnsignedLong      sipAPI_%s->api_long_as_unsigned_long\n"
"#define sipExportSymbol             sipAPI_%s->api_export_symbol\n"
"#define sipImportSymbol             sipAPI_%s->api_import_symbol\n"
"#define sipRegisterIntTypes         sipAPI_%s->api_register_int_types\n"
"#define sipParseSignature           sipAPI_%s->api_parse_signature\n"
"#define sipFindClass                sipAPI_%s->api_find_class\n"
"#define sipFindNamedEnum            sipAPI_%s->api_find_named_enum\n"
"#define sipString_AsChar            sipAPI_%s->api_string_as_char\n"
"#define sipUnicode_AsWChar          sipAPI_%s->api_unicode_as_wchar\n"
"#define sipUnicode_AsWString        sipAPI_%s->api_unicode_as_wstring\n"
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname
        ,mname);

    /* The name strings. */
    noIntro = TRUE;

    for (nd = pt->namecache; nd != NULL; nd = nd->next)
    {
        if (!isUsedName(nd))
            continue;

        if (noIntro)
        {
            prcode(fp,
"\n"
"\n"
"/* The strings used by this module. */\n"
                );

            noIntro = FALSE;
        }

        prcode(fp,
"extern char %N[];\n"
            ,nd);
    }

    /* The unscoped enum macros. */
    generateEnumMacros(pt, NULL, fp);

    prcode(fp,
"\n"
"\n"
"/* The SIP API, this module's API and the APIs of any imported modules. */\n"
"extern const sipAPIDef *sipAPI_%s;\n"
"extern sipExportedModuleDef sipModuleAPI_%s;\n"
        ,mname
        ,mname,mname);

    for (mld = pt->module->allimports; mld != NULL; mld = mld->next)
        prcode(fp,
"extern const sipExportedModuleDef *sipModuleAPI_%s_%s;\n"
            ,mname,mld->module->name);

    if (optQ_OBJECT4(pt))
        prcode(fp,
"\n"
"typedef const QMetaObject *(*sip_qt_metaobject_func)(sipWrapper *,sipWrapperType *,const QMetaObject *);\n"
"extern sip_qt_metaobject_func sip_%s_qt_metaobject;\n"
"\n"
"typedef int (*sip_qt_metacall_func)(sipWrapper *,sipWrapperType *,QMetaObject::Call,int,void **);\n"
"extern sip_qt_metacall_func sip_%s_qt_metacall;\n"
            , mname
            , mname);

    /*
     * Note that we don't forward declare the virtual handlers.  This is
     * because we would need to #include everything needed for their
     * argument types.
     */

    prcode(fp,
"\n"
"#endif\n"
        );

    closeFile(fp);
    free(hfile);
}


/*
 * Return the filename of a source code part on the heap.
 */
static char *makePartName(char *codeDir,char *mname,int part,char *srcSuffix)
{
    char buf[20];

    sprintf(buf,"part%d",part);

    return concat(codeDir,"/sip",mname,buf,srcSuffix,NULL);
}


/*
 * Generate the C/C++ code.
 */
static void generateCpp(sipSpec *pt, char *codeDir, char *srcSuffix, int *parts)
{
    char *cppfile, *mname = pt->module->name;
    int noIntro, nrSccs = 0, files_in_part, max_per_part, this_part;
    int is_inst_class, is_inst_voidp, is_inst_char, is_inst_string;
    int is_inst_int, is_inst_long, is_inst_ulong, is_inst_longlong;
    int is_inst_ulonglong, is_inst_double, is_inst_enum, nr_enummembers;
    int hasexternal = FALSE, slot_extenders = FALSE, ctor_extenders = FALSE;
    FILE *fp;
    moduleListDef *mld;
    classDef *cd;
    memberDef *md;
    ifaceFileDef *iff;
    virtHandlerDef *vhd;
    nameDef *nd;
    exceptionDef *xd;

    /* Calculate the number of files in each part. */
    if (*parts)
    {
        int nr_files = 1;

        for (iff = pt->ifacefiles; iff != NULL; iff = iff->next)
            if (iff->module == pt->module)
                ++nr_files;

        max_per_part = (nr_files + *parts - 1) / *parts;
        files_in_part = 1;
        this_part = 0;

        cppfile = makePartName(codeDir,mname,0,srcSuffix);
    }
    else
        cppfile = concat(codeDir,"/sip",mname,"cmodule",srcSuffix,NULL);

    fp = createCompilationUnit(pt, cppfile, "Module code.");

    prcode(fp,
"\n"
"#include \"sipAPI%s.h\"\n"
"\n"
        ,mname);

    for (iff = pt->ifacefiles; iff != NULL; iff = iff->next)
        if (iff->module == pt->module && iff->type != exception_iface)
            prcode(fp,
"#include \"sip%s%F.h\"\n"
                ,iff->module->name,iff->fqcname);

    generateUsedIncludes(pt->used, FALSE, fp);

    /*
     * If there should be a Qt support API then generate stubs values for the
     * optional parts.  These should be undefined in %ModuleCode if a C++
     * implementation is provided.
     */
    if (pt->qobjclass >= 0)
        prcode(fp,
"\n"
"#define sipQtIsQtSignal                     0\n"
"#define sipQtCreateUniversalSignalShortcut  0\n"
"#define sipQtCreateUniversalSignal          0\n"
"#define sipQtFindUniversalSignalShortcut    0\n"
"#define sipQtFindUniversalSignal            0\n"
"#define sipQtEmitSignalShortcut             0\n"
"#define sipQtEmitSignal                     0\n"
            );

    /* Define the names. */
    noIntro = TRUE;

    for (nd = pt->namecache; nd != NULL; nd = nd->next)
    {
        if (!isUsedName(nd))
            continue;

        if (noIntro)
        {
            prcode(fp,
"\n"
"\n"
"/* Define the strings used by this module. */\n"
                );

            noIntro = FALSE;
        }

        prcode(fp,
"char %N[] = \"%s\";\n"
            ,nd,nd->text);
    }

    /* Generate the C++ code blocks. */
    generateCppCodeBlock(pt->cppcode,fp);

    /* Generate any virtual handler declarations. */
    for (vhd = pt->module->virthandlers; vhd != NULL; vhd = vhd->next)
        if (!isDuplicateVH(vhd))
            generateVirtualHandler(pt,vhd,fp);

    /* Generate the global functions. */
    for (md = pt->othfuncs; md != NULL; md = md->next)
    {
        if (md->module != pt->module)
            continue;

        if (md->slot == no_slot)
            generateOrdinaryFunction(pt,NULL,md,fp);
        else
        {
            overDef *od;

            /*
             * Make sure that there is still an overload and we
             * haven't moved them all to classes.
             */
            for (od = pt->overs; od != NULL; od = od->next)
                if (od->common == md)
                {
                    generateSlot(pt, NULL, NULL, md, fp);
                    slot_extenders = TRUE;
                    break;
                }
        }
    }

    /* Generate any class specific ctor or slot extenders. */
    for (cd = pt->proxies; cd != NULL; cd = cd->next)
    {
        if (cd->ctors != NULL)
        {
            generateTypeInit(pt, cd, fp);
            ctor_extenders = TRUE;
        }

        for (md = cd->members; md != NULL; md = md->next)
        {
            generateSlot(pt, cd, NULL, md, fp);
            slot_extenders = TRUE;
        }
    }

    /* Generate any ctor extender table. */
    if (ctor_extenders)
    {
        prcode(fp,
"\n"
"static sipInitExtenderDef initExtenders[] = {\n"
            );

        for (cd = pt->proxies; cd != NULL; cd = cd->next)
            if (cd->ctors != NULL)
            {
                prcode(fp,
"    {init_%C, ", classFQCName(cd));

                generateEncodedClass(pt, cd, 0, fp);

                prcode(fp, ", NULL},\n"
                    );
            }

        prcode(fp,
"    {NULL, {0, 0, 0}, NULL}\n"
"};\n"
            );
    }

    /* Generate any slot extender table. */
    if (slot_extenders)
    {
        prcode(fp,
"\n"
"static sipPySlotExtenderDef slotExtenders[] = {\n"
            );

        for (md = pt->othfuncs; md != NULL; md = md->next)
        {
            overDef *od;

            if (md->module != pt->module)
                continue;

            if (md->slot == no_slot)
                continue;

            for (od = pt->overs; od != NULL; od = od->next)
                if (od->common == md)
                {
                    prcode(fp,
"    {(void *)slot_%s, %s, {0, 0, 0}},\n"
                        , md->pyname->text, slotName(md->slot));

                    break;
                }
        }

        for (cd = pt->proxies; cd != NULL; cd = cd->next)
            for (md = cd->members; md != NULL; md = md->next)
            {
                prcode(fp,
"    {(void *)slot_%C_%s, %s, ", classFQCName(cd), md->pyname->text, slotName(md->slot));

                generateEncodedClass(pt, cd, 0, fp);

                prcode(fp, "},\n"
                      );
            }

        prcode(fp,
"    {NULL, (sipPySlotType)0, {0, 0, 0}}\n"
"};\n"
            );
    }

    /* Generate the global access functions. */
    generateAccessFunctions(pt,NULL,fp);

    /* Generate the module data structures. */
    if (pt->module->nrclasses > 0)
    {
        nrSccs = generateSubClassConvertors(pt, fp);

        prcode(fp,
"\n"
"\n"
"/*\n"
" * This defines each class in this module.  The values are replaced by the\n"
" * proper Python types during the export process.\n"
" */\n"
"static sipWrapperType *typesTable[] = {\n"
            );

        generateClassTableEntries(pt, &pt->module->root, fp);

        prcode(fp,
"};\n"
            );

        /* Generate the external classes table if needed. */
        for (cd = pt->classes; cd != NULL; cd = cd->next)
        {
            if (!isExternal(cd))
                continue;

            if (cd->iff->module != pt->module)
                continue;

            if (!hasexternal)
            {
                prcode(fp,
"\n"
"\n"
"/* This defines each external type declared in this module, */\n"
"static sipExternalTypeDef externalTypesTable[] = {\n"
                    );

                hasexternal = TRUE;
            }

            prcode(fp,
"    {%d, \"", cd->classnr);
            prScopedName(fp, classFQCName(cd), ".");
            prcode(fp,"\"},\n"
                );
        }

        if (hasexternal)
            prcode(fp,
"    {-1, NULL}\n"
"};\n"
                );
    }

    if (pt->module->nrmappedtypes > 0)
    {
        mappedTypeDef *mtd;
        argDef type;

        type.argflags = 0;
        type.name = NULL;
        type.nrderefs = 0;
        type.defval = NULL;

        prcode(fp,
"\n"
"\n"
"/* This defines each mapped type in this module. */\n"
"static sipMappedType *mappedTypesTable[] = {\n"
            );

        for (mtd = pt->mappedtypes; mtd != NULL; mtd = mtd->next)
        {
            if (mtd->iff->module != pt->module)
                continue;

            type.atype = mapped_type;
            type.u.mtd = mtd;

            prcode(fp,
"    &sipMappedTypeDef_%T,\n"
                ,&type);
        }

        prcode(fp,
"    0\n"
"};\n"
            );
    }

    if (pt->module->nrenums > 0)
    {
        enumDef *ed;

        prcode(fp,
"\n"
"\n"
"/* Define each named enum in this module. */\n"
            );

        /* Generate any slot tables. */
        for (ed = pt->enums; ed != NULL; ed = ed->next)
        {
            memberDef *slot;

            if (ed->module != pt->module || ed->fqcname == NULL)
                continue;

            if (ed->slots == NULL)
                continue;

            for (slot = ed->slots; slot != NULL; slot = slot->next)
                generateSlot(pt, NULL, ed, slot, fp);

            prcode(fp,
"\n"
"static sipPySlotDef slots_%C[] = {\n"
                , ed->fqcname);

            for (slot = ed->slots; slot != NULL; slot = slot->next)
            {
                const char *stype;

                if ((stype = slotName(slot->slot)) != NULL)
                    prcode(fp,
"    {(void *)slot_%C_%s, %s},\n"
                        , ed->fqcname, slot->pyname->text, stype);
            }

            prcode(fp,
"    {0, (sipPySlotType)0}\n"
"};\n"
"\n"
                );
        }

        prcode(fp,
"static sipEnumDef enumTypesTable[] = {\n"
            );

        for (ed = pt->enums; ed != NULL; ed = ed->next)
        {
            char *emname;

            if (ed->module != pt->module || ed->fqcname == NULL)
                continue;

            if (ed->ecd == NULL)
                emname = mname;
            else if (ed->ecd->real == NULL)
                emname = ed->module->name;
            else
                emname = ed->ecd->real->iff->module->name;

            prcode(fp,
"    {\"%s.%P\", ", emname, ed->ecd, ed->pyname->text);

            if (isRenamedEnum(ed) || (ed->ecd != NULL && isRenamedClass(ed->ecd)))
                prcode(fp, "\"%S\", ", ed->fqcname);
            else
                prcode(fp, "NULL, ");

            if (ed->ecd == NULL)
                prcode(fp, "-1");
            else
                prcode(fp, "%d", ed->ecd->classnr);

            if (ed->slots != NULL)
                prcode(fp, ", slots_%C", ed->fqcname);
            else
                prcode(fp, ", NULL");

            prcode(fp, "},\n"
                );
        }

        prcode(fp,
"};\n"
            );

        nr_enummembers = generateEnumMemberTable(pt,NULL,fp);
    }
    else
        nr_enummembers = 0;

    if (pt->module->nrtypedefs > 0)
    {
        typedefDef *td;

        prcode(fp,
"\n"
"\n"
"/*\n"
" * These define each typedef in this module.  They are only needed in case\n"
" * they are used as arguments to Qt signals.\n"
" */\n"
"\n"
"static sipTypedefDef typedefsTable[] = {\n"
            );

        for (td = pt->typedefs; td != NULL; td = td->next)
        {
            char *tdmname, *sat;
            scopedNameDef *fqname;

            if (td->module != pt->module)
                continue;

            fqname = NULL;
            tdmname = NULL;
            sat = "unknown";

            switch (td->type.atype)
            {
            case string_type:
                sat = (td->type.nrderefs == 0 ? "char" : "string");
                break;

            case sstring_type:
                sat = (td->type.nrderefs == 0 ? "schar" : "sstring");
                break;

            case ustring_type:
                sat = (td->type.nrderefs == 0 ? "uchar" : "ustring");
                break;

            case wstring_type:
                sat = (td->type.nrderefs == 0 ? "wchar" : "wstring");
                break;

            case short_type:
                sat = "short";
                break;

            case ushort_type:
                sat = "ushort";
                break;

            case cint_type:
            case int_type:
                sat = "int";
                break;

            case uint_type:
                sat = "uint";
                break;

            case long_type:
                sat = "long";
                break;

            case ulong_type:
                sat = "ulong";
                break;

            case longlong_type:
                sat = "longlong";
                break;

            case ulonglong_type:
                sat = "ulonglong";
                break;

            case cfloat_type:
            case float_type:
                sat = "float";
                break;

            case cdouble_type:
            case double_type:
                sat = "double";
                break;

            case bool_type:
            case cbool_type:
                sat = "bool";
                break;

            case void_type:
                if (td->type.nrderefs != 0)
                    sat = "void";
                break;

            case enum_type:
                if ((fqname = td->type.u.ed->fqcname) != NULL)
                {
                    sat = "enum";

                    if (td->type.u.ed->module != pt->module)
                        tdmname = td->type.u.ed->module->fullname;
                }
                break;

            case class_type:
                sat = "class";
                fqname = classFQCName(td->type.u.cd);

                if (td->type.u.cd->iff->module != pt->module)
                    tdmname = td->type.u.cd->iff->module->fullname;
                break;

            case mapped_type:
                sat = "mtype";
                fqname = td->type.u.mtd->iff->fqcname;

                if (td->type.u.mtd->iff->module != pt->module)
                    tdmname = td->type.u.mtd->iff->module->fullname;
                break;
            }

            prcode(fp,
"    {\"%S\", %s_sat", td->fqname, sat);

            if (fqname != NULL)
                prcode(fp, ", \"%S\"", fqname);
            else
                prcode(fp, ", NULL");

            if (tdmname != NULL)
                prcode(fp, ", \"%s\"", tdmname);
            else
                prcode(fp, ", NULL");

            prcode(fp, "},\n"
                );
        }

        prcode(fp,
"    {NULL, unknown_sat, NULL, NULL}\n"
"};\n"
            );
    }

    if (pt->module->nrvirthandlers > 0)
    {
        prcode(fp,
"\n"
"\n"
"/*\n"
" * This defines the virtual handlers that this module implements and can be\n"
" * used by other modules.\n"
" */\n"
"static sipVirtHandlerFunc virtHandlersTable[] = {\n"
            );

        for (vhd = pt->module->virthandlers; vhd != NULL; vhd = vhd->next)
            if (!isDuplicateVH(vhd))
                prcode(fp,
"    (sipVirtHandlerFunc)sipVH_%s_%d,\n"
                    ,mname,vhd->virthandlernr);

        prcode(fp,
"};\n"
            );
    }

    if (pt->module->allimports != NULL)
    {
        prcode(fp,
"\n"
"\n"
"/* This defines the modules that this module needs to import. */\n"
"static sipImportedModuleDef importsTable[] = {\n"
            );

        for (mld = pt->module->allimports; mld != NULL; mld = mld->next)
            prcode(fp,
"    {\"%s\", %d, NULL},\n"
                , mld->module->fullname, mld->module->version);

        prcode(fp,
"    {NULL, -1, NULL}\n"
"};\n"
            );
    }

    if (nrSccs > 0)
    {
        prcode(fp,
"\n"
"\n"
"/* This defines the class sub-convertors that this module defines. */\n"
"static sipSubClassConvertorDef convertorsTable[] = {\n"
            );

        for (cd = pt->classes; cd != NULL; cd = cd->next)
        {
            if (cd->iff->module != pt->module)
                continue;

            if (cd->convtosubcode == NULL)
                continue;

            prcode(fp,
"    {sipSubClass_%C, ",classFQCName(cd));

            generateEncodedClass(pt,cd->subbase,0,fp);

            prcode(fp,", NULL},\n");
        }

        prcode(fp,
"    {NULL, {0, 0, 0}, NULL}\n"
"};\n"
            );
    }

    /* Generate any license information. */
    if (pt->module->license != NULL)
    {
        licenseDef *ld = pt->module->license;

        prcode(fp,
"\n"
"\n"
"/* Define the module's license. */\n"
"static sipLicenseDef module_license = {\n"
            );

        prcode(fp,
"    \"%s\",\n"
            ,ld->type);

        if (ld->licensee != NULL)
            prcode(fp,
"    \"%s\",\n"
                ,ld->licensee);
        else
            prcode(fp,
"    NULL,\n"
                );

        if (ld->timestamp != NULL)
            prcode(fp,
"    \"%s\",\n"
                ,ld->timestamp);
        else
            prcode(fp,
"    NULL,\n"
                );

        if (ld->sig != NULL)
            prcode(fp,
"    \"%s\"\n"
                ,ld->sig);
        else
            prcode(fp,
"    NULL\n"
                );

        prcode(fp,
"};\n"
            );
    }

    /* Generate each instance table. */
    is_inst_class = generateClasses(pt,NULL,fp);
    is_inst_voidp = generateVoidPointers(pt,NULL,fp);
    is_inst_char = generateChars(pt,NULL,fp);
    is_inst_string = generateStrings(pt,NULL,fp);
    is_inst_int = generateInts(pt,NULL,fp);
    is_inst_long = generateLongs(pt,NULL,fp);
    is_inst_ulong = generateUnsignedLongs(pt,NULL,fp);
    is_inst_longlong = generateLongLongs(pt,NULL,fp);
    is_inst_ulonglong = generateUnsignedLongLongs(pt,NULL,fp);
    is_inst_double = generateDoubles(pt,NULL,fp);
    is_inst_enum = generateEnums(pt,NULL,fp);

    /* Generate any exceptions table. */
    if (pt->module->nrexceptions > 0)
           prcode(fp,
"\n"
"\n"
"static PyObject *exceptionsTable[%d];\n"
            ,pt->module->nrexceptions);

    /* Generate any Qt support API. */
    if (pt->qobjclass >= 0)
        prcode(fp,
"\n"
"\n"
"/* This defines the Qt support API. */\n"
"\n"
"static sipQtAPI qtAPI = {\n"
"    &typesTable[%d],\n"
"    sipQtIsQtSignal,\n"
"    sipQtCreateUniversalSignalShortcut,\n"
"    sipQtCreateUniversalSignal,\n"
"    sipQtFindUniversalSignalShortcut,\n"
"    sipQtFindUniversalSignal,\n"
"    sipQtEmitSignalShortcut,\n"
"    sipQtEmitSignal,\n"
"    sipQtCreateUniversalSlot,\n"
"    sipQtDestroyUniversalSlot,\n"
"    sipQtFindSlot,\n"
"    sipQtConnect,\n"
"    sipQtDisconnect,\n"
"    sipQtSignalsBlocked,\n"
"    sipQtGetSender,\n"
"    sipQtForgetSender,\n"
"    sipQtSameSignalSlotName,\n"
"    sipQtFindConnection\n"
"};\n"
            ,pt->qobjclass);

    prcode(fp,
"\n"
"\n"
"/* This defines this module. */\n"
"sipExportedModuleDef sipModuleAPI_%s = {\n"
"    NULL,\n"
"    SIP_API_MINOR_NR,\n"
"    \"%s\",\n"
"    NULL,\n"
"    %d,\n"
"    %s,\n"
"    %s,\n"
"    %d,\n"
"    %s,\n"
"    %s,\n"
"    %s,\n"
"    %d,\n"
"    NULL,\n"
"    %s,\n"
"    %d,\n"
"    %s,\n"
"    %s,\n"
"    %s,\n"
"    %s,\n"
"    {%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s},\n"
"    %s,\n"
"    %s,\n"
"    %s,\n"
"    %s,\n"
"    %s,\n"
"    NULL\n"
"};\n"
        , mname
        , pt->module->fullname
        , pt->module->version
        , pt->module->allimports != NULL ? "importsTable" : "NULL"
        , pt->qobjclass >= 0 ? "&qtAPI" : "NULL"
        , pt->module->nrclasses
        , pt->module->nrclasses > 0 ? "typesTable" : "NULL"
        , hasexternal ? "externalTypesTable" : "NULL"
        , pt->module->nrmappedtypes > 0 ? "mappedTypesTable" : "NULL"
        , pt->module->nrenums
        , pt->module->nrenums > 0 ? "enumTypesTable" : "NULL"
        , nr_enummembers
        , nr_enummembers > 0 ? "enummembers" : "NULL"
        , pt->module->nrtypedefs > 0 ? "typedefsTable" : "NULL"
        , pt->module->nrvirthandlers > 0 ? "virtHandlersTable" : "NULL"
        , nrSccs > 0 ? "convertorsTable" : "NULL"
        , is_inst_class ? "classInstances" : "NULL"
        , is_inst_voidp ? "voidPtrInstances" : "NULL"
        , is_inst_char ? "charInstances" : "NULL"
        , is_inst_string ? "stringInstances" : "NULL"
        , is_inst_int ? "intInstances" : "NULL"
        , is_inst_long ? "longInstances" : "NULL"
        , is_inst_ulong ? "unsignedLongInstances" : "NULL"
        , is_inst_longlong ? "longLongInstances" : "NULL"
        , is_inst_ulonglong ? "unsignedLongLongInstances" : "NULL"
        , is_inst_double ? "doubleInstances" : "NULL"
        , is_inst_enum ? "enumInstances" : "NULL"
        , pt->module->license != NULL ? "&module_license" : "NULL"
        , pt->module->nrexceptions > 0 ? "exceptionsTable" : "NULL"
        , slot_extenders ? "slotExtenders" : "NULL"
        , ctor_extenders ? "initExtenders" : "NULL"
        , hasDelayedDtors(pt->module) ? "sipDelayedDtors" : "NULL");

    /* Generate the storage for the external API pointers. */
    prcode(fp,
"\n"
"\n"
"/* The SIP API and the APIs of any imported modules. */\n"
"const sipAPIDef *sipAPI_%s;\n"
        ,mname);

    for (mld = pt->module->allimports; mld != NULL; mld = mld->next)
        prcode(fp,
"const sipExportedModuleDef *sipModuleAPI_%s_%s;\n"
            ,mname,mld->module->name);

    if (optQ_OBJECT4(pt))
        prcode(fp,
"\n"
"sip_qt_metaobject_func sip_%s_qt_metaobject;\n"
"sip_qt_metacall_func sip_%s_qt_metacall;\n"
            , mname
            , mname);

    /* Generate the Python module initialisation function. */
    prcode(fp,
"\n"
"\n"
"/* The Python module initialisation function. */\n"
"#if defined(SIP_STATIC_MODULE)\n"
"%svoid init%s()\n"
"#else\n"
"PyMODINIT_FUNC init%s()\n"
"#endif\n"
"{\n"
"   static PyMethodDef sip_methods[] = {\n"
        ,(generating_c ? "" : "extern \"C\" "), mname
        ,mname);

    /* Generate the global functions. */

    for (md = pt->othfuncs; md != NULL; md = md->next)
        if (md->module == pt->module && md->slot == no_slot)
            prcode(fp,
"        {%N, func_%s, METH_VARARGS, NULL},\n"
                ,md->pyname,md->pyname->text);

    prcode(fp,
"        {0, 0, 0, 0}\n"
"    };\n"
"\n"
"   PyObject *sipModule, *sipModuleDict, *sip_sipmod, *sip_capiobj;\n"
"\n"
        );

    /* Generate the pre-initialisation code. */
    generateCppCodeBlock(pt->preinitcode,fp);

    prcode(fp,
"    /* Initialise the module and get it's dictionary. */\n"
"    sipModule = Py_InitModule((char *)sipModuleAPI_%s.em_name,sip_methods);\n"
"    sipModuleDict = PyModule_GetDict(sipModule);\n"
"\n"
"    /* Import the SIP module and get it's API. */\n"
"    sip_sipmod = PyImport_ImportModule((char *)\"sip\");\n"
"\n"
"    if (sip_sipmod == NULL)\n"
"        return;\n"
"\n"
"    sip_capiobj = PyDict_GetItemString(PyModule_GetDict(sip_sipmod),\"_C_API\");\n"
"\n"
"    if (sip_capiobj == NULL || !PyCObject_Check(sip_capiobj))\n"
"        return;\n"
"\n"
        ,mname);

    if (generating_c)
        prcode(fp,
"    sipAPI_%s = (const sipAPIDef *)PyCObject_AsVoidPtr(sip_capiobj);\n"
        ,mname);
    else
        prcode(fp,
"    sipAPI_%s = reinterpret_cast<const sipAPIDef *>(PyCObject_AsVoidPtr(sip_capiobj));\n"
        ,mname);

    prcode(fp,
"\n"
"    /* Export the module and publish it's API. */\n"
"    if (sipAPI_%s->api_export_module(&sipModuleAPI_%s,SIP_API_MAJOR_NR,SIP_API_MINOR_NR,sipModuleDict) < 0)\n"
"       return;\n"
        ,mname
        ,mname);

    noIntro = TRUE;

    for (mld = pt->module->allimports; mld != NULL; mld = mld->next)
    {
        if (noIntro)
        {
            prcode(fp,
"\n"
"    /* Get the APIs of the modules that this one is dependent on. */\n"
                );

            noIntro = FALSE;
        }

        prcode(fp,
"    sipModuleAPI_%s_%s = sipModuleAPI_%s.em_imports[%d].im_module;\n"
            ,mname,mld->module->name,mname,mld->module->modulenr);
    }

    generateClassesInline(pt,fp);
    generateEnumsInline(pt, fp);

    /* Create any exceptions. */
    for (xd = pt->exceptions; xd != NULL; xd = xd->next)
    {
        if (xd->iff->module != pt->module)
            continue;

        if (xd->iff->type != exception_iface)
            continue;

        if (xd->exceptionnr < 0)
            continue;

        prcode(fp,
"\n"
"    if ((exceptionsTable[%d] = PyErr_NewException((char *)\"%s.%s\",", xd->exceptionnr, xd->iff->module->name, xd->pyname);

        if (xd->bibase != NULL)
            prcode(fp, "PyExc_%s", xd->bibase);
        else if (xd->base->iff->module == pt->module)
            prcode(fp, "exceptionsTable[%d]", xd->base->exceptionnr);
        else
            prcode(fp, "sipException_%C", xd->base->iff->fqcname);

        prcode(fp, ",NULL)) == NULL || PyDict_SetItemString(sipModuleDict,\"%s\",exceptionsTable[%d]) < 0)\n"
"        return;\n"
            , xd->pyname, xd->exceptionnr);
    }

    /* Generate any Qt metatype registration calls. */
    if (optRegisterTypes(pt))
        for (cd = pt->classes; cd != NULL; cd = cd->next)
        {
            if (cd->iff->module != pt->module)
                continue;

            generateRegisterMetaType(cd, fp);
        }

    /* Generate the post-initialisation code. */
    generateCppCodeBlock(pt->postinitcode,fp);

    /*
     * This has to be done after the post-initialisation code in case this
     * module is exporting the symbol.
     */
    if (optQ_OBJECT4(pt))
        prcode(fp,
"\n"
"    sip_%s_qt_metaobject = (sip_qt_metaobject_func)sipImportSymbol(\"qtcore_qt_metaobject\");\n"
"    sip_%s_qt_metacall = (sip_qt_metacall_func)sipImportSymbol(\"qtcore_qt_metacall\");\n"
            , mname
            , mname);

    prcode(fp,
"}\n"
        );

    /* Generate the interface source and header files. */
    for (iff = pt->ifacefiles; iff != NULL; iff = iff->next)
    {
        if (iff->module == pt->module && iff->type != exception_iface)
        {
            if (*parts && files_in_part++ == max_per_part)
            {
                /* Close the old part. */
                closeFile(fp);
                free(cppfile);

                /* Create a new one. */
                files_in_part = 1;
                ++this_part;

                cppfile = makePartName(codeDir,mname,this_part,srcSuffix);
                fp = createCompilationUnit(pt, cppfile, "Module code.");
            }

            generateIfaceCpp(pt,iff,codeDir,srcSuffix,(*parts ? fp : NULL));
        }

        generateIfaceHeader(pt,iff,codeDir);
    }

    closeFile(fp);
    free(cppfile);

    /* How many parts we actually generated. */
    if (*parts)
        *parts = this_part + 1;
}


/*
 * Generate all the sub-class convertors for a module.
 */
static int generateSubClassConvertors(sipSpec *pt, FILE *fp)
{
    int nrSccs = 0;
    classDef *cd;

    for (cd = pt->classes; cd != NULL; cd = cd->next)
    {
        if (cd->iff->module != pt->module)
            continue;

        if (cd->convtosubcode == NULL)
            continue;

        prcode(fp,
"\n"
"\n"
"/* Convert to a sub-class if possible. */\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static sipWrapperType *sipSubClass_%C(void **);}\n"
                , classFQCName(cd));

        prcode(fp,
"static sipWrapperType *sipSubClass_%C(void **sipCppRet)\n"
"{\n"
"    %S *sipCpp = reinterpret_cast<%S *>(*sipCppRet);\n"
"    sipWrapperType *sipClass;\n"
"\n"
            , classFQCName(cd)
            , classFQCName(cd->subbase), classFQCName(cd->subbase));

        generateCppCodeBlock(cd->convtosubcode, fp);

        prcode(fp,
"\n"
"    return sipClass;\n"
"}\n"
            );

        ++nrSccs;
    }

    return nrSccs;
}


/*
 * Generate an entry for a class in the classes table and all its children.
 */
static void generateClassTableEntries(sipSpec *pt, nodeDef *nd, FILE *fp)
{
    nodeDef *cnd;

    /* Generate the entry if it's not the root. */
    if (nd->cd != NULL)
        if (isExternal(nd->cd))
            prcode(fp,
"    0,\n"
                );
        else
            prcode(fp,
"    (sipWrapperType *)(void *)&sipType_%s_%C,\n"
                , pt->module->name, classFQCName(nd->cd));

    /* Generate all it's children. */
    for (cnd = nd->child; cnd != NULL; cnd = cnd->next)
        generateClassTableEntries(pt, cnd, fp);
}


/*
 * Generate the structure representing an encoded class.
 */
static void generateEncodedClass(sipSpec *pt,classDef *cd,int last,FILE *fp)
{
    moduleDef *mod = cd->iff->module;

    prcode(fp,"{%u, ",cd->classnr);

    if (mod == pt->module)
        prcode(fp,"255");
    else
        prcode(fp,"%u",mod->modulenr);

    prcode(fp,", %u}",last);
}


/*
 * Generate an ordinary function (ie. not a class method).
 */
static void generateOrdinaryFunction(sipSpec *pt,classDef *cd,memberDef *md,
                     FILE *fp)
{
    overDef *od;

    prcode(fp,
"\n"
"\n"
        );

    if (cd != NULL)
    {
        if (!generating_c)
            prcode(fp,
"extern \"C\" {static PyObject *meth_%C_%s(PyObject *,PyObject *);}\n"
                , classFQCName(cd), md->pyname->text);

        prcode(fp,
"static PyObject *meth_%C_%s(PyObject *,PyObject *sipArgs)\n"
            ,classFQCName(cd),md->pyname->text);

        od = cd->overs;
    }
    else
    {
        if (!generating_c)
            prcode(fp,
"extern \"C\" {static PyObject *func_%s(PyObject *,PyObject *);}\n"
                , md->pyname->text);

        prcode(fp,
"static PyObject *func_%s(PyObject *%s,PyObject *sipArgs)\n"
            ,md->pyname->text,(generating_c ? "sipSelf" : ""));

        od = pt->overs;
    }

    prcode(fp,
"{\n"
"    int sipArgsParsed = 0;\n"
        );

    while (od != NULL)
    {
        if (od->common == md)
            generateFunctionBody(pt,od,cd,cd,TRUE,fp);

        od = od->next;
    }

    prcode(fp,
"\n"
"    /* Raise an exception if the arguments couldn't be parsed. */\n"
"    sipNoFunction(sipArgsParsed,%N);\n"
"\n"
"    return NULL;\n"
"}\n"
        ,md->pyname);
}


/*
 * Generate the table of enum members for a scope.  Return the number of them.
 */
static int generateEnumMemberTable(sipSpec *pt,classDef *cd,FILE *fp)
{
    int i, nr_members;
    enumDef *ed;
    enumMemberDef **etab, **et;

    /* First we count how many. */

    nr_members = 0;

    for (ed = pt->enums; ed != NULL; ed = ed->next)
    {
        enumMemberDef *emd;

        if (ed->ecd != cd || ed->module != pt->module)
            continue;

        if (cd == NULL && ed->fqcname == NULL)
            continue;

        for (emd = ed->members; emd != NULL; emd = emd->next)
            ++nr_members;
    }

    if (nr_members == 0)
        return 0;

    /* Create a table so they can be sorted. */

    etab = sipMalloc(sizeof (enumMemberDef *) * nr_members);

    et = etab;

    for (ed = pt->enums; ed != NULL; ed = ed->next)
    {
        enumMemberDef *emd;

        if (ed->ecd != cd || ed->module != pt->module)
            continue;

        if (cd == NULL && ed->fqcname == NULL)
            continue;

        for (emd = ed->members; emd != NULL; emd = emd->next)
            *et++ = emd;
    }

    qsort(etab,nr_members,sizeof (enumMemberDef *),compareEnumMembers);

    /* Now generate the table. */

    if (cd != NULL)
        prcode(fp,
"\n"
"static sipEnumMemberDef enummembers_%C[] = {\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"\n"
"/* These are the enum members of all global enums. */\n"
"static sipEnumMemberDef enummembers[] = {\n"
        );

    for (i = 0; i < nr_members; ++i)
    {
        enumMemberDef *emd;

        emd = etab[i];

        prcode(fp,
"    {%N, ",emd->pyname);

        if (cd != NULL)
        {
            if (isProtectedEnum(emd->ed))
                prcode(fp,"sip");

            prcode(fp,"%S::%s",classFQCName(cd),emd->cname);
        }
        else
            prcode(fp,"%s"
                ,emd->cname);

        prcode(fp, ", %d},\n", emd->ed->enumnr);
    }

    prcode(fp,
"};\n"
        );

    return nr_members;
}


/*
 * The qsort helper to compare two enumMemberDef structures based on the name
 * of the enum member.
 */

static int compareEnumMembers(const void *m1,const void *m2)
{
    return strcmp((*(enumMemberDef **)m1)->pyname->text,
              (*(enumMemberDef **)m2)->pyname->text);
}


/*
 * Generate the access functions for the variables.
 */
static void generateAccessFunctions(sipSpec *pt,classDef *cd,FILE *fp)
{
    varDef *vd;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        if (vd->accessfunc == NULL)
            continue;

        if (vd->ecd != cd || vd->module != pt->module)
            continue;

        prcode(fp,
"\n"
"\n"
"/* Access function. */\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static void *access_%C();}\n"
            , vd->fqcname);

        prcode(fp,
"static void *access_%C()\n"
"{\n"
            ,vd->fqcname);

        generateCppCodeBlock(vd->accessfunc,fp);

        prcode(fp,
"}\n"
            );
    }
}


/*
 * Generate the inline code to add a set of enum instances to a dictionary.
 */
static void generateEnumsInline(sipSpec *pt, FILE *fp)
{
    int noIntro;
    varDef *vd;

    noIntro = TRUE;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        if (vd->module != pt->module)
            continue;

        if (vd->type.atype != enum_type)
            continue;

        if (needsHandler(vd))
            continue;

        /* Skip enums that don't need inline code. */
        if (generating_c || vd->accessfunc != NULL || vd->type.nrderefs != 0)
            continue;

        if (noIntro)
        {
            prcode(fp,
"\n"
"    /* Define the enum instances that have to be added inline. */\n"
                );

            noIntro = FALSE;
        }

        prcode(fp,
"    sipAddEnumInstance(");

        if (vd->ecd == NULL)
            prcode(fp,"sipModuleDict");
        else
            prcode(fp,"(PyObject *)sipClass_%C",classFQCName(vd->ecd));

        prcode(fp,",%N,(int)%S,sipEnum_%C);\n"
            , vd->pyname, vd->fqcname, vd->type.u.ed->fqcname);
    }
}


/*
 * Generate the inline code to add a set of class instances to a dictionary.
 */
static void generateClassesInline(sipSpec *pt,FILE *fp)
{
    int noIntro;
    varDef *vd;

    noIntro = TRUE;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        if (vd->module != pt->module)
            continue;

        if (vd->type.atype != class_type && vd->type.atype != mapped_type)
            continue;

        if (needsHandler(vd))
            continue;

        /* Skip classes that don't need inline code. */
        if (generating_c || vd->accessfunc != NULL || vd->type.nrderefs != 0)
            continue;

        if (noIntro)
        {
            prcode(fp,
"\n"
"    /*\n"
"     * Define the class and mapped type instances that have to be added\n"
"     * inline.\n"
"     */\n"
                );

            noIntro = FALSE;
        }

        if (vd->type.atype == class_type)
            prcode(fp,
"    sipAddClassInstance(");
        else
            prcode(fp,
"    sipAddMappedTypeInstance(");

        if (vd->ecd == NULL)
            prcode(fp,"sipModuleDict");
        else
            prcode(fp,"(PyObject *)sipClass_%C",classFQCName(vd->ecd));

        prcode(fp,",%N,",vd->pyname);

        if (isConstArg(&vd->type))
            prcode(fp,"const_cast<%b *>(&%S)",&vd->type,vd->fqcname);
        else
            prcode(fp,"&%S",vd->fqcname);

        if (vd->type.atype == class_type)
            prcode(fp, ",sipClass_%C);\n"
                , classFQCName(vd->type.u.cd));
        else
            prcode(fp, ",sipMappedType_%T);\n"
                , &vd->type);
    }
}


/*
 * Generate the code to add a set of class instances to a dictionary.  Return
 * TRUE if there was at least one.
 */
static int generateClasses(sipSpec *pt,classDef *cd,FILE *fp)
{
    int noIntro;
    varDef *vd;

    noIntro = TRUE;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        scopedNameDef *vcname;

        if (vd->ecd != cd || vd->module != pt->module)
            continue;

        if (vd->type.atype != class_type)
            continue;

        if (needsHandler(vd))
            continue;

        /*
         * Skip ordinary C++ class instances which need to be done with
         * inline code rather than through a static table.  This is
         * because C++ does not guarantee the order in which the table
         * and the instance will be created.  So far this has only been
         * seen to be a problem when statically linking SIP generated
         * modules on Windows.
         */
        if (!generating_c && vd->accessfunc == NULL && vd->type.nrderefs == 0)
            continue;

        if (noIntro)
        {
            if (cd != NULL)
                prcode(fp,
"\n"
"\n"
"/* Define the class instances to be added to this type dictionary. */\n"
"static sipClassInstanceDef classInstances_%C[] = {\n"
                    ,classFQCName(cd));
            else
                prcode(fp,
"\n"
"\n"
"/* Define the class instances to be added to this module dictionary. */\n"
"static sipClassInstanceDef classInstances[] = {\n"
                    );

            noIntro = FALSE;
        }

        vcname = classFQCName(vd->type.u.cd);

        if (vd->accessfunc != NULL)
        {
            prcode(fp,
"    {%N, (void *)access_%C, &sipClass_%C, SIP_ACCFUNC},\n"
                ,vd->pyname,vd->fqcname,vcname);
        }
        else if (vd->type.nrderefs != 0)
        {
            prcode(fp,
"    {%N, &%S, &sipClass_%C, SIP_INDIRECT},\n"
                ,vd->pyname,vd->fqcname,vcname);
        }
        else if (isConstArg(&vd->type))
        {
            prcode(fp,
"    {%N, const_cast<%b *>(&%S), &sipClass_%C, 0},\n"
                ,vd->pyname,&vd->type,vd->fqcname,vcname);
        }
        else
        {
            prcode(fp,
"    {%N, &%S, &sipClass_%C, 0},\n"
                ,vd->pyname,vd->fqcname,vcname);
        }
    }

    if (!noIntro)
        prcode(fp,
"    {0, 0, 0, 0}\n"
"};\n"
            );

    return !noIntro;
}


/*
 * Generate the code to add a set of void pointers to a dictionary.  Return
 * TRUE if there was at least one.
 */
static int generateVoidPointers(sipSpec *pt,classDef *cd,FILE *fp)
{
    int noIntro;
    varDef *vd;

    noIntro = TRUE;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        if (vd->ecd != cd || vd->module != pt->module)
            continue;

        if (vd->type.atype != void_type && vd->type.atype != struct_type)
            continue;

        if (needsHandler(vd))
            continue;

        if (noIntro)
        {
            if (cd != NULL)
                prcode(fp,
"\n"
"\n"
"/* Define the void pointers to be added to this type dictionary. */\n"
"static sipVoidPtrInstanceDef voidPtrInstances_%C[] = {\n"
                    ,classFQCName(cd));
            else
                prcode(fp,
"\n"
"\n"
"/* Define the void pointers to be added to this module dictionary. */\n"
"static sipVoidPtrInstanceDef voidPtrInstances[] = {\n"
                    );

            noIntro = FALSE;
        }

        if (isConstArg(&vd->type))
            prcode(fp,
"    {%N, const_cast<%b *>(%S)},\n"
                , vd->pyname, &vd->type, vd->fqcname);
        else
            prcode(fp,
"    {%N, %S},\n"
                , vd->pyname, vd->fqcname);
    }

    if (!noIntro)
        prcode(fp,
"    {0, 0}\n"
"};\n"
            );

    return !noIntro;
}


/*
 * Generate the code to add a set of characters to a dictionary.  Return TRUE
 * if there was at least one.
 */
static int generateChars(sipSpec *pt,classDef *cd,FILE *fp)
{
    int noIntro;
    varDef *vd;

    noIntro = TRUE;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        argType vtype = vd->type.atype;

        if (vd->ecd != cd || vd->module != pt->module)
            continue;

        if (!((vtype == sstring_type || vtype == ustring_type || vtype == string_type || vtype == wstring_type) && vd->type.nrderefs == 0))
            continue;

        if (needsHandler(vd))
            continue;

        if (noIntro)
        {
            if (cd != NULL)
                prcode(fp,
"\n"
"\n"
"/* Define the chars to be added to this type dictionary. */\n"
"static sipCharInstanceDef charInstances_%C[] = {\n"
                    ,classFQCName(cd));
            else
                prcode(fp,
"\n"
"\n"
"/* Define the chars to be added to this module dictionary. */\n"
"static sipCharInstanceDef charInstances[] = {\n"
                    );

            noIntro = FALSE;
        }

        prcode(fp,
"    {%N, %S},\n"
            ,vd->pyname,vd->fqcname);
    }

    if (!noIntro)
        prcode(fp,
"    {0, 0}\n"
"};\n"
            );

    return !noIntro;
}


/*
 * Generate the code to add a set of strings to a dictionary.  Return TRUE if
 * there is at least one.
 */
static int generateStrings(sipSpec *pt,classDef *cd,FILE *fp)
{
    int noIntro;
    varDef *vd;

    noIntro = TRUE;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        argType vtype = vd->type.atype;

        if (vd->ecd != cd || vd->module != pt->module)
            continue;

        if (!((vtype == sstring_type || vtype == ustring_type || vtype == string_type || vtype == wstring_type) && vd->type.nrderefs != 0))
            continue;

        if (needsHandler(vd))
            continue;

        if (noIntro)
        {
            if (cd != NULL)
                prcode(fp,
"\n"
"\n"
"/* Define the strings to be added to this type dictionary. */\n"
"static sipStringInstanceDef stringInstances_%C[] = {\n"
                    ,classFQCName(cd));
            else
                prcode(fp,
"\n"
"\n"
"/* Define the strings to be added to this module dictionary. */\n"
"static sipStringInstanceDef stringInstances[] = {\n"
                    );

            noIntro = FALSE;
        }

        prcode(fp,
"    {%N, %S},\n"
            ,vd->pyname,vd->fqcname);
    }

    if (!noIntro)
        prcode(fp,
"    {0, 0}\n"
"};\n"
            );

    return !noIntro;
}


/*
 * Generate the code to add a set of enum instances to a dictionary.  Return
 * TRUE if there was at least one.
 */
static int generateEnums(sipSpec *pt, classDef *cd, FILE *fp)
{
    int noIntro;
    varDef *vd;

    noIntro = TRUE;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        if (vd->ecd != cd || vd->module != pt->module)
            continue;

        if (vd->type.atype != enum_type || vd->type.u.ed->fqcname == NULL)
            continue;

        if (needsHandler(vd))
            continue;

        /* Skip enums that need inline code. */
        if (!generating_c && vd->accessfunc == NULL && vd->type.nrderefs == 0)
            continue;

        if (noIntro)
        {
            if (cd != NULL)
                prcode(fp,
"\n"
"\n"
"/* Define the enum instances to be added to this type dictionary. */\n"
"static sipEnumInstanceDef enumInstances_%C[] = {\n"
                    ,classFQCName(cd));
            else
                prcode(fp,
"\n"
"\n"
"/* Define the enum instances to be added to this module dictionary. */\n"
"static sipEnumInstanceDef enumInstances[] = {\n"
                    );

            noIntro = FALSE;
        }

        prcode(fp,
"    {%N, (int)%S, &sipEnum_%C},\n"
            ,vd->pyname,vd->fqcname,vd->type.u.ed->fqcname);
    }

    if (!noIntro)
        prcode(fp,
"    {0, 0, 0}\n"
"};\n"
            );

    return !noIntro;
}


/*
 * Generate the code to add a set of ints to a dictionary.  Return TRUE if
 * there was at least one.
 */
static int generateInts(sipSpec *pt, classDef *cd, FILE *fp)
{
    int noIntro;
    varDef *vd;
    enumDef *ed;

    noIntro = TRUE;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        argType vtype = vd->type.atype;

        if (vd->ecd != cd || vd->module != pt->module)
            continue;

        if (!(vtype == enum_type || vtype == ushort_type ||
              vtype == short_type || vtype == uint_type ||
              vtype == cint_type || vtype == int_type ||
              vtype == bool_type || vtype == cbool_type))
            continue;

        if (needsHandler(vd))
            continue;

        /* Named enums are handled elsewhere. */
        if (vtype == enum_type && vd->type.u.ed->fqcname != NULL)
            continue;

        if (noIntro)
        {
            ints_intro(cd, fp);
            noIntro = FALSE;
        }

        prcode(fp,
"    {%N, %S},\n"
            ,vd->pyname,vd->fqcname);
    }

    /* Now do global anonymous enums. */
    if (cd == NULL)
        for (ed = pt->enums; ed != NULL; ed = ed->next)
        {
            enumMemberDef *em;

            if (ed->ecd != cd || ed->module != pt->module)
                continue;

            if (ed->fqcname != NULL)
                continue;

            for (em = ed->members; em != NULL; em = em->next)
            {
                if (noIntro)
                {
                    ints_intro(cd, fp);
                    noIntro = FALSE;
                }

                prcode(fp,
"    {%N, %s},\n"
                    , em->pyname, em->cname);
            }
        }

    if (!noIntro)
        prcode(fp,
"    {0, 0}\n"
"};\n"
            );

    return !noIntro;
}


/*
 * Generate the intro for a table of int instances.
 */
static void ints_intro(classDef *cd, FILE *fp)
{
    if (cd != NULL)
        prcode(fp,
"\n"
"\n"
"/* Define the ints to be added to this type dictionary. */\n"
"static sipIntInstanceDef intInstances_%C[] = {\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"\n"
"\n"
"/* Define the ints to be added to this module dictionary. */\n"
"static sipIntInstanceDef intInstances[] = {\n"
            );
}


/*
 * Generate the code to add a set of longs to a dictionary.  Return TRUE if
 * there was at least one.
 */
static int generateLongs(sipSpec *pt, classDef *cd, FILE *fp)
{
    return generateVariableType(pt, cd, long_type, "long", "Long", "long", fp);
}


/*
 * Generate the code to add a set of unsigned longs to a dictionary.  Return
 * TRUE if there was at least one.
 */
static int generateUnsignedLongs(sipSpec *pt, classDef *cd, FILE *fp)
{
    return generateVariableType(pt, cd, ulong_type, "unsigned long", "UnsignedLong", "unsignedLong", fp);
}


/*
 * Generate the code to add a set of long longs to a dictionary.  Return TRUE
 * if there was at least one.
 */
static int generateLongLongs(sipSpec *pt, classDef *cd, FILE *fp)
{
    return generateVariableType(pt, cd, longlong_type, "long long", "LongLong", "longLong", fp);
}


/*
 * Generate the code to add a set of unsigned long longs to a dictionary.
 * Return TRUE if there was at least one.
 */
static int generateUnsignedLongLongs(sipSpec *pt, classDef *cd, FILE *fp)
{
    return generateVariableType(pt, cd, ulonglong_type, "unsigned long long", "UnsignedLongLong", "unsignedLongLong", fp);
}


/*
 * Generate the code to add a set of a particular type to a dictionary.  Return
 * TRUE if there was at least one.
 */
static int generateVariableType(sipSpec *pt, classDef *cd, argType atype, const char *eng, const char *s1, const char *s2, FILE *fp)
{
    int noIntro;
    varDef *vd;

    noIntro = TRUE;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        argType vtype = vd->type.atype;

        if (vd->ecd != cd || vd->module != pt->module)
            continue;

        if (vtype != atype)
            continue;

        if (needsHandler(vd))
            continue;

        if (noIntro)
        {
            if (cd != NULL)
                prcode(fp,
"\n"
"\n"
"/* Define the %ss to be added to this type dictionary. */\n"
"static sip%sInstanceDef %sInstances_%C[] = {\n"
                    , eng
                    , s1, s2, classFQCName(cd));
            else
                prcode(fp,
"\n"
"\n"
"/* Define the %ss to be added to this module dictionary. */\n"
"static sip%sInstanceDef %sInstances[] = {\n"
                    , eng
                    , s1, s2);

            noIntro = FALSE;
        }

        prcode(fp,
"    {%N, %S},\n"
            ,vd->pyname,vd->fqcname);
    }

    if (!noIntro)
        prcode(fp,
"    {0, 0}\n"
"};\n"
            );

    return !noIntro;
}


/*
 * Generate the code to add a set of doubles to a dictionary.  Return TRUE if
 * there was at least one.
 */
static int generateDoubles(sipSpec *pt,classDef *cd,FILE *fp)
{
    int noIntro;
    varDef *vd;

    noIntro = TRUE;

    for (vd = pt->vars; vd != NULL; vd = vd->next)
    {
        argType vtype = vd->type.atype;

        if (vd->ecd != cd || vd->module != pt->module)
            continue;

        if (!(vtype == float_type || vtype == cfloat_type || vtype == double_type || vtype == cdouble_type))
            continue;

        if (needsHandler(vd))
            continue;

        if (noIntro)
        {
            if (cd != NULL)
                prcode(fp,
"\n"
"\n"
"/* Define the doubles to be added to this type dictionary. */\n"
"static sipDoubleInstanceDef doubleInstances_%C[] = {\n"
                    ,classFQCName(cd));
            else
                prcode(fp,
"\n"
"\n"
"/* Define the doubles to be added to this module dictionary. */\n"
"static sipDoubleInstanceDef doubleInstances[] = {\n"
                    );

            noIntro = FALSE;
        }

        prcode(fp,
"    {%N, %S},\n"
            ,vd->pyname,vd->fqcname);
    }

    if (!noIntro)
        prcode(fp,
"    {0, 0}\n"
"};\n"
            );

    return !noIntro;
}


/*
 * Generate the C/C++ code for an interface.
 */
static void generateIfaceCpp(sipSpec *pt,ifaceFileDef *iff,char *codeDir,
                 char *srcSuffix,FILE *master)
{
    char *cppfile, *cmname = iff->module->name;
    classDef *cd;
    mappedTypeDef *mtd;
    FILE *fp;

    if (master == NULL)
    {
        cppfile = createIfaceFileName(codeDir,iff,srcSuffix);
        fp = createCompilationUnit(pt, cppfile, "Interface wrapper code.");
    }
    else
        fp = master;

    prcode(fp,
"\n"
"#include \"sipAPI%s.h\"\n"
"#include \"sip%s%F.h\"\n"
        ,cmname
        ,cmname,iff->fqcname);

    generateUsedIncludes(iff->used, FALSE, fp);

    for (cd = pt->classes; cd != NULL; cd = cd->next)
        if (cd->iff == iff)
        {
            if (isProtectedClass(cd))
                prcode(fp,
"\n"
"#include \"sip%s%F.h\"\n"
                    ,cmname,cd->ecd->iff->fqcname);

            if (!isExternal(cd))
                generateClassCpp(cd, pt, fp);
        }

    for (mtd = pt->mappedtypes; mtd != NULL; mtd = mtd->next)
        if (mtd->iff == iff)
            generateMappedTypeCpp(mtd,fp);

    if (master == NULL)
    {
        closeFile(fp);
        free(cppfile);
    }
}


/*
 * Return a filename for an interface C++ or header file on the heap.
 */
static char *createIfaceFileName(char *codeDir,ifaceFileDef *iff,char *suffix)
{
    char *fn;
    scopedNameDef *snd;

    fn = concat(codeDir,"/sip",iff->module->name,NULL);

    for (snd = iff->fqcname; snd != NULL; snd = snd->next)
        append(&fn,snd->name);

    append(&fn,suffix);

    return fn;
}


/*
 * Generate the C++ code for a mapped type version.
 */
static void generateMappedTypeCpp(mappedTypeDef *mtd,FILE *fp)
{
    int need_xfer;

    prcode(fp,
"\n"
"\n"
"/* Call the mapped type's destructor. */\n"
        );

    if (!generating_c)
        prcode(fp,
"extern \"C\" {static void release_%T(void *, int);}\n"
            , &mtd->type);

    prcode(fp,
"static void release_%T(void *ptr, int%s)\n"
"{\n"
        , &mtd->type, (generating_c ? " status" : ""));

    if (release_gil)
        prcode(fp,
"    Py_BEGIN_ALLOW_THREADS\n"
            );

    if (generating_c)
        prcode(fp,
"    sipFree(ptr);\n"
            );
    else
        prcode(fp,
"    delete reinterpret_cast<%b *>(ptr);\n"
            , &mtd->type);

    if (release_gil)
        prcode(fp,
"    Py_END_ALLOW_THREADS\n"
            );

    prcode(fp,
"}\n"
"\n"
        );

    generateConvertToDefinitions(mtd,NULL,fp);

    /* Generate the from type convertor. */

    need_xfer = (generating_c || usedInCode(mtd->convfromcode, "sipTransferObj"));

    prcode(fp,
"\n"
"\n"
        );

    if (!generating_c)
        prcode(fp,
"extern \"C\" {static PyObject *convertFrom_%T(void *, PyObject *);}\n"
            , &mtd->type);

    prcode(fp,
"static PyObject *convertFrom_%T(void *sipCppV,PyObject *%s)\n"
"{\n"
"   ", &mtd->type, (need_xfer ? "sipTransferObj" : ""));

    generateMappedTypeFromVoid(mtd, "sipCpp", "sipCppV", fp);

    prcode(fp, ";\n"
"\n"
        );

    generateCppCodeBlock(mtd->convfromcode,fp);

    prcode(fp,
"}\n"
"\n"
"\n"
"sipMappedType sipMappedTypeDef_%T = {\n"
"    \"%B\",\n"
"    release_%T,\n"
"    forceConvertTo_%T,\n"
"    convertTo_%T,\n"
"    convertFrom_%T\n"
"};\n"
        , &mtd->type
        , &mtd->type
        , &mtd->type
        , &mtd->type
        , &mtd->type
        , &mtd->type
        , &mtd->type);
}


/*
 * Generate the C++ code for a class.
 */
static void generateClassCpp(classDef *cd,sipSpec *pt,FILE *fp)
{
    varDef *vd;

    /* Generate any local class code. */

    generateCppCodeBlock(cd->cppcode,fp);

    generateClassFunctions(pt,cd,fp);

    generateAccessFunctions(pt,cd,fp);

    /* Generate the variable handlers. */
    if (hasVarHandlers(cd))
    {
        for (vd = pt->vars; vd != NULL; vd = vd->next)
            if (vd->ecd == cd && needsHandler(vd))
                generateVariableHandler(vd,fp);

        /* Generate the variable table. */
        prcode(fp,
"\n"
"PyMethodDef variables_%C[] = {\n"
            ,classFQCName(cd));

        for (vd = pt->vars; vd != NULL; vd = vd->next)
            if (vd->ecd == cd && needsHandler(vd))
                prcode(fp,
"    {%N, var_%C, %s, NULL},\n"
                    ,vd->pyname,vd->fqcname,(isStaticVar(vd) ? "METH_STATIC" : "0"));

        prcode(fp,
"    {0, 0, 0, 0}\n"
"};\n"
            );
    }

    if (cd->iff->type != namespace_iface)
        generateConvertToDefinitions(NULL,cd,fp);

    /* The type definition structure. */
    generateTypeDefinition(pt, cd, fp);
}


/*
 * Return a sorted array of relevant functions for a namespace.
 */

static sortedMethTab *createFunctionTable(classDef *cd,int *nrp)
{
    int nr;
    sortedMethTab *mtab, *mt;
    memberDef *md;

    /* First we need to count the number of applicable functions. */

    nr = 0;

    for (md = cd->members; md != NULL; md = md->next)
        ++nr;

    if ((*nrp = nr) == 0)
        return NULL;

    /* Create the table of methods. */

    mtab = sipMalloc(sizeof (sortedMethTab) * nr);

    /* Initialise the table. */

    mt = mtab;

    for (md = cd->members; md != NULL; md = md->next)
    {
        mt->md = md;
        mt->is_static = TRUE;

        ++mt;
    }

    /* Finally sort the table. */

    qsort(mtab,nr,sizeof (sortedMethTab),compareMethTab);

    return mtab;
}


/*
 * Return a sorted array of relevant methods (either lazy or non-lazy) for a
 * class.
 */
static sortedMethTab *createMethodTable(classDef *cd, int *nrp)
{
    int nr;
    visibleList *vl;
    sortedMethTab *mtab, *mt;

    /*
     * First we need to count the number of applicable methods.  Only provide
     * an entry point if there is at least one overload that is defined in this
     * class and is a non-abstract function or slot.  We allow private (even
     * though we don't actually generate code) because we need to intercept the
     * name before it reaches a more public version further up the class
     * hierarchy.  We add the ctor and any variable handlers as special
     * entries.
     */
    nr = 0;

    for (vl = cd->visible; vl != NULL; vl = vl->next)
    {
        overDef *od;

        if (vl->m->slot != no_slot)
            continue;

        for (od = vl->cd->overs; od != NULL; od = od->next)
        {
            /*
             * Skip protected methods if we don't have the means to
             * handle them.
             */
            if (isProtected(od) && !hasShadow(cd))
                continue;

            if (skipOverload(od,vl->m,cd,vl->cd,TRUE))
                continue;

            ++nr;

            break;
        }
    }

    if ((*nrp = nr) == 0)
        return NULL;

    /* Create the table of methods. */

    mtab = sipMalloc(sizeof (sortedMethTab) * nr);

    /* Initialise the table. */

    mt = mtab;

    for (vl = cd->visible; vl != NULL; vl = vl->next)
    {
        int need_method, is_static;
        overDef *od;

        if (vl->m->slot != no_slot)
            continue;

        need_method = FALSE;
        is_static = TRUE;

        for (od = vl->cd->overs; od != NULL; od = od->next)
        {
            /*
             * Skip protected methods if we don't have the means to
             * handle them.
             */
            if (isProtected(od) && !hasShadow(cd))
                continue;

            if (!skipOverload(od,vl->m,cd,vl->cd,TRUE))
            {
                need_method = TRUE;

                if (!isPrivate(od) && !isStatic(od))
                    is_static = FALSE;
            }
        }

        if (need_method)
        {
            mt->md = vl->m;
            mt->is_static = is_static;

            ++mt;
        }
    }

    /* Finally sort the table. */

    qsort(mtab,nr,sizeof (sortedMethTab),compareMethTab);

    return mtab;
}


/*
 * The qsort helper to compare two sortedMethTab structures based on the Python
 * name of the method.
 */

static int compareMethTab(const void *m1,const void *m2)
{
    return strcmp(((sortedMethTab *)m1)->md->pyname->text,
              ((sortedMethTab *)m2)->md->pyname->text);
}


/*
 * Generate the sorted table of methods and return the number of entries.
 */
static int generateMethodTable(classDef *cd,FILE *fp)
{
    int nr;
    sortedMethTab *mtab;

    mtab = (cd->iff->type == namespace_iface) ?
        createFunctionTable(cd,&nr) :
        createMethodTable(cd,&nr);

    if (mtab != NULL)
    {
        int i;

        prcode(fp,
"\n"
"\n"
"static PyMethodDef methods_%C[] = {\n"
            ,classFQCName(cd));

        for (i = 0; i < nr; ++i)
        {
            memberDef *md = mtab[i].md;

            /*
             * For the moment we are suppressing the generation of
             * METH_STATIC until we understand descriptors better.
             * It could be that they will simplify the handling of
             * lazy attributes and allow things to be cached in the
             * type dictionary.
             */
            mtab[i].is_static = FALSE;

            prcode(fp,
"    {%N, meth_%C_%s, METH_VARARGS%s, NULL}%s\n"
                ,md->pyname,classFQCName(cd),md->pyname->text,(mtab[i].is_static ? "|METH_STATIC" : ""),((i + 1) < nr) ? "," : "");
        }

        free(mtab);

        prcode(fp,
"};\n"
            );
    }

    return nr;
}


/*
 * Generate the "to type" convertor definitions.
 */

static void generateConvertToDefinitions(mappedTypeDef *mtd,classDef *cd,
                     FILE *fp)
{
    codeBlock *convtocode;
    ifaceFileDef *iff;
    argDef type;

    if (cd != NULL)
    {
        convtocode = cd->convtocode;

        iff = cd->iff;

        type.atype = class_type;
        type.u.cd = cd;
    }
    else
    {
        convtocode = mtd->convtocode;

        iff = mtd->iff;

        type.atype = mapped_type;
        type.u.mtd = mtd;
    }

    type.argflags = 0;
    type.name = NULL;
    type.nrderefs = 0;
    type.defval = NULL;

    /* Generate the type convertors. */

    if (convtocode != NULL)
    {
        int need_ptr, need_xfer;

        /*
         * Sometimes type convertors are just stubs that set the error
         * flag, so check if we actually need everything so that we
         * can avoid compiler warnings.
         */
        need_ptr = (generating_c || usedInCode(convtocode, "sipCppPtr"));
        need_xfer = (generating_c || usedInCode(convtocode, "sipTransferObj"));

        prcode(fp,
"\n"
"\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static int convertTo_%T(PyObject *, void **, int *, PyObject *);}\n"
                , &type);

        prcode(fp,
"static int convertTo_%T(PyObject *sipPy,void **%s,int *sipIsErr,PyObject *%s)\n"
"{\n"
            , &type, (need_ptr ? "sipCppPtrV" : ""), (need_xfer ? "sipTransferObj" : ""));

        if (need_ptr)
            if (generating_c)
                prcode(fp,
"    %b **sipCppPtr = (%b **)sipCppPtrV;\n"
"\n"
                    , &type, &type);
            else
                prcode(fp,
"    %b **sipCppPtr = reinterpret_cast<%b **>(sipCppPtrV);\n"
"\n"
                    , &type, &type);

        generateCppCodeBlock(convtocode,fp);

        prcode(fp,
"}\n"
            );
    }

    prcode(fp,
"\n"
"\n"
        );

    if (!generating_c)
        prcode(fp,
"extern \"C\" {static void *forceConvertTo_%T(PyObject *, int *);}\n"
            , &type);

    prcode(fp,
"static void *forceConvertTo_%T(PyObject *valobj,int *iserrp)\n"
"{\n"
"    if (*iserrp || valobj == NULL)\n"
"        return NULL;\n"
"\n"
        ,&type);

    if (convtocode != NULL)
        prcode(fp,
"    if (convertTo_%T(valobj,NULL,NULL,NULL))\n"
"    {\n"
"        void *val;\n"
"\n"
"        /*\n"
"         * Note that we throw away the flag that says if the value\n"
"         * has just been created on the heap or not.\n"
"         */\n"
"        convertTo_%T(valobj,&val,iserrp,NULL);\n"
"\n"
"        return val;\n"
"    }\n"
            ,&type
            ,&type);
    else
        prcode(fp,
"    if (valobj == Py_None || sipIsSubClassInstance(valobj,sipClass_%T))\n"
"        return sipConvertToCpp(valobj,sipClass_%T,iserrp);\n"
            ,&type
            ,&type);

    if (cd != NULL)
        prcode(fp,
"\n"
"    sipBadClass(%N);\n"
            , iff->name);
    else
        prcode(fp,
"\n"
"    sipBadClass(\"%B\");\n"
            , &mtd->type);

    prcode(fp,
"\n"
"    *iserrp = 1;\n"
"\n"
"    return NULL;\n"
"}\n"
        );
}


/*
 * Generate a variable handler.
 */
static void generateVariableHandler(varDef *vd,FILE *fp)
{
    argType atype = vd->type.atype;

    prcode(fp,
"\n"
"\n"
        );

    if (!generating_c)
        prcode(fp,
"extern \"C\" {static PyObject *var_%C(PyObject *, PyObject *);}\n"
            , vd->fqcname);

    prcode(fp,
"static PyObject *var_%C(PyObject *%s,PyObject *sipPy)\n"
"{\n"
        ,vd->fqcname,(isStaticVar(vd) ? "" : "sipSelf"));

    if (atype == class_type || atype == mapped_type)
        prcode(fp,
"    int sipIsErr = 0;\n"
            );

    if (vd->type.nrderefs == 0 && (atype == mapped_type || (atype == class_type && vd->type.u.cd->convtocode != NULL)))
        prcode(fp,
"    int sipValState;\n"
            );

    if (vd->getcode == NULL || vd->setcode == NULL)
    {
        prcode(fp,
"    ");

        generateNamedValueType(&vd->type, "sipVal", fp);

        prcode(fp, ";\n"
            );
    }

    if (!isStaticVar(vd))
    {
        if (generating_c)
            prcode(fp,
"    %S *sipCpp = (%S *)sipGetCppPtr((sipWrapper *)sipSelf,sipClass_%C);\n"
                ,classFQCName(vd->ecd),classFQCName(vd->ecd),classFQCName(vd->ecd));
        else
            prcode(fp,
"    %S *sipCpp = reinterpret_cast<%S *>(sipGetCppPtr((sipWrapper *)sipSelf,sipClass_%C));\n"
                ,classFQCName(vd->ecd),classFQCName(vd->ecd),classFQCName(vd->ecd));

        prcode(fp,
"\n"
"    if (!sipCpp)\n"
"        return NULL;\n"
            );
    }

    prcode(fp,
"\n"
"    if (sipPy == NULL)\n"
"    {\n"
        );

    /* Generate the get handler part. */

    if (vd->getcode != NULL)
    {
        generateCppCodeBlock(vd->getcode, fp);

        prcode(fp,
"\n"
"        return sipPy;\n"
            );
    }
    else
    {
        int pyobj = FALSE;

        prcode(fp,
"        sipVal = %s", (((atype == class_type || atype == mapped_type) && vd->type.nrderefs == 0) ? "&" : ""));

        generateVarMember(vd, fp);

        prcode(fp, ";\n"
"\n"
            );

        switch (atype)
        {
        case mapped_type:
            prcode(fp,
"        sipPy = sipConvertFromMappedType(sipVal,sipMappedType_%T,NULL);\n"
                ,&vd->type);

            break;

        case class_type:
            generateVarClassConversion(vd,fp);
            break;

        case bool_type:
        case cbool_type:
            prcode(fp,
"        sipPy = PyBool_FromLong(sipVal);\n"
                );

            break;

        case sstring_type:
        case ustring_type:
        case string_type:
            if (vd->type.nrderefs == 0)
                prcode(fp,
"        sipPy = PyString_FromStringAndSize(%s&sipVal,1);\n"
                    ,(atype != string_type) ? "(char *)" : "");
            else
                prcode(fp,
"        sipPy = PyString_FromString(%ssipVal);\n"
                    ,(atype != string_type) ? "(char *)" : "");

            break;

        case wstring_type:
            if (vd->type.nrderefs == 0)
                prcode(fp,
"        sipPy = PyUnicode_FromWideChar(&sipVal,1);\n"
                    );
            else
                prcode(fp,
"        sipPy = PyUnicode_FromWideChar(sipVal,(SIP_SSIZE_T)wcslen(sipVal));\n"
                    );

            break;

        case float_type:
        case cfloat_type:
            prcode(fp,
"        sipPy = PyFloat_FromDouble((double)sipVal);\n"
                );
            break;

        case double_type:
        case cdouble_type:
            prcode(fp,
"        sipPy = PyFloat_FromDouble(sipVal);\n"
                );
            break;

        case enum_type:
            if (vd->type.u.ed->fqcname != NULL)
            {
                prcode(fp,
"        sipPy = sipConvertFromNamedEnum(sipVal,sipEnum_%C);\n"
                    ,vd->type.u.ed->fqcname);

                break;
            }

            /* Drop through. */

        case short_type:
        case cint_type:
        case int_type:
            prcode(fp,
"        sipPy = PyInt_FromLong(sipVal);\n"
                );
            break;

        case long_type:
            prcode(fp,
"        sipPy = PyLong_FromLong(sipVal);\n"
                );
            break;

        case ushort_type:
        case uint_type:
        case ulong_type:
            prcode(fp,
"        sipPy = PyLong_FromUnsignedLong(sipVal);\n"
                );
            break;

        case longlong_type:
            prcode(fp,
"        sipPy = PyLong_FromLongLong(sipVal);\n"
                );
            break;

        case ulonglong_type:
            prcode(fp,
"        sipPy = PyLong_FromUnsignedLongLong(sipVal);\n"
                );
            break;

        case struct_type:
            prcode(fp,
"        sipPy = sipConvertFromVoidPtr(");

            if (isConstArg(&vd->type))
                prcode(fp, "const_cast<%b *>(sipVal)", &vd->type);
            else
                prcode(fp, "sipVal");

            prcode(fp, ");\n"
                );

            break;

        case void_type:
            prcode(fp,
"        sipPy = sipConvertFromVoidPtr(");

            if (isConstArg(&vd->type))
                prcode(fp, "const_cast<void *>(sipVal)");
            else
                prcode(fp, "sipVal");

            prcode(fp, ");\n"
                );

            break;

        case pyobject_type:
        case pytuple_type:
        case pylist_type:
        case pydict_type:
        case pycallable_type:
        case pyslice_type:
        case pytype_type:
            prcode(fp,
"        Py_XINCREF(sipVal);\n"
                );
            pyobj = TRUE;
            break;
        }

        prcode(fp,
"\n"
"        return %s;\n"
            ,(pyobj ? "sipVal" : "sipPy"));
    }

    prcode(fp,
"    }\n"
"\n"
        );

    /* Generate the set handler part. */

    if (vd->setcode != NULL)
    {
        prcode(fp,
"    {\n"
"       int sipErr = 0;\n"
"\n"
            );

        generateCppCodeBlock(vd->setcode, fp);

        prcode(fp,
"\n"
"        if (sipErr)\n"
"            return NULL;\n"
"    }\n"
            );
    }
    else
    {
        char *deref;
        int might_be_temp;

        might_be_temp = generateObjToCppConversion(&vd->type,fp);

        deref = "";

        if (atype == class_type || atype == mapped_type)
        {
            if (vd->type.nrderefs == 0)
                deref = "*";

            prcode(fp,
"\n"
"    if (sipIsErr)\n"
"        return NULL;\n"
"\n"
                );
        }
        else
        {
            if ((atype == sstring_type || atype == ustring_type || atype == string_type || atype == wstring_type) && vd->type.nrderefs != 0)
            {
                prcode(fp,
"\n"
"    if (sipVal == NULL)\n"
                    );
            }
            else
                prcode(fp,
"\n"
"    if (PyErr_Occurred() != NULL)\n"
                    );

            prcode(fp,
"    {\n"
"        sipBadSetType(%N,%N);\n"
"        return NULL;\n"
"    }\n"
"\n"
                ,vd->ecd->iff->name,vd->pyname);
        }

        if (atype == pyobject_type || atype == pytuple_type ||
            atype == pylist_type || atype == pydict_type ||
            atype == pycallable_type || atype == pyslice_type ||
            atype == pytype_type)
        {
            prcode(fp,
"    Py_XDECREF(");

            generateVarMember(vd, fp);

            prcode(fp, ");\n"
"    Py_INCREF(sipVal);\n"
"\n"
                );
        }

        prcode(fp,
"    ");

        generateVarMember(vd, fp);

        prcode(fp, " = %ssipVal;\n"
            , deref);

        /* Note that wchar_t * leaks here. */

        if (might_be_temp)
            prcode(fp,
"\n"
"    sipReleaseInstance(sipVal,sipClass_%C,sipValState);\n"
                , classFQCName(vd->type.u.cd));
        else if (vd->type.atype == mapped_type && vd->type.nrderefs == 0)
            prcode(fp,
"\n"
"    sipReleaseMappedType(sipVal,sipMappedType_%T,sipValState);\n"
                , &vd->type);
    }

    prcode(fp,
"\n"
"    Py_INCREF(Py_None);\n"
"    return Py_None;\n"
"}\n"
        );
}


/*
 * Generate the member variable of a class.
 */
static void generateVarMember(varDef *vd, FILE *fp)
{
    if (isStaticVar(vd))
        prcode(fp,"%S::",classFQCName(vd->ecd));
    else
        prcode(fp,"sipCpp->");

    prcode(fp, "%s", scopedNameTail(vd->fqcname));
}


/*
 * Generate an variable class conversion fragment.
 */
static void generateVarClassConversion(varDef *vd,FILE *fp)
{
    classDef *cd = vd->type.u.cd;

    prcode(fp,
"        sipPy = sipConvertFromInstance(");

    if (isConstArg(&vd->type))
        prcode(fp,"const_cast<%b *>(sipVal)",&vd->type);
    else
        prcode(fp,"sipVal");

    prcode(fp,",sipClass_%C,NULL);\n"
        ,classFQCName(cd));
}


/*
 * Generate the declaration of a variable that is initialised from a Python
 * object.  Return TRUE if the value might be a temporary on the heap.
 */
static int generateObjToCppConversion(argDef *ad,FILE *fp)
{
    int might_be_temp = FALSE;
    char *rhs = NULL;

    prcode(fp,
"    sipVal = ");

    switch (ad->atype)
    {
    case mapped_type:
        {
            const char *tail;

            if (generating_c)
            {
                prcode(fp, "(%b *)", ad);
                tail = "";
            }
            else
            {
                prcode(fp, "reinterpret_cast<%b *>(", ad);
                tail = ")";
            }

            /*
             * Note that we don't support /Transfer/ but could do.
             */

            prcode(fp, "sipForceConvertToMappedType(sipPy,sipMappedType_%T,NULL,%s,%s,&sipIsErr)", ad, (ad->nrderefs ? "0" : "SIP_NOT_NONE"), (ad->nrderefs ? "NULL" : "&sipValState"));

            prcode(fp, "%s;\n"
                , tail);
        }
        break;

    case class_type:
        {
            const char *tail;

            if (ad->nrderefs == 0 && ad->u.cd->convtocode != NULL)
                might_be_temp = TRUE;

            if (generating_c)
            {
                prcode(fp, "(%b *)", ad);
                tail = "";
            }
            else
            {
                prcode(fp, "reinterpret_cast<%b *>(", ad);
                tail = ")";
            }

            /*
             * Note that we don't support /Transfer/ but could do.
             * We could also support /Constrained/ (so long as we
             * also supported it for all types).
             */

            prcode(fp, "sipForceConvertToInstance(sipPy,sipClass_%C,NULL,%s,%s,&sipIsErr)", classFQCName(ad->u.cd), (ad->nrderefs ? "0" : "SIP_NOT_NONE"), (might_be_temp ? "&sipValState" : "NULL"));

            prcode(fp, "%s;\n"
                , tail);
        }
        break;

    case enum_type:
        prcode(fp, "(%E)PyInt_AsLong(sipPy);\n"
            , ad->u.ed);
        break;

    case sstring_type:
        if (ad->nrderefs == 0)
            rhs = "(signed char)sipString_AsChar(sipPy)";
        else
            rhs = "(signed char *)PyString_AsString(sipPy)";
        break;

    case ustring_type:
        if (ad->nrderefs == 0)
            rhs = "(unsigned char)sipString_AsChar(sipPy)";
        else
            rhs = "(unsigned char *)PyString_AsString(sipPy)";
        break;

    case string_type:
        if (ad->nrderefs == 0)
            rhs = "sipString_AsChar(sipPy)";
        else
            rhs = "PyString_AsString(sipPy)";
        break;

    case wstring_type:
        if (ad->nrderefs == 0)
            rhs = "sipUnicode_AsWChar(sipPy)";
        else
            rhs = "sipUnicode_AsWString(sipPy)";
        break;

    case float_type:
    case cfloat_type:
        rhs = "(float)PyFloat_AsDouble(sipPy)";
        break;

    case double_type:
    case cdouble_type:
        rhs = "PyFloat_AsDouble(sipPy)";
        break;

    case bool_type:
    case cbool_type:
        rhs = "(bool)PyInt_AsLong(sipPy)";
        break;

    case ushort_type:
        rhs = "(unsigned short)sipLong_AsUnsignedLong(sipPy)";
        break;

    case short_type:
        rhs = "(short)PyInt_AsLong(sipPy)";
        break;

    case uint_type:
        rhs = "(unsigned)sipLong_AsUnsignedLong(sipPy)";
        break;

    case int_type:
    case cint_type:
        rhs = "(int)PyInt_AsLong(sipPy)";
        break;

    case ulong_type:
        rhs = "sipLong_AsUnsignedLong(sipPy)";
        break;

    case long_type:
        rhs = "PyLong_AsLong(sipPy)";
        break;

    case ulonglong_type:
        rhs = "PyLong_AsUnsignedLongLong(sipPy)";
        break;

    case longlong_type:
        rhs = "PyLong_AsLongLong(sipPy)";
        break;

    case struct_type:
        prcode(fp, "(struct %S *)sipConvertToVoidPtr(sipPy);\n"
            , ad->u.sname);
        break;

    case void_type:
        rhs = "sipConvertToVoidPtr(sipPy)";
        break;

    case pyobject_type:
    case pytuple_type:
    case pylist_type:
    case pydict_type:
    case pycallable_type:
    case pyslice_type:
    case pytype_type:
        rhs = "sipPy";
        break;
    }

    if (rhs != NULL)
        prcode(fp, "%s;\n"
            , rhs);

    return might_be_temp;
}


/*
 * Returns TRUE if the given method is a slot that takes zero arguments.
 */
static int isZeroArgSlot(memberDef *md)
{
    slotType st = md->slot;

    return (st == str_slot || st == int_slot || st == long_slot ||
        st == float_slot || st == invert_slot || st == neg_slot ||
        st == len_slot || st == nonzero_slot || st == pos_slot ||
        st == abs_slot || st == repr_slot || st == hash_slot);
}


/*
 * Returns TRUE if the given method is a slot that takes more than one
 * argument.
 */
static int isMultiArgSlot(memberDef *md)
{
    slotType st = md->slot;

    return (st == setitem_slot || st == call_slot);
}


/*
 * Returns TRUE if the given method is a slot that returns void (ie. nothing
 * other than an error indicator).
 */
int isVoidReturnSlot(memberDef *md)
{
    slotType st = md->slot;

    return (st == setitem_slot || st == delitem_slot);
}


/*
 * Returns TRUE if the given method is a slot that returns int.
 */
int isIntReturnSlot(memberDef *md)
{
    slotType st = md->slot;

    return (st == len_slot || st == nonzero_slot || st == contains_slot ||
        st == cmp_slot);
}


/*
 * Returns TRUE if the given method is a slot that returns long.
 */
int isLongReturnSlot(memberDef *md)
{
    slotType st = md->slot;

    return (st == hash_slot);
}


/*
 * Returns TRUE if the given method is a slot that takes an int argument.
 */
static int isIntArgSlot(memberDef *md)
{
    slotType st = md->slot;

    return (st == repeat_slot || st == irepeat_slot);
}


/*
 * Returns TRUE if the given method is an inplace number slot.
 */
static int isInplaceNumberSlot(memberDef *md)
{
    slotType st = md->slot;

    return (st == iadd_slot || st == isub_slot || st == imul_slot ||
        st == idiv_slot || st == imod_slot ||
        st == ior_slot || st == ixor_slot || st == iand_slot ||
        st == ilshift_slot || st == irshift_slot);
}


/*
 * Returns TRUE if the given method is an inplace sequence slot.
 */
static int isInplaceSequenceSlot(memberDef *md)
{
    slotType st = md->slot;

    return (st == iconcat_slot || st == irepeat_slot);
}


/*
 * Returns TRUE if the given method is a number slot slot.
 */
int isNumberSlot(memberDef *md)
{
    slotType st = md->slot;

    return (st == add_slot || st == sub_slot || st == mul_slot ||
        st == div_slot || st == mod_slot ||
        st == and_slot || st == or_slot || st == xor_slot ||
        st == lshift_slot || st == rshift_slot);
}


/*
 * Returns TRUE if the given method is a rich compare slot.
 */
int isRichCompareSlot(memberDef *md)
{
    slotType st = md->slot;

    return (st == lt_slot || st == le_slot || st == eq_slot ||
        st == ne_slot || st == gt_slot || st == ge_slot);
}


/*
 * Generate a Python slot handler for either a class, an enum or an extender.
 */
static void generateSlot(sipSpec *pt, classDef *cd, enumDef *ed, memberDef *md, FILE *fp)
{
    char *arg_str, *prefix, *ret_type;
    int ret_int, nr_args;
    overDef *od, *overs;
    scopedNameDef *fqcname;
    nameDef *pyname;

    if (ed != NULL)
    {
        prefix = "Enum";
        pyname = ed->pyname;
        fqcname = ed->fqcname;
        overs = ed->overs;
    }
    else if (cd != NULL)
    {
        prefix = "Class";
        pyname = cd->iff->name;
        fqcname = classFQCName(cd);
        overs = cd->overs;
    }
    else
    {
        prefix = NULL;
        pyname = NULL;
        fqcname = NULL;
        overs = pt->overs;
    }

    if (isVoidReturnSlot(md) || isIntReturnSlot(md))
    {
        ret_int = TRUE;
        ret_type = "int ";
    }
    else
    {
        ret_int = FALSE;

        if (isLongReturnSlot(md))
            ret_type = "long ";
        else
            ret_type = "PyObject *";
    }

    if (isIntArgSlot(md))
    {
        nr_args = 0;
        arg_str = "PyObject *sipSelf,int a0";
    }
    else if (isMultiArgSlot(md))
    {
        nr_args = 2;
        arg_str = "PyObject *sipSelf,PyObject *sipArgs";
    }
    else if (isZeroArgSlot(md))
    {
        nr_args = 0;
        arg_str = "PyObject *sipSelf";
    }
    else if (isNumberSlot(md))
    {
        nr_args = 2;
        arg_str = "PyObject *sipArg0,PyObject *sipArg1";
    }
    else
    {
        nr_args = 1;
        arg_str = "PyObject *sipSelf,PyObject *sipArg";
    }

    prcode(fp,
"\n"
"\n"
        );

    if (!generating_c)
    {
        prcode(fp,
"extern \"C\" {static %sslot_", ret_type);

        if (fqcname != NULL)
            prcode(fp, "%C_", fqcname);

        prcode(fp, "%s(%s);}\n"
            , md->pyname->text, arg_str);
    }

    prcode(fp,
"static %sslot_", ret_type);

    if (fqcname != NULL)
        prcode(fp, "%C_", fqcname);

    prcode(fp, "%s(%s)\n"
"{\n"
        , md->pyname->text, arg_str);

    if (isInplaceNumberSlot(md))
        prcode(fp,
"    if (!PyObject_TypeCheck(sipSelf,(PyTypeObject *)sip%s_%C))\n"
"    {\n"
"        Py_INCREF(Py_NotImplemented);\n"
"        return Py_NotImplemented;\n"
"    }\n"
"\n"
            , prefix, fqcname);

    if (!isNumberSlot(md))
        if (cd != NULL)
            prcode(fp,
"    %S *sipCpp = reinterpret_cast<%S *>(sipGetCppPtr((sipWrapper *)sipSelf,sipClass_%C));\n"
"\n"
"    if (!sipCpp)\n"
"        return %s;\n"
"\n"
                , fqcname, fqcname, fqcname
                , (md->slot == cmp_slot ? "-2" : (ret_int ? "-1" : "0")));
        else
            prcode(fp,
"    %S sipCpp = static_cast<%S>(PyInt_AsLong(sipSelf));\n"
"\n"
                , fqcname, fqcname);

    if (nr_args > 0)
        prcode(fp,
"    int sipArgsParsed = 0;\n"
            );

    for (od = overs; od != NULL; od = od->next)
        if (od->common == md && isAbstract(od))
        {
            prcode(fp,
"    bool sipSelfWasArg = !sipSelf;\n"
                );

            break;
        }

    for (od = overs; od != NULL; od = od->next)
        if (od->common == md)
            generateFunctionBody(pt, od, cd, cd, (ed == NULL && !dontDerefSelf(od)), fp);

    if (nr_args > 0)
        switch (md->slot)
        {
        case cmp_slot:
            prcode(fp,
"\n"
"    return 2;\n"
                );
            break;

        case concat_slot:
        case iconcat_slot:
        case repeat_slot:
        case irepeat_slot:
            prcode(fp,
"\n"
"    /* Raise an exception if the argument couldn't be parsed. */\n"
"    sipBadOperatorArg(sipSelf,sipArg,%s);\n"
"\n"
"    return NULL;\n"
                ,slotName(md->slot));
            break;

        default:
            if (isNumberSlot(md) || isRichCompareSlot(md))
            {
                /* We can't extend enum slots. */
                if (cd == NULL)
                    prcode(fp,
"\n"
"    Py_INCREF(Py_NotImplemented);\n"
"    return Py_NotImplemented;\n"
                        );
                else if (isNumberSlot(md))
                    prcode(fp,
"\n"
"    return sipPySlotExtend(&sipModuleAPI_%s,%s,NULL,sipArg0,sipArg1);\n"
                        , pt->module->name, slotName(md->slot));
                else
                    prcode(fp,
"\n"
"    return sipPySlotExtend(&sipModuleAPI_%s,%s,sip%s_%C,sipSelf,sipArg);\n"
                        , pt->module->name, slotName(md->slot), prefix, fqcname);
            }
            else if (isInplaceNumberSlot(md))
                prcode(fp,
"\n"
"    PyErr_Clear();\n"
"\n"
"    Py_INCREF(Py_NotImplemented);\n"
"    return Py_NotImplemented;\n"
                    );
            else
                prcode(fp,
"\n"
"    /* Raise an exception if the arguments couldn't be parsed. */\n"
"    sipNoMethod(sipArgsParsed,%N,%N);\n"
"\n"
"    return %s;\n"
                    , pyname, md->pyname
                    ,ret_int ? "-1" : "0");
        }

    prcode(fp,
"}\n"
        );
}


/*
 * Generate the member functions for a class.
 */
static void generateClassFunctions(sipSpec *pt,classDef *cd,FILE *fp)
{
    visibleList *vl;
    memberDef *md;

    /* Any shadow code. */
    if (hasShadow(cd))
        generateShadowCode(pt,cd,fp);

    /* The member functions. */
    for (vl = cd->visible; vl != NULL; vl = vl->next)
        if (vl->m->slot == no_slot)
            generateFunction(pt, vl->m, vl->cd->overs, cd, vl->cd, fp);

    /* The slot functions. */
    for (md = cd->members; md != NULL; md = md->next)
        if (cd->iff->type == namespace_iface)
            generateOrdinaryFunction(pt,cd,md,fp);
        else if (md->slot != no_slot && md->slot != unicode_slot)
            generateSlot(pt, cd, NULL, md, fp);

    if (cd->iff->type != namespace_iface && !generating_c)
    {
        classList *cl;
        int need_ptr, need_state;

        /* The cast function. */
        prcode(fp,
"\n"
"\n"
"/* Cast a pointer to a type somewhere in its superclass hierarchy. */\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static void *cast_%C(void *, sipWrapperType *);}\n"
                , classFQCName(cd));

        prcode(fp,
"static void *cast_%C(void *ptr,sipWrapperType *targetClass)\n"
"{\n"
            ,classFQCName(cd));

        if (cd->supers != NULL)
            prcode(fp,
"    void *res;\n"
"\n"
                );

        prcode(fp,
"    if (targetClass == sipClass_%C)\n"
"        return ptr;\n"
            ,classFQCName(cd));

        for (cl = cd->supers; cl != NULL; cl = cl->next)
        {
            scopedNameDef *sname = cl->cd->iff->fqcname;

            prcode(fp,
"\n"
"    if ((res = sipCast_%C((%S *)(%S *)ptr,targetClass)) != NULL)\n"
"        return res;\n"
                ,sname,sname,classFQCName(cd));
        }

        prcode(fp,
"\n"
"    return NULL;\n"
"}\n"
            );

        /* Generate the release function without compiler warnings. */
        need_ptr = need_state = FALSE;

        if (canCreate(cd) || isPublicDtor(cd))
        {
            if (hasShadow(cd))
                need_ptr = need_state = TRUE;
            else if (isPublicDtor(cd))
                need_ptr = TRUE;
        }

        prcode(fp,
"\n"
"\n"
"/* Call the instance's destructor. */\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static void release_%C(void *, int);}\n"
                , classFQCName(cd));

        prcode(fp,
"static void release_%C(void *%s,int%s)\n"
"{\n"
            , classFQCName(cd), (need_ptr ? "ptr" : ""), (need_state ? " state" : ""));

        /*
         * If there is an explicit public dtor then assume there is
         * some way to call it which we haven't worked out (because we
         * don't fully understand C++).
         */
        if (canCreate(cd) || isPublicDtor(cd))
        {
            int rgil = ((release_gil || isReleaseGILDtor(cd)) && !isHoldGILDtor(cd));

            if (rgil)
                prcode(fp,
"    Py_BEGIN_ALLOW_THREADS\n"
"\n"
                    );

            if (hasShadow(cd))
            {
                prcode(fp,
"    if (state & SIP_DERIVED_CLASS)\n"
"        delete reinterpret_cast<sip%C *>(ptr);\n"
                    , classFQCName(cd));

                if (isPublicDtor(cd))
                    prcode(fp,
"    else\n"
"        delete reinterpret_cast<%U *>(ptr);\n"
                        , cd);
            }
            else if (isPublicDtor(cd))
                prcode(fp,
"    delete reinterpret_cast<%U *>(ptr);\n"
                    , cd);

            if (rgil)
                prcode(fp,
"\n"
"    Py_END_ALLOW_THREADS\n"
                    );
        }

        prcode(fp,
"}\n"
            );
    }

    /* The traverse function. */
    if (cd->travcode != NULL)
    {
        prcode(fp,
"\n"
"\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static int traverse_%C(void *, visitproc, void *);}\n"
                , classFQCName(cd));

        prcode(fp,
"static int traverse_%C(void *sipCppV,visitproc sipVisit,void *sipArg)\n"
"{\n"
"    ", classFQCName(cd));

        generateClassFromVoid(cd, "sipCpp", "sipCppV", fp);

        prcode(fp, ";\n"
"    int sipRes;\n"
"\n"
            );

        generateCppCodeBlock(cd->travcode, fp);

        prcode(fp,
"\n"
"    return sipRes;\n"
"}\n"
            );
    }

    /* The clear function. */
    if (cd->clearcode != NULL)
    {
        prcode(fp,
"\n"
"\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static int clear_%C(void *);}\n"
                , classFQCName(cd));

        prcode(fp,
"static int clear_%C(void *sipCppV)\n"
"{\n"
"    ", classFQCName(cd));

        generateClassFromVoid(cd, "sipCpp", "sipCppV", fp);

        prcode(fp, ";\n"
"    int sipRes;\n"
"\n"
            );

        generateCppCodeBlock(cd->clearcode, fp);

        prcode(fp,
"\n"
"    return sipRes;\n"
"}\n"
            );
    }

    /* The buffer interface functions. */
    if (cd->readbufcode != NULL)
    {
        prcode(fp,
"\n"
"\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static SIP_SSIZE_T getreadbuffer_%C(PyObject *, void *, SIP_SSIZE_T, void **);}\n"
                , classFQCName(cd));

        prcode(fp,
"static SIP_SSIZE_T getreadbuffer_%C(PyObject *%s, void *sipCppV, SIP_SSIZE_T %s, void **%s)\n"
"{\n"
"    ", classFQCName(cd)
     , argName("sipSelf", cd->readbufcode)
     , argName("sipSegment", cd->readbufcode)
     , argName("sipPtrPtr", cd->readbufcode));

        generateClassFromVoid(cd, "sipCpp", "sipCppV", fp);

        prcode(fp, ";\n"
"    SIP_SSIZE_T sipRes;\n"
"\n"
            );

        generateCppCodeBlock(cd->readbufcode, fp);

        prcode(fp,
"\n"
"    return sipRes;\n"
"}\n"
            );
    }

    if (cd->writebufcode != NULL)
    {
        prcode(fp,
"\n"
"\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static SIP_SSIZE_T getwritebuffer_%C(PyObject *, void *, SIP_SSIZE_T, void **);}\n"
                , classFQCName(cd));

        prcode(fp,
"static SIP_SSIZE_T getwritebuffer_%C(PyObject *%s, void *sipCppV, SIP_SSIZE_T %s, void **%s)\n"
"{\n"
"    ", classFQCName(cd)
     , argName("sipSelf", cd->writebufcode)
     , argName("sipSegment", cd->writebufcode)
     , argName("sipPtrPtr", cd->writebufcode));

        generateClassFromVoid(cd, "sipCpp", "sipCppV", fp);

        prcode(fp, ";\n"
"    SIP_SSIZE_T sipRes;\n"
"\n"
            );

        generateCppCodeBlock(cd->writebufcode, fp);

        prcode(fp,
"\n"
"    return sipRes;\n"
"}\n"
            );
    }

    if (cd->segcountcode != NULL)
    {
        prcode(fp,
"\n"
"\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static SIP_SSIZE_T getsegcount_%C(PyObject *, void *, SIP_SSIZE_T *);}\n"
                , classFQCName(cd));

        prcode(fp,
"static SIP_SSIZE_T getsegcount_%C(PyObject *%s, void *sipCppV, SIP_SSIZE_T *%s)\n"
"{\n"
"    ", classFQCName(cd)
     , argName("sipSelf", cd->segcountcode)
     , argName("sipLenPtr", cd->segcountcode));

        generateClassFromVoid(cd, "sipCpp", "sipCppV", fp);

        prcode(fp, ";\n"
"    SIP_SSIZE_T sipRes;\n"
"\n"
            );

        generateCppCodeBlock(cd->segcountcode, fp);

        prcode(fp,
"\n"
"    return sipRes;\n"
"}\n"
            );
    }

    if (cd->charbufcode != NULL)
    {
        prcode(fp,
"\n"
"\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static SIP_SSIZE_T getcharbuffer_%C(PyObject *, void *, SIP_SSIZE_T, void **);}\n"
                , classFQCName(cd));

        prcode(fp,
"static SIP_SSIZE_T getcharbuffer_%C(PyObject *%s, void *sipCppV, SIP_SSIZE_T %s, void **%s)\n"
"{\n"
"    ", classFQCName(cd)
     , argName("sipSelf", cd->charbufcode)
     , argName("sipSegment", cd->charbufcode)
     , argName("sipPtrPtr", cd->charbufcode));

        generateClassFromVoid(cd, "sipCpp", "sipCppV", fp);

        prcode(fp, ";\n"
"    SIP_SSIZE_T sipRes;\n"
"\n"
            );

        generateCppCodeBlock(cd->charbufcode, fp);

        prcode(fp,
"\n"
"    return sipRes;\n"
"}\n"
            );
    }

    /* The dealloc function. */
    if (needDealloc(cd))
    {
        prcode(fp,
"\n"
"\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static void dealloc_%C(sipWrapper *);}\n"
                , classFQCName(cd));

        prcode(fp,
"static void dealloc_%C(sipWrapper *sipSelf)\n"
"{\n"
            ,classFQCName(cd));

        if (tracing)
            prcode(fp,
"    sipTrace(SIP_TRACE_DEALLOCS,\"dealloc_%C()\\n\");\n"
"\n"
                ,classFQCName(cd));

        /* Disable the virtual handlers. */
        if (hasShadow(cd))
            prcode(fp,
"    if (sipIsDerived(sipSelf))\n"
"        reinterpret_cast<sip%C *>(sipSelf->u.cppPtr)->sipPySelf = NULL;\n"
"\n"
                ,classFQCName(cd));

        if (generating_c || isPublicDtor(cd) || (hasShadow(cd) && isProtectedDtor(cd)))
        {
            prcode(fp,
"    if (sipIsPyOwned(sipSelf))\n"
"    {\n"
                );

            if (cd->dealloccode != NULL)
            {
                if (usedInCode(cd->dealloccode, "sipCpp"))
                {
                    prcode(fp,
"        ");

                    generateClassFromVoid(cd, "sipCpp", "sipSelf->u.cppPtr", fp);

                    prcode(fp, ";\n"
                        );
                }

                generateCppCodeBlock(cd->dealloccode,fp);

                prcode(fp,
"\n"
                    );
            }

            if (isDelayedDtor(cd))
                prcode(fp,
"        sipAddDelayedDtor(sipSelf);\n"
                    );
            else if (generating_c)
                prcode(fp,
"        sipFree(sipSelf->u.cppPtr);\n"
                    );
            else
                prcode(fp,
"        release_%C(sipSelf->u.cppPtr,%s);\n"
                    , classFQCName(cd), (hasShadow(cd) ? "sipSelf->flags" : "0"));

            prcode(fp,
"    }\n"
                );
        }

        prcode(fp,
"}\n"
            );
    }

    /* The type initialisation function. */
    if (canCreate(cd))
        generateTypeInit(pt, cd, fp);
}


/*
 * Generate the shadow (derived) class code.
 */
static void generateShadowCode(sipSpec *pt,classDef *cd,FILE *fp)
{
    int nrVirts, virtNr;
    virtOverDef *vod;
    ctorDef *ct;

    nrVirts = countVirtuals(cd);

    /* Generate the wrapper class constructors. */

    for (ct = cd->ctors; ct != NULL; ct = ct->next)
    {
        char *prefix;
        int a;
        ctorDef *dct;

        if (isPrivateCtor(ct))
            continue;

        if (ct->cppsig == NULL)
            continue;

        /* Check we haven't already handled this C++ signature. */
        for (dct = cd->ctors; dct != ct; dct = dct->next)
            if (dct->cppsig != NULL && sameSignature(dct->cppsig, ct->cppsig, TRUE))
                break;

        if (dct != ct)
            continue;

        prcode(fp,
"\n"
"sip%C::sip%C(",classFQCName(cd),classFQCName(cd));

        generateArgs(ct->cppsig,Definition,fp);

        prcode(fp,")%X: %S(",ct->exceptions,classFQCName(cd));

        prefix = "";

        for (a = 0; a < ct->cppsig->nrArgs; ++a)
        {
            prcode(fp,"%sa%d",prefix,a);
            prefix = ",";
        }

        prcode(fp,"), sipPySelf(0)\n"
"{\n"
            );

        if (tracing)
        {
            prcode(fp,
"    sipTrace(SIP_TRACE_CTORS,\"sip%C::sip%C(",classFQCName(cd),classFQCName(cd));
            generateArgs(ct->cppsig,Declaration,fp);
            prcode(fp,")%X (this=0x%%08x)\\n\",this);\n"
"\n"
                ,ct->exceptions);
        }

        prcode(fp,
"    sipCommonCtor(%s,%d);\n"
"}\n"
            ,(nrVirts > 0 ? "sipPyMethods" : "NULL"),nrVirts);
    }

    /* The destructor. */

    if (!isPrivateDtor(cd))
    {
        prcode(fp,
"\n"
"sip%C::~sip%C()%X\n"
"{\n"
            ,classFQCName(cd),classFQCName(cd),cd->dtorexceptions);

        if (tracing)
            prcode(fp,
"    sipTrace(SIP_TRACE_DTORS,\"sip%C::~sip%C()%X (this=0x%%08x)\\n\",this);\n"
"\n"
                ,classFQCName(cd),classFQCName(cd),cd->dtorexceptions);

        if (cd->dtorcode != NULL)
            generateCppCodeBlock(cd->dtorcode,fp);

        prcode(fp,
"    sipCommonDtor(sipPySelf);\n"
"}\n"
            );
    }

    /* The metacall method if required. */
    if (isQObjectSubClass(cd) && optQ_OBJECT4(pt))
    {
        prcode(fp,
"\n"
"const QMetaObject *sip%C::metaObject() const\n"
"{\n"
"    return sip_%s_qt_metaobject(sipPySelf,sipClass_%C,%S::metaObject());\n"
"}\n"
"\n"
"int sip%C::qt_metacall(QMetaObject::Call _c,int _id,void **_a)\n"
"{\n"
"    sip%C::metaObject();\n"
"\n"
"    _id = %S::qt_metacall(_c,_id,_a);\n"
"\n"
"    if (_id >= 0)\n"
"    {\n"
"        SIP_BLOCK_THREADS\n"
"        _id = sip_%s_qt_metacall(sipPySelf,sipClass_%C,_c,_id,_a);\n"
"        SIP_UNBLOCK_THREADS\n"
"    }\n"
"\n"
"    return _id;\n"
"}\n"
            , classFQCName(cd)
            , pt->module->name, classFQCName(cd), classFQCName(cd)
            , classFQCName(cd)
            , classFQCName(cd)
            , classFQCName(cd)
            , pt->module->name, classFQCName(cd));
    }

    /* Generate the virtual catchers. */
 
    virtNr = 0;
 
    for (vod = cd->vmembers; vod != NULL; vod = vod->next)
    {
        overDef *od = &vod->o;
        virtOverDef *dvod;

        if (isPrivate(od))
            continue;

        /* Check we haven't already handled this C++ signature. */
        for (dvod = cd->vmembers; dvod != vod; dvod = dvod->next)
            if (strcmp(dvod->o.cppname,od->cppname) == 0 && sameSignature(dvod->o.cppsig,od->cppsig,TRUE))
                break;

        if (dvod != vod)
            continue;

        generateVirtualCatcher(pt,cd,virtNr++,vod,fp);
    }

    /* Generate the wrapper around each protected member function. */

    generateProtectedDefinitions(cd,fp);

    /* Generate the emitters if needed. */
    if (!optNoEmitters(pt))
        generateEmitters(pt, cd, fp);
}


/*
 * Generate the emitter functions.
 */
static void generateEmitters(sipSpec *pt, classDef *cd, FILE *fp)
{
    int noIntro;
    visibleList *vl;

    for (vl = cd->visible; vl != NULL; vl = vl->next)
    {
        overDef *od;

        for (od = vl->cd->overs; od != NULL; od = od->next)
            if (od->common == vl->m && isSignal(od))
            {
                generateEmitter(pt,cd,vl,fp);
                break;
            }
    }

    /* Generate the table of signals to support fan-outs. */

    noIntro = TRUE;

    for (vl = cd->visible; vl != NULL; vl = vl->next)
    {
        overDef *od;

        for (od = vl->cd->overs; od != NULL; od = od->next)
            if (od->common == vl->m && isSignal(od))
            {
                if (noIntro)
                {
                    setHasSigSlots(cd);

                    prcode(fp,
"\n"
"static sipQtSignal signals_%C[] = {\n"
                        ,classFQCName(cd));

                    noIntro = FALSE;
                }

                prcode(fp,
"    {%N, %C_emit_%s},\n"
                    ,vl->m->pyname,classFQCName(cd),vl->m->pyname->text);

                break;
            }
    }

    if (!noIntro)
        prcode(fp,
"    {NULL, NULL}\n"
"};\n"
            );
}


/*
 * Generate the protected enums for a class.
 */
static void generateProtectedEnums(sipSpec *pt,classDef *cd,FILE *fp)
{
    enumDef *ed;

    for (ed = pt->enums; ed != NULL; ed = ed->next)
    {
        char *eol;
        enumMemberDef *emd;

        /* Ignore unless this class is the publisher. */
        if (cd != ed->pcd)
            continue;

        prcode(fp,
"\n"
"    /* Expose this protected enum. */\n"
"    enum");

        if (ed->fqcname != NULL)
            prcode(fp," sip%s",scopedNameTail(ed->fqcname));

        prcode(fp," {");

        eol = "\n";

        for (emd = ed->members; emd != NULL; emd = emd->next)
        {
            prcode(fp,"%s"
"        %s = %S::%s",eol,emd->cname,classFQCName(ed->ecd),emd->cname);

            eol = ",\n";
        }

        prcode(fp,"\n"
"    };\n"
            );
    }
}


/*
 * Generate the catcher for a virtual function.
 */
static void generateVirtualCatcher(sipSpec *pt, classDef *cd, int virtNr,
        virtOverDef *vod, FILE *fp)
{
    overDef *od = &vod->o;
    virtHandlerDef *vhd = od->virthandler;
    argDef *res, *ad;
    int a;

    normaliseArgs(od->cppsig);

    res = &od->cppsig->result;

    if (res->atype == void_type && res->nrderefs == 0)
        res = NULL;

    prcode(fp,
"\n");

    generateBaseType(&od->cppsig->result,fp);

    prcode(fp," sip%C::%O(",classFQCName(cd),od);
    generateArgs(od->cppsig,Definition,fp);
    prcode(fp,")%s%X\n"
"{\n"
        ,(isConst(od) ? " const" : ""),od->exceptions);

    if (tracing)
    {
        prcode(fp,
"    sipTrace(SIP_TRACE_CATCHERS,\"");

        generateBaseType(&od->cppsig->result,fp);
        prcode(fp," sip%C::%O(",classFQCName(cd),od);
        generateArgs(od->cppsig,Declaration,fp);
        prcode(fp,")%s%X (this=0x%%08x)\\n\",this);\n"
"\n"
            ,(isConst(od) ? " const" : ""),od->exceptions);
    }

    restoreArgs(od->cppsig);

    if (vhd->module == pt->module)
    {
        prcode(fp,
"    extern ");

        generateBaseType(&od->cppsig->result,fp);

        prcode(fp," sipVH_%s_%d(sip_gilstate_t,PyObject *",vhd->module->name,vhd->virthandlernr);
    }
    else
    {
        prcode(fp,
"    typedef ");

        generateBaseType(&od->cppsig->result,fp);

        prcode(fp," (*sipVH_%s_%d)(sip_gilstate_t,PyObject *",vhd->module->name,vhd->virthandlernr);
    }

    if (vhd->cppsig->nrArgs > 0)
    {
        prcode(fp,",");
        generateArgs(vhd->cppsig,Declaration,fp);
    }

    prcode(fp,");\n"
        );

    if (isNewThread(od))
        prcode(fp,
"\n"
"    SIP_BLOCK_THREADS\n"
            );

    prcode(fp,
"\n"
"    sip_gilstate_t sipGILState;\n"
"    PyObject *meth;\n"
"\n"
"    meth = sipIsPyMethod(&sipGILState,");

    if (isConst(od))
        prcode(fp,"const_cast<sipMethodCache *>(");

    prcode(fp,"&sipPyMethods[%d]",virtNr);

    if (isConst(od))
        prcode(fp,")");

    prcode(fp,",sipPySelf,");

    if (isAbstract(od))
        prcode(fp,"%N",cd->iff->name);
    else
        prcode(fp,"NULL");

    prcode(fp,",%N);\n"
"\n"
        ,od->common->pyname);

    if (isNewThread(od))
        prcode(fp,
"    if (meth)\n"
"    {\n"
"        sipStartThread();\n"
"        ");
    else
    {
        prcode(fp,
"    if (!meth)\n"
            );

        if (isAbstract(od))
            generateVirtHandlerErrorReturn(res,fp);
        else
        {
            if (res == NULL)
                prcode(fp,
"    {\n"
"        ");
            else
                prcode(fp,
"        return ");

            generateUnambiguousClass(cd,vod->scope,fp);

            prcode(fp,"::%O(",od);
 
            for (a = 0; a < od->cppsig->nrArgs; ++a)
                prcode(fp,"%sa%d",(a == 0 ? "" : ","),a);
 
            prcode(fp,");\n"
                );

            if (res == NULL)
                prcode(fp,
"        return;\n"
"    }\n"
                    );
        }

        prcode(fp,
"\n"
"    ");

        if (res != NULL)
            prcode(fp,"return ");
    }

    if (vhd->module == pt->module)
        prcode(fp,"sipVH_%s_%d",vhd->module->name,vhd->virthandlernr);
    else
        prcode(fp,"((sipVH_%s_%d)(sipModuleAPI_%s_%s->em_virthandlers[%d]))",vhd->module->name,vhd->virthandlernr,pt->module->name,vhd->module->name,vhd->virthandlernr);

    prcode(fp,"(sipGILState,meth");

    ad = od->cppsig->args;

    for (a = 0; a < od->cppsig->nrArgs; ++a)
    {
        if (ad->atype == class_type && isProtectedClass(ad->u.cd))
            prcode(fp,",static_cast<%U *>(a%d)",ad->u.cd,a);
        else if (ad->atype == enum_type && isProtectedEnum(ad->u.ed))
            prcode(fp, ",(%E)a%d", ad->u.ed, a);
        else
            prcode(fp,",a%d",a);

        ++ad;
    }
 
    prcode(fp,");\n"
        );

    if (isNewThread(od))
        prcode(fp,
"        sipEndThread();\n"
"    }\n"
"\n"
"    SIP_UNBLOCK_THREADS\n"
            );

    prcode(fp,
"}\n"
        );
}


/*
 * Generate the scope of the near class of a virtual taking duplicate
 * super-classes into account.
 */
static void generateUnambiguousClass(classDef *cd,classDef *scope,FILE *fp)
{
    mroDef *mro;

    /* See if the near class has a duplicate. */
    for (mro = cd->mro; mro != NULL; mro = mro->next)
        if (mro->cd == scope)
        {
            if (hasDuplicateSuper(mro))
            {
                mroDef *guardc;

                /*
                 * Backtrack to find the class that directly
                 * sub-classes the duplicated one.  This will
                 * be the one that disambiguates the duplicated
                 * one.
                 */
                guardc = mro;

                while (guardc != cd->mro)
                {
                    mroDef *sub;
                    classList *cl;

                    for (sub = cd->mro; sub->next != guardc; sub = sub->next)
                        ;

                    for (cl = sub->cd->supers; cl != NULL; cl = cl->next)
                        if (cl->cd == mro->cd)
                        {
                            prcode(fp,"%S",classFQCName(sub->cd));

                            return;
                        }

                    /* Try the previous one. */
                    guardc = sub;
                }
            }

            break;
        }

    /* If we got here there is nothing to worry about. */
    prcode(fp,"%S",classFQCName(scope));
}


/*
 * Generate a cast to zero.
 */
static void generateCastZero(argDef *ad,FILE *fp)
{
    if (ad->atype == enum_type)
        prcode(fp,"(%E)",ad->u.ed);

    prcode(fp,"0");
}


/*
 * Generate the return statement for a virtual handler when there has been an
 * error (ie. there is nothing sensible to return).
 */
static void generateVirtHandlerErrorReturn(argDef *res,FILE *fp)
{
    prcode(fp,
"        return");

    if (res == NULL)
    {
        prcode(fp,";\n"
            );

        return;
    }

    prcode(fp," ");

    if (res->atype == mapped_type && res->nrderefs == 0)
    {
        argDef res_noconstref;

        /*
         * We don't know anything about the mapped type so we just hope
         * is has a default ctor.
         */

        if (isReference(res))
            prcode(fp,"*new ");

        res_noconstref = *res;
        resetIsConstArg(&res_noconstref);
        resetIsReference(&res_noconstref);
        prcode(fp,"%B()",&res_noconstref);
    }
    else if (res->atype == class_type && res->nrderefs == 0)
    {
        ctorDef *ct = res->u.cd->defctor;

        /*
         * If we don't have a suitable ctor then the generated code
         * will issue an error message.
         */
        if (ct != NULL && isPublicCtor(ct) && ct->cppsig != NULL)
        {
            argDef res_noconstref;

            /*
             * If this is a badly designed class.  We can only
             * generate correct code by leaking memory.
             */
            if (isReference(res))
                prcode(fp,"*new ");

            res_noconstref = *res;
            resetIsConstArg(&res_noconstref);
            resetIsReference(&res_noconstref);
            prcode(fp,"%B",&res_noconstref);

            generateCallDefaultCtor(ct,fp);
        }
        else
        {
            fatalScopedName(classFQCName(res->u.cd));
            fatal(" must have a default constructor\n");
        }
    }
    else
        generateCastZero(res,fp);

    prcode(fp,";\n"
        );
}


/*
 * Generate the call to a default ctor.
 */
static void generateCallDefaultCtor(ctorDef *ct, FILE *fp)
{
    int a;

    prcode(fp, "(");

    for (a = 0; a < ct->cppsig->nrArgs; ++a)
    {
        argDef *ad = &ct->cppsig->args[a];

        if (ad->defval != NULL)
            break;

        if (a > 0)
            prcode(fp, ",");

        /*
         * Do what we can to provide type information to the compiler.
         */
        if (ad->atype == class_type && ad->nrderefs > 0 && !isReference(ad))
            prcode(fp, "static_cast<%B>(0)", ad);
        else if (ad->atype == enum_type)
            prcode(fp, "static_cast<%E>(0)", ad->u.ed);
        else if (ad->atype == float_type || ad->atype == cfloat_type)
            prcode(fp, "0.0F");
        else if (ad->atype == double_type || ad->atype == cdouble_type)
            prcode(fp, "0.0");
        else if (ad->atype == uint_type)
            prcode(fp, "0U");
        else if (ad->atype == long_type || ad->atype == longlong_type)
            prcode(fp, "0L");
        else if (ad->atype == ulong_type || ad->atype == ulonglong_type)
            prcode(fp, "0UL");
        else if ((ad->atype == ustring_type || ad->atype == sstring_type || ad->atype == string_type) && ad->nrderefs == 0)
            prcode(fp, "'\\0'");
        else if (ad->atype == wstring_type && ad->nrderefs == 0)
            prcode(fp, "L'\\0'");
        else
            prcode(fp, "0");
    }

    prcode(fp, ")");
}


/*
 * Generate the emitter function for a signal.
 */
static void generateEmitter(sipSpec *pt,classDef *cd,visibleList *vl,FILE *fp)
{
    char *pname = vl->m->pyname->text;
    overDef *od;

    prcode(fp,
"\n"
"int sip%C::sipEmit_%s(PyObject *sipArgs)\n"
"{\n"
"    int sipArgsParsed = 0;\n"
        ,classFQCName(cd),pname);

    for (od = vl->cd->overs; od != NULL; od = od->next)
    {
        int rgil = ((release_gil || isReleaseGIL(od)) && !isHoldGIL(od));

        if (od->common != vl->m || !isSignal(od))
            continue;

        /*
         * Generate the code that parses the args and emits the
         * appropriate overloaded signal.
         */
        prcode(fp,
"\n"
"    {\n"
            );

        generateArgParser(pt, &od->pysig, cd, NULL, NULL, FALSE, fp);

        prcode(fp,
"        {\n"
            );

        if (rgil)
            prcode(fp,
"            Py_BEGIN_ALLOW_THREADS\n"
                );

        prcode(fp,
"            emit %s("
            ,od->cppname);

        generateArgs(od->cppsig,Call,fp);

        prcode(fp,");\n"
            );

        if (rgil)
            prcode(fp,
"            Py_END_ALLOW_THREADS\n"
                );

        deleteTemps(&od->pysig, fp);

        prcode(fp,
"\n"
"            return 0;\n"
"        }\n"
"    }\n"
            );
    }

    prcode(fp,
"\n"
"    sipNoMethod(sipArgsParsed,%N,%N);\n"
"\n"
"    return -1;\n"
"}\n"
"\n"
        , cd->iff->name, vl->m->pyname);

    if (!generating_c)
        prcode(fp,
"extern \"C\" {static int %C_emit_%s(sipWrapper *, PyObject *);}\n"
            , classFQCName(cd), pname);

    prcode(fp,
"static int %C_emit_%s(sipWrapper *w,PyObject *sipArgs)\n"
"{\n"
"    sip%C *ptr = reinterpret_cast<sip%C *>(sipGetComplexCppPtr(w));\n"
"\n"
"    return (ptr ? ptr->sipEmit_%s(sipArgs) : -1);\n"
"}\n"
        ,classFQCName(cd),pname
        ,classFQCName(cd),classFQCName(cd)
        ,pname);
}


/*
 * Generate the declarations of the protected wrapper functions for a class.
 */

static void generateProtectedDeclarations(classDef *cd,FILE *fp)
{
    int noIntro;
    visibleList *vl;

    noIntro = TRUE;

    for (vl = cd->visible; vl != NULL; vl = vl->next)
    {
        overDef *od;

        if (vl->m->slot != no_slot)
            continue;

        for (od = vl->cd->overs; od != NULL; od = od->next)
        {
            if (od->common != vl->m || !isProtected(od))
                continue;

            if (noIntro)
            {
                prcode(fp,
"\n"
"    /*\n"
"     * There is a public method for every protected method visible from\n"
"     * this class.\n"
"     */\n"
                    );

                noIntro = FALSE;
            }

            prcode(fp,
"    ");

            if (isStatic(od))
                prcode(fp,"static ");

            generateBaseType(&od->cppsig->result,fp);

            if (!isStatic(od) && !isAbstract(od) && (isVirtual(od) || isVirtualReimp(od)))
            {
                prcode(fp, " sipProtectVirt_%s(bool", od->cppname);

                if (od->cppsig->nrArgs > 0)
                    prcode(fp, ",");
            }
            else
                prcode(fp, " sipProtect_%s(", od->cppname);

            generateArgs(od->cppsig,Declaration,fp);
            prcode(fp,")%s;\n"
                ,(isConst(od) ? " const" : ""));
        }
    }
}


/*
 * Generate the definitions of the protected wrapper functions for a class.
 */
static void generateProtectedDefinitions(classDef *cd,FILE *fp)
{
    visibleList *vl;

    for (vl = cd->visible; vl != NULL; vl = vl->next)
    {
        overDef *od;

        if (vl->m->slot != no_slot)
            continue;

        for (od = vl->cd->overs; od != NULL; od = od->next)
        {
            char *mname = od->cppname;
            int parens;
            argDef *res;

            if (od->common != vl->m || !isProtected(od))
                continue;

            prcode(fp,
"\n"
                );

            generateBaseType(&od->cppsig->result,fp);

            if (!isStatic(od) && !isAbstract(od) && (isVirtual(od) || isVirtualReimp(od)))
            {
                prcode(fp, " sip%C::sipProtectVirt_%s(bool sipSelfWasArg", classFQCName(cd), mname);

                if (od->cppsig->nrArgs > 0)
                    prcode(fp, ",");
            }
            else
                prcode(fp, " sip%C::sipProtect_%s(", classFQCName(cd), mname);

            generateArgs(od->cppsig,Definition,fp);
            prcode(fp,")%s\n"
"{\n"
                ,(isConst(od) ? " const" : ""));

            parens = 1;

            res = &od->cppsig->result;

            if (res->atype == void_type && res->nrderefs == 0)
                prcode(fp,
"    ");
            else
            {
                prcode(fp,
"    return ");

                if (res->atype == class_type && isProtectedClass(res->u.cd))
                {
                    prcode(fp,"static_cast<%U *>(",res->u.cd);
                    ++parens;
                }
                else if (res->atype == enum_type && isProtectedEnum(res->u.ed))
                    /*
                     * One or two older compilers can't
                     * handle a static_cast here so we
                     * revert to a C-style cast.
                     */
                    prcode(fp,"(%E)",res->u.ed);
            }

            if (!isAbstract(od))
                if (isVirtual(od) || isVirtualReimp(od))
                {
                    prcode(fp, "(sipSelfWasArg ? %S::%s(", classFQCName(vl->cd), mname);

                    generateProtectedCallArgs(od, fp);

                    prcode(fp, ") : ");
                    ++parens;
                }
                else
                    prcode(fp, "%S::", classFQCName(vl->cd));

            prcode(fp,"%s(",mname);

            generateProtectedCallArgs(od, fp);

            while (parens--)
                prcode(fp,")");

            prcode(fp,";\n"
"}\n"
                );
        }
    }
}


/*
 * Generate the arguments for a call to a protected method.
 */
static void generateProtectedCallArgs(overDef *od, FILE *fp)
{
    int a;

    for (a = 0; a < od->cppsig->nrArgs; ++a)
    {
        argDef *ad = &od->cppsig->args[a];

        if (a > 0)
            prcode(fp, ",");

        if (ad->atype == enum_type && isProtectedEnum(ad->u.ed))
            prcode(fp, "(%S)", ad->u.ed->fqcname);

        prcode(fp, "a%d", a);
    }
}


/*
 * Generate the function that does most of the work to handle a particular
 * virtual function.
 */
static void generateVirtualHandler(sipSpec *pt,virtHandlerDef *vhd,FILE *fp)
{
    int a, nrvals, copy, isref;
    argDef *res, res_noconstref;

    res = &vhd->cppsig->result;

    copy = isref = FALSE;

    if (res->atype == void_type && res->nrderefs == 0)
        res = NULL;
    else
    {
        /*
         * If we are returning a reference to an instance then we take care to
         * handle Python errors but still return a valid C++ instance.  If we
         * are returning an instance then we take care to make a local copy of
         * the instance returned from Python before the Python object is
         * garbage collected and the C++ instance (possibly) destroyed.
         */
        if ((res->atype == class_type || res->atype == mapped_type) && res->nrderefs == 0)
            if (isReference(res))
                isref = TRUE;
            else
                copy = TRUE;

        res_noconstref = *res;
        resetIsConstArg(&res_noconstref);
        resetIsReference(&res_noconstref);
    }

    prcode(fp,
"\n"
        );

    generateBaseType(&vhd->cppsig->result, fp);

    prcode(fp," sipVH_%s_%d(sip_gilstate_t sipGILState,PyObject *sipMethod"
        ,pt->module->name,vhd->virthandlernr);

    if (vhd->cppsig->nrArgs > 0)
    {
        prcode(fp,",");

        generateArgs(vhd->cppsig, Definition, fp);
    }

    prcode(fp,")\n"
"{\n"
        );

    if (res != NULL)
    {
        prcode(fp, "    ");

        /*
         * wchar_t * return values are always on the heap.  To reduce memory
         * leaks we keep the last result around until we have a new one.  This
         * means that ownership of the return value stays with the function
         * returning it - which is consistent with how other types work, even
         * thought it may not be what's required in all cases.
         */
        if (res->atype == wstring_type && res->nrderefs == 1)
            prcode(fp, "static ");

        generateBaseType(&res_noconstref,fp);

        prcode(fp," %ssipRes",(isref ? "*" : ""));

        if (copy && res->atype == class_type && res->nrderefs == 0)
        {
            ctorDef *ct = res->u.cd->defctor;

            if (ct != NULL && isPublicCtor(ct) && ct->cppsig != NULL && ct->cppsig->nrArgs > 0 && ct->cppsig->args[0].defval == NULL)
                generateCallDefaultCtor(ct,fp);
        }
        else if (!copy)
        {
            /*
             * We initialise the result to try and suppress a
             * compiler warning.
             */
            prcode(fp," = ");
            generateCastZero(res,fp);
        }

        prcode(fp,";\n"
            );

        if (res->atype == wstring_type && res->nrderefs == 1)
            prcode(fp,
"\n"
"    if (sipRes)\n"
"    {\n"
"        // Return any previous result to the heap.\n"
"        sipFree(%s);\n"
"        sipRes = 0;\n"
"    }\n"
"\n"
                , (isConstArg(res) ? "const_cast<wchar_t *>(sipRes)" : "sipRes"));
    }

    if (vhd->virtcode != NULL)
    {
        int error_flag = needErrorFlag(vhd->virtcode);

        if (error_flag)
            prcode(fp,
"    int sipIsErr = 0;\n"
                );

        prcode(fp,
"\n"
            );

        generateCppCodeBlock(vhd->virtcode,fp);

        if (error_flag)
            prcode(fp,
"\n"
"    if (sipIsErr)\n"
"        PyErr_Print();\n"
                );

        prcode(fp,
"\n"
"    Py_DECREF(sipMethod);\n"
"\n"
"    SIP_RELEASE_GIL(sipGILState)\n"
            );

        if (res != NULL)
            prcode(fp,
"\n"
"    return sipRes;\n"
                );

        prcode(fp,
"}\n"
            );

        return;
    }

    /* See how many values we expect. */
    nrvals = (res != NULL ? 1 : 0);

    for (a = 0; a < vhd->pysig->nrArgs; ++a)
        if (isOutArg(&vhd->pysig->args[a]))
            ++nrvals;

    if (copy)
    {
        prcode(fp,
"    ");

        generateBaseType(&res_noconstref,fp);

        prcode(fp," *sipResOrig;\n");

        if (res->atype == class_type && res->u.cd->convtocode != NULL)
            prcode(fp,
"    int sipResState;\n"
                );
    }

    /* Call the method. */
    prcode(fp,
"    PyObject *sipResObj = sipCallMethod(0,sipMethod,");

    generateTupleBuilder(vhd->pysig, fp);

    prcode(fp,");\n"
"\n"
"    %s (!sipResObj || sipParseResult(0,sipMethod,sipResObj,\"",(isref ? "int sipIsErr =" : "if"));

    /* Build the format string. */
    if (nrvals == 0)
        prcode(fp,"Z");
    else
    {
        if (nrvals > 1)
            prcode(fp,"(");

        if (res != NULL)
            prcode(fp, "%s", getParseResultFormat(res, TRUE, isTransferVH(vhd)));

        for (a = 0; a < vhd->pysig->nrArgs; ++a)
        {
            argDef *ad = &vhd->pysig->args[a];

            if (isOutArg(ad))
                prcode(fp, "%s", getParseResultFormat(ad, FALSE, FALSE));
        }

        if (nrvals > 1)
            prcode(fp,")");
    }

    prcode(fp,"\"");

    /* Pass the destination pointers. */
    if (res != NULL)
    {
        generateParseResultExtraArgs(res, TRUE, fp);
        prcode(fp,",&sipRes%s",(copy ? "Orig" : ""));
    }

    for (a = 0; a < vhd->pysig->nrArgs; ++a)
    {
        argDef *ad = &vhd->pysig->args[a];

        if (isOutArg(ad))
        {
            generateParseResultExtraArgs(ad, FALSE, fp);
            prcode(fp,",%sa%d",(isReference(ad) ? "&" : ""),a);
        }
    }

    if (isref)
        prcode(fp,") < 0);\n"
"\n"
"    if (sipIsErr)\n"
            );
    else
        prcode(fp,") < 0)\n"
            );

    prcode(fp,
"        PyErr_Print();\n"
        );

    /* Make a copy if needed. */
    if (copy)
    {
        prcode(fp,
"    else\n"
"    {\n"
"        sipRes = *sipResOrig;\n"
            );

        if (res->atype == mapped_type)
            prcode(fp,
"        delete sipResOrig;\n"
                );
        else if (res->atype == class_type && res->u.cd->convtocode != NULL)
            prcode(fp,
"        sipReleaseInstance(sipResOrig,sipClass_%C,sipResState);\n"
                , classFQCName(res->u.cd));

        prcode(fp,
"    }\n"
            );
    }

    prcode(fp,
"\n"
"    Py_XDECREF(sipResObj);\n"
"    Py_DECREF(sipMethod);\n"
"\n"
"    SIP_RELEASE_GIL(sipGILState)\n"
        );

    if (res != NULL)
    {
        if (isref)
        {
            prcode(fp,
"\n"
"    if (sipIsErr)\n"
                );

            generateVirtHandlerErrorReturn(res,fp);
        }

        prcode(fp,
"\n"
"    return %ssipRes;\n"
            ,(isref ? "*" : ""));
    }

    prcode(fp,
"}\n"
        );
}


/*
 * Generate the extra arguments needed by sipParseResult() for a particular
 * type.
 */
static void generateParseResultExtraArgs(argDef *ad, int isres, FILE *fp)
{
    switch (ad->atype)
    {
    case mapped_type:
        prcode(fp, ",sipMappedType_%T", ad);
        break;

    case class_type:
        prcode(fp, ",sipClass_%C", classFQCName(ad->u.cd));

        if (isres && ad->nrderefs == 0 && ad->u.cd->convtocode != NULL && !isReference(ad))
            prcode(fp, ",&sipResState");

        break;

    case pytuple_type:
        prcode(fp,",&PyTuple_Type");
        break;

    case pylist_type:
        prcode(fp,",&PyList_Type");
        break;

    case pydict_type:
        prcode(fp,",&PyDict_Type");
        break;

    case pyslice_type:
        prcode(fp,",&PySlice_Type");
        break;

    case pytype_type:
        prcode(fp,",&PyType_Type");
        break;

    case enum_type:
        if (ad->u.ed->fqcname != NULL)
            prcode(fp,",sipEnum_%C",ad->u.ed->fqcname);
        break;
    }
}


/*
 * Return the format characters used by sipParseResult() for a particular type.
 */
static const char *getParseResultFormat(argDef *ad, int isres, int xfervh)
{
    switch (ad->atype)
    {
    case mapped_type:
        {
            static const char *s[] = {
                "D0", "D1", "D2", "D3",
                "D4", "D5", "D6", "D7"
            };

            int f = 0x04;

            if (isres && ad->nrderefs == 0)
                f |= 0x01;

            if (isres && xfervh)
                f |= 0x02;

            return s[f];
        }

    case class_type:
        {
            static char s[] = "C?";

            int f = 0x04;

            if (isres && ad->nrderefs == 0)
            {
                f |= 0x01;

                if (ad->u.cd->convtocode != NULL)
                {
                    f &= ~0x04;

                    /*
                     * If it is a reference then we are
                     * going to return the dereference.  To
                     * make sure it remains valid we can
                     * either leak the temporary from the
                     * %ConvertToCode or we can suppress
                     * the %ConvertToCode.  We choose the
                     * latter.
                     */
                    if (isReference(ad))
                        f |= 0x10;
                }
            }

            if (isres && xfervh)
                f |= 0x02;

            s[1] = '0' + f;

            return s;
        }

    case bool_type:
    case cbool_type:
        return "b";

    case sstring_type:
    case ustring_type:
    case string_type:
        return ((ad->nrderefs == 0) ? "c" : "s");

    case wstring_type:
        return ((ad->nrderefs == 0) ? "w" : "x");

    case enum_type:
        return ((ad->u.ed->fqcname != NULL) ? "E" : "e");

    case ushort_type:
        return "t";

    case short_type:
        return "h";

    case int_type:
    case cint_type:
        return "i";

    case uint_type:
        return "u";

    case long_type:
        return "l";

    case ulong_type:
        return "m";

    case longlong_type:
        return "n";

    case ulonglong_type:
        return "o";

    case void_type:
    case struct_type:
        return "V";

    case float_type:
    case cfloat_type:
        return "f";

    case double_type:
    case cdouble_type:
        return "d";

    case pyobject_type:
        return "O";

    case pytuple_type:
    case pylist_type:
    case pydict_type:
    case pyslice_type:
    case pytype_type:
        return (isAllowNone(ad) ? "N" : "T");
    }

    /* We should never get here. */
    return " ";
}


/*
 * Generate the code to build a tuple of Python arguments.
 */
static void generateTupleBuilder(signatureDef *sd,FILE *fp)
{
    int a, arraylenarg;

    prcode(fp,"\"");

    for (a = 0; a < sd->nrArgs; ++a)
    {
        char *fmt = "";
        argDef *ad = &sd->args[a];

        if (!isInArg(ad))
            continue;

        switch (ad->atype)
        {
        case sstring_type:
        case ustring_type:
        case string_type:
            if (ad->nrderefs == 0 || (ad->nrderefs == 1 && isOutArg(ad)))
                fmt = "c";
            else if (isArray(ad))
                fmt = "a";
            else
                fmt = "s";

            break;

        case wstring_type:
            if (ad->nrderefs == 0 || (ad->nrderefs == 1 && isOutArg(ad)))
                fmt = "w";
            else if (isArray(ad))
                fmt = "A";
            else
                fmt = "x";

            break;

        case bool_type:
        case cbool_type:
            fmt = "b";
            break;

        case enum_type:
            fmt = (ad->u.ed->fqcname != NULL) ? "E" : "e";
            break;

        case cint_type:
            fmt = "i";
            break;

        case uint_type:
            if (isArraySize(ad))
                arraylenarg = a;
            else
                fmt = "u";

            break;

        case int_type:
            if (isArraySize(ad))
                arraylenarg = a;
            else
                fmt = "i";

            break;

        case ushort_type:
            if (isArraySize(ad))
                arraylenarg = a;
            else
                fmt = "t";

            break;

        case short_type:
            if (isArraySize(ad))
                arraylenarg = a;
            else
                fmt = "h";

            break;

        case long_type:
            if (isArraySize(ad))
                arraylenarg = a;
            else
                fmt = "l";

            break;

        case ulong_type:
            if (isArraySize(ad))
                arraylenarg = a;
            else
                fmt = "m";

            break;

        case longlong_type:
            if (isArraySize(ad))
                arraylenarg = a;
            else
                fmt = "n";

            break;

        case ulonglong_type:
            if (isArraySize(ad))
                arraylenarg = a;
            else
                fmt = "o";

            break;

        case struct_type:
        case void_type:
            fmt = "V";
            break;

        case float_type:
        case cfloat_type:
            fmt = "f";
            break;

        case double_type:
        case cdouble_type:
            fmt = "d";
            break;

        case signal_type:
        case slot_type:
        case slotcon_type:
        case slotdis_type:
            fmt = "s";
            break;

        case mapped_type:
            fmt = "D";
            break;

        case class_type:
            fmt = "C";
            break;

        case rxcon_type:
        case rxdis_type:
        case qobject_type:
            fmt = "O";
            break;

        case pyobject_type:
        case pytuple_type:
        case pylist_type:
        case pydict_type:
        case pycallable_type:
        case pyslice_type:
        case pytype_type:
            fmt = "S";
            break;
        }

        prcode(fp,fmt);
    }

    prcode(fp,"\"");

    for (a = 0; a < sd->nrArgs; ++a)
    {
        int derefs;
        argDef *ad = &sd->args[a];

        if (!isInArg(ad))
            continue;

        derefs = ad->nrderefs;

        switch (ad->atype)
        {
        case sstring_type:
        case ustring_type:
        case string_type:
        case wstring_type:
            if (!(ad->nrderefs == 0 || (ad->nrderefs == 1 && isOutArg(ad))))
                --derefs;

            break;

        case mapped_type:
        case class_type:
            if (ad->nrderefs > 0)
                --derefs;

            break;

        case struct_type:
        case void_type:
            --derefs;
            break;
        }

        if (ad->atype == mapped_type || ad->atype == class_type ||
            ad->atype == rxcon_type || ad->atype == rxdis_type ||
            ad->atype == qobject_type)
        {
            prcode(fp,",");

            if (isConstArg(ad))
                prcode(fp,"const_cast<%b *>(",ad);

            if (ad->nrderefs == 0)
                prcode(fp,"&");
            else
                while (derefs-- != 0)
                    prcode(fp,"*");

            prcode(fp,"a%d",a);

            if (isConstArg(ad))
                prcode(fp,")");

            if (ad->atype == mapped_type)
                prcode(fp, ",sipMappedType_%T,NULL", ad);
            else if (ad->atype == class_type)
                prcode(fp, ",sipClass_%C,NULL", classFQCName(ad->u.cd));
            else
                prcode(fp,",sipClass_QObject");
        }
        else
        {
            if (!isArraySize(ad))
            {
                prcode(fp,",");

                while (derefs-- != 0)
                    prcode(fp,"*");

                prcode(fp,"a%d",a);
            }

            if (isArray(ad))
            {
                argType astype = sd->args[arraylenarg].atype;

                prcode(fp,",%sa%d",(astype == int_type ? "" : "(int)"),arraylenarg);
            }
            else if (ad->atype == enum_type && ad->u.ed->fqcname != NULL)
                prcode(fp,",sipEnum_%C",ad->u.ed->fqcname);
        }
    }
}


/*
 * Generate the class interface #include directives required by either a class
 * or a module.
 */
static void generateUsedIncludes(ifaceFileList *iffl, int header, FILE *fp)
{
    int newl = TRUE;

    while (iffl != NULL)
    {
        if (header == iffl->header)
        {
            if (newl)
            {
                prcode(fp,
"\n"
                    );

                newl = FALSE;
            }

            prcode(fp,
"#include \"sip%s%F.h\"\n"
                , iffl->iff->module->name, iffl->iff->fqcname);
        }

        iffl = iffl->next;
    }

    if (!newl)
        prcode(fp,
"\n"
            );
}


/*
 * Generate the header file for the C++ interface.
 */
static void generateIfaceHeader(sipSpec *pt,ifaceFileDef *iff,char *codeDir)
{
    char *wfile;
    char *cmname = iff->module->name;
    classDef *cd;
    mappedTypeDef *mtd;
    exceptionDef *xd;
    int genused;
    FILE *fp;

    /* Create the header file. */

    wfile = createIfaceFileName(codeDir,iff,".h");
    fp = createFile(pt,wfile,"Interface header file.");

    prcode(fp,
"\n"
"#ifndef _%s%F_h\n"
"#define _%s%F_h\n"
"\n"
        ,cmname,iff->fqcname,cmname,iff->fqcname);

    genused = TRUE;

    for (cd = pt->classes; cd != NULL; cd = cd->next)
        if (cd->iff == iff)
        {
            if (iff->module == pt->module)
                generateClassHeader(cd,genused,pt,fp);
            else if (!isExternal(cd))
                generateImportedClassHeader(cd,pt,fp);

            genused = FALSE;
        }

    genused = TRUE;

    for (mtd = pt->mappedtypes; mtd != NULL; mtd = mtd->next)
        if (mtd->iff == iff)
        {
            if (iff->module == pt->module)
                generateMappedTypeHeader(mtd,genused,fp);
            else
                generateImportedMappedTypeHeader(mtd,pt,fp);

            genused = FALSE;
        }

    for (xd = pt->exceptions; xd != NULL; xd = xd->next)
        if (xd->iff == iff)
        {
            generateCppCodeBlock(xd->hdrcode,fp);

            if (xd->exceptionnr >= 0)
            {
                prcode(fp,
"\n"
"#define sipException_%C sipModuleAPI_%s"
                    ,iff->fqcname,pt->module->name);

                if (iff->module == pt->module)
                    prcode(fp,".");
                else
                    prcode(fp,"_%s->",iff->module->name);

                prcode(fp,"em_exceptions[%d]\n"
                    ,xd->exceptionnr);
            }
        }

    prcode(fp,
"\n"
"#endif\n"
        );

    closeFile(fp);
    free(wfile);
}


/*
 * Generate the C++ header code for an imported mapped type.
 */
static void generateImportedMappedTypeHeader(mappedTypeDef *mtd,sipSpec *pt,
                         FILE *fp)
{
    char *mname = pt->module->name;
    char *imname = mtd->iff->module->name;
    argDef type;

    generateCppCodeBlock(mtd->hdrcode,fp);

    type.atype = mapped_type;
    type.u.mtd = mtd;
    type.argflags = 0;
    type.name = NULL;
    type.nrderefs = 0;
    type.defval = NULL;

    prcode(fp,
"\n"
"#define sipMappedType_%T        sipModuleAPI_%s_%s->em_mappedtypes[%d]\n"
"#define sipForceConvertTo_%T    sipModuleAPI_%s_%s->em_mappedtypes[%d]->mt_fcto\n"
"#define sipConvertFrom_%T       sipModuleAPI_%s_%s->em_mappedtypes[%d]->mt_cfrom\n"
        ,&type,mname,imname,mtd->mappednr
        ,&type,mname,imname,mtd->mappednr
        ,&type,mname,imname,mtd->mappednr);
}


/*
 * Generate the C++ header code for a generated mapped type.
 */
static void generateMappedTypeHeader(mappedTypeDef *mtd,int genused,FILE *fp)
{
    prcode(fp,
"\n"
"\n"
        );

    generateCppCodeBlock(mtd->hdrcode,fp);

    if (genused)
        generateUsedIncludes(mtd->iff->used, TRUE, fp);

    prcode(fp,
"\n"
"#define sipMappedType_%T        &sipMappedTypeDef_%T\n"
"#define sipForceConvertTo_%T    sipMappedTypeDef_%T.mt_fcto\n"
"#define sipConvertFrom_%T       sipMappedTypeDef_%T.mt_cfrom\n"
"\n"
"extern sipMappedType sipMappedTypeDef_%T;\n"
        ,&mtd->type,&mtd->type
        ,&mtd->type,&mtd->type
        ,&mtd->type,&mtd->type
        ,&mtd->type);
}


/*
 * Generate the C++ header code for an imported class.
 */
static void generateImportedClassHeader(classDef *cd,sipSpec *pt,FILE *fp)
{
    char *mname = pt->module->name;
    char *imname = cd->iff->module->name;
    classDef *hcd;

    for (hcd = cd; hcd != NULL; hcd = hcd->ecd)
        generateCppCodeBlock(hcd->hdrcode,fp);

    prcode(fp,
"\n"
"#define sipClass_%C             sipModuleAPI_%s_%s->em_types[%d]\n"
"#define sipCast_%C              sipModuleAPI_%s_%s->em_types[%d]->type->td_cast\n"
"#define sipForceConvertTo_%C    sipModuleAPI_%s_%s->em_types[%d]->type->td_fcto\n"
        ,classFQCName(cd),mname,imname,cd->classnr
        ,classFQCName(cd),mname,imname,cd->classnr
        ,classFQCName(cd),mname,imname,cd->classnr);

    generateEnumMacros(pt, cd, fp);
}


/*
 * Generate the C++ header code for a generated class.
 */
static void generateClassHeader(classDef *cd,int genused,sipSpec *pt,FILE *fp)
{
    char *mname = pt->module->name;
    classDef *hcd;

    for (hcd = cd; hcd != NULL; hcd = hcd->ecd)
        generateCppCodeBlock(hcd->hdrcode,fp);

    if (genused)
        generateUsedIncludes(cd->iff->used, TRUE, fp);

    if (cd->iff->type != namespace_iface)
    {
        prcode(fp,
"\n"
"#define sipClass_%C             sipModuleAPI_%s.em_types[%d]\n"
            ,classFQCName(cd),mname,cd->classnr);

        if (!isExternal(cd))
            prcode(fp,
"#define sipCast_%C              sipType_%s_%C.td_cast\n"
"#define sipForceConvertTo_%C    sipType_%s_%C.td_fcto\n"
                , classFQCName(cd), mname, classFQCName(cd)
                , classFQCName(cd), mname, classFQCName(cd));
    }

    generateEnumMacros(pt, cd, fp);

    if (!isExternal(cd))
        prcode(fp,
"\n"
"extern sipTypeDef sipType_%s_%C;\n"
            , mname, classFQCName(cd));

    if (hasShadow(cd))
        generateShadowClassDeclaration(pt,cd,fp);
}


/*
 * Generate the sipEnum_* macros.
 */
static void generateEnumMacros(sipSpec *pt, classDef *cd, FILE *fp)
{
    enumDef *ed;
    int noIntro = TRUE;

    for (ed = pt->enums; ed != NULL; ed = ed->next)
    {
        if (ed->fqcname == NULL || ed->ecd != cd)
            continue;

        if (noIntro)
        {
            prcode(fp,
"\n"
                );

            noIntro = FALSE;
        }

        prcode(fp,
"#define sipEnum_%C              sipModuleAPI_%s", ed->fqcname, pt->module->name);

        if (pt->module == ed->module)
            prcode(fp, ".");
        else
            prcode(fp, "_%s->", ed->module->name);

        prcode(fp, "em_enums[%d]\n"
            , ed->enumnr);
    }
}


/*
 * Generate the shadow class declaration.
 */
static void generateShadowClassDeclaration(sipSpec *pt,classDef *cd,FILE *fp)
{
    int noIntro, nrVirts;
    ctorDef *ct;
    virtOverDef *vod;
    classDef *pcd;

    prcode(fp,
"\n"
"\n"
"class sip%C : public %S\n"
"{\n"
"public:\n"
        ,classFQCName(cd),classFQCName(cd));

    /* Define a shadow class for any protected classes we have. */

    for (pcd = pt->classes; pcd != NULL; pcd = pcd->next)
    {
        if (pcd->ecd != cd || !isProtectedClass(pcd))
            continue;

        prcode(fp,
"    class sip%s : public %s {};\n"
            ,classBaseName(pcd),classBaseName(pcd));
    }

    /* The constructor declarations. */

    for (ct = cd->ctors; ct != NULL; ct = ct->next)
    {
        ctorDef *dct;

        if (isPrivateCtor(ct))
            continue;

        if (ct->cppsig == NULL)
            continue;

        /* Check we haven't already handled this C++ signature. */
        for (dct = cd->ctors; dct != ct; dct = dct->next)
            if (dct->cppsig != NULL && sameSignature(dct->cppsig, ct->cppsig, TRUE))
                break;

        if (dct != ct)
            continue;

        prcode(fp,
"    sip%C(",classFQCName(cd));

        generateArgs(ct->cppsig,Declaration,fp);

        prcode(fp,")%X;\n"
            ,ct->exceptions);
    }

    /* The destructor. */

    if (!isPrivateDtor(cd))
        prcode(fp,
"    %s~sip%C()%X;\n"
            ,(cd->vmembers != NULL ? "virtual " : ""),classFQCName(cd),cd->dtorexceptions);

    /* The metacall methods if required. */
    if (isQObjectSubClass(cd) && optQ_OBJECT4(pt))
        prcode(fp,
"\n"
"    const QMetaObject *metaObject() const;\n"
"    int qt_metacall(QMetaObject::Call,int,void **);\n"
            );

    /* The exposure of protected enums. */

    generateProtectedEnums(pt,cd,fp);

    /* The wrapper around each protected member function. */

    generateProtectedDeclarations(cd,fp);

    /* The public wrapper around each signal emitter. */
    if (!optNoEmitters(pt))
    {
        visibleList *vl;

        noIntro = TRUE;

        for (vl = cd->visible; vl != NULL; vl = vl->next)
        {
            overDef *od;

            if (vl->m->slot != no_slot)
                continue;

            for (od = vl->cd->overs; od != NULL; od = od->next)
            {
                if (od->common != vl->m || !isSignal(od))
                    continue;

                if (noIntro)
                {
                    prcode(fp,
"\n"
"    /*\n"
"     * There is a public method for every Qt signal that can be emitted\n"
"     * by this object.  This function is called by Python to emit the\n"
"     * signal.\n"
"     */\n"
                        );

                    noIntro = FALSE;
                }

                prcode(fp,
"    int sipEmit_%s(PyObject *);\n"
                    ,vl->m->pyname->text);

                break;
            }
        }
    }

    /* The catcher around each virtual function in the hierarchy. */
    noIntro = TRUE;

    for (vod = cd->vmembers; vod != NULL; vod = vod->next)
    {
        overDef *od = &vod->o;
        virtOverDef *dvod;

        if (isPrivate(od))
            continue;

        /* Check we haven't already handled this C++ signature. */
        for (dvod = cd->vmembers; dvod != vod; dvod = dvod->next)
            if (strcmp(dvod->o.cppname,od->cppname) == 0 && sameSignature(dvod->o.cppsig,od->cppsig,TRUE))
                break;

        if (dvod != vod)
            continue;

        if (noIntro)
        {
            prcode(fp,
"\n"
"    /*\n"
"     * There is a protected method for every virtual method visible from\n"
"     * this class.\n"
"     */\n"
"protected:\n"
                );

            noIntro = FALSE;
        }

        prcode(fp,
"    ");
 
        prOverloadDecl(fp, od, FALSE);
        prcode(fp, ";\n");
    }

    prcode(fp,
"\n"
"public:\n"
"    sipWrapper *sipPySelf;\n"
        );

    /* The private declarations. */

    prcode(fp,
"\n"
"private:\n"
"    sip%C(const sip%C &);\n"
"    sip%C &operator = (const sip%C &);\n"
        ,classFQCName(cd),classFQCName(cd)
        ,classFQCName(cd),classFQCName(cd));

    if ((nrVirts = countVirtuals(cd)) > 0)
        prcode(fp,
"\n"
"    sipMethodCache sipPyMethods[%d];\n"
            ,nrVirts);

    prcode(fp,
"};\n"
        );
}


/*
 * Generate the C++ declaration for an overload.
 */
void prOverloadDecl(FILE *fp, overDef *od, int defval)
{
    int a;

    normaliseArgs(od->cppsig);

    generateBaseType(&od->cppsig->result, fp);
 
    prcode(fp, " %O(", od);

    for (a = 0; a < od->cppsig->nrArgs; ++a)
    {
        argDef *ad = &od->cppsig->args[a];

        if (a > 0)
            prcode(fp, ",");

        generateBaseType(ad, fp);

        if (defval && ad->defval != NULL)
        {
            prcode(fp, " = ");
            generateExpression(ad->defval, fp);
        }
    }
 
    prcode(fp, ")%s%X", (isConst(od) ? " const" : ""), od->exceptions);

    restoreArgs(od->cppsig);
}


/*
 * Generate typed arguments.
 */
static void generateArgs(signatureDef *sd,funcArgType ftype,FILE *fp)
{
    int a;

    for (a = 0; a < sd->nrArgs; ++a)
    {
        if (a > 0)
            prcode(fp,",");

        generateSingleArg(&sd->args[a],a,ftype,fp);
    }
}


/*
 * Generate the declaration of a named variable to hold a result from a C++
 * function call.
 */
static void generateNamedValueType(argDef *ad,char *name,FILE *fp)
{
    argDef mod = *ad;

    if (ad->nrderefs == 0)
        if (ad->atype == class_type || ad->atype == mapped_type)
            mod.nrderefs = 1;
        else
            resetIsConstArg(&mod);

    resetIsReference(&mod);
    generateNamedBaseType(&mod,name,fp);
}


/*
 * Generate a single argument.
 */
static void generateSingleArg(argDef *ad,int argnr,funcArgType ftype,FILE *fp)
{
    char name[50];
    int genType, genName, derefPtr;

    /* Break the type down to individual modifications. */

    genType = FALSE;
    genName = FALSE;
    derefPtr = FALSE;

    switch (ftype)
    {
    case Call:
        genName = TRUE;
        derefPtr = TRUE;
        break;

    case Declaration:
        genType = TRUE;
        break;

    case Definition:
        genType = TRUE;
        genName = TRUE;
        break;
    }

    if (genName)
    {
        char *ind = "";

        if (derefPtr)
            switch (ad->atype)
            {
            case sstring_type:
            case ustring_type:
            case string_type:
            case wstring_type:
                if (ad->nrderefs > (isOutArg(ad) ? 0 : 1))
                    ind = "&";

                break;

            case mapped_type:
            case class_type:
                if (ad->nrderefs == 2)
                    ind = "&";
                else if (ad->nrderefs == 0)
                    ind = "*";

                break;

            case struct_type:
            case void_type:
                if (ad->nrderefs == 2)
                    ind = "&";

                break;

            default:
                if (ad->nrderefs == 1)
                    ind = "&";
            }

        sprintf(name,"%sa%d",ind,argnr);
    }
    else
        name[0] = '\0';

    if (genType)
        generateNamedBaseType(ad,name,fp);
    else if (genName)
        prcode(fp,name);
}


/*
 * Generate a C++ type.
 */
static void generateBaseType(argDef *ad,FILE *fp)
{
    generateNamedBaseType(ad,"",fp);
}


/*
 * Generate a C++ type and name.
 */
static void generateNamedBaseType(argDef *ad,char *name,FILE *fp)
{
    int nr_derefs = ad->nrderefs;

    /*
     * A function type is handled differently because of the position of
     * the name.
     */
    if (ad->atype == function_type)
    {
        int i;
        signatureDef *sig = ad->u.sa;

        generateBaseType(&sig->result,fp);

        prcode(fp," (");

        for (i = 0; i < nr_derefs; ++i)
            prcode(fp,"*");

        prcode(fp,"%s)(",name);
        generateArgs(sig,Declaration,fp);
        prcode(fp,")");

        return;
    }

    if (isConstArg(ad))
        prcode(fp,"const ");

    switch (ad->atype)
    {
    case sstring_type:
        prcode(fp,"signed char");
        break;

    case ustring_type:
        prcode(fp,"unsigned char");
        break;

    case wstring_type:
        prcode(fp,"wchar_t");
        break;

    case signal_type:
    case slot_type:
    case anyslot_type:
    case slotcon_type:
    case slotdis_type:
        nr_derefs = 1;

        /* Drop through. */

    case string_type:
        prcode(fp,"char");
        break;

    case ushort_type:
        prcode(fp,"unsigned short");
        break;

    case short_type:
        prcode(fp,"short");
        break;

    case uint_type:
        prcode(fp,"unsigned");
        break;

    case int_type:
    case cint_type:
        prcode(fp,"int");
        break;

    case ulong_type:
        prcode(fp,"unsigned long");
        break;

    case long_type:
        prcode(fp,"long");
        break;

    case ulonglong_type:
        prcode(fp,"unsigned PY_LONG_LONG");
        break;

    case longlong_type:
        prcode(fp,"PY_LONG_LONG");
        break;

    case struct_type:
        prcode(fp,"struct %S",ad->u.sname);
        break;

    case void_type:
        prcode(fp,"void");
        break;

    case bool_type:
    case cbool_type:
        prcode(fp,"bool");
        break;

    case float_type:
    case cfloat_type:
        prcode(fp,"float");
        break;

    case double_type:
    case cdouble_type:
        prcode(fp,"double");
        break;

    case defined_type:
        /*
         * The only defined types still remaining are arguments to
         * templates.
         */

        prcode(fp,"%S",ad->u.snd);
        break;

    case rxcon_type:
    case rxdis_type:
        nr_derefs = 1;
        prcode(fp,"QObject");
        break;

    case mapped_type:
        generateBaseType(&ad->u.mtd->type,fp);
        break;

    case class_type:
        prcode(fp,"%U",ad->u.cd);
        break;

    case template_type:
        {
            static const char tail[] = ">";
            int a;
            templateDef *td = ad->u.td;

            prcode(fp, "%S%s", td->fqname, (prcode_xml ? "&lt;" : "<"));

            for (a = 0; a < td->types.nrArgs; ++a)
            {
                if (a > 0)
                    prcode(fp,",");

                generateBaseType(&td->types.args[a],fp);
            }

            if (prcode_last == tail)
                prcode(fp, " ");

            prcode(fp, (prcode_xml ? "&gt;" : tail));
            break;
        }

    case enum_type:
        prcode(fp,"%E",ad->u.ed);
        break;

    case pyobject_type:
    case pytuple_type:
    case pylist_type:
    case pydict_type:
    case pycallable_type:
    case pyslice_type:
    case pytype_type:
    case qobject_type:
    case ellipsis_type:
        prcode(fp, "PyObject *");
        break;
    }

    if (nr_derefs > 0)
    {
        int i;

        prcode(fp," ");

        for (i = 0; i < nr_derefs; ++i)
            prcode(fp,"*");
    }

    if (isReference(ad))
        prcode(fp, (prcode_xml ? "&amp;" : "&"));

    if (*name != '\0')
    {
        if (nr_derefs == 0)
            prcode(fp," ");

        prcode(fp,name);
    }
}


/*
 * Generate the definition of an argument variable and any supporting
 * variables.
 */
static void generateVariable(argDef *ad,int argnr,FILE *fp)
{
    argType atype = ad->atype;
    argDef orig;

    if (isInArg(ad) && ad->defval != NULL &&
        (atype == class_type || atype == mapped_type) &&
        (ad->nrderefs == 0 || isReference(ad)))
    {
        /*
         * Generate something to hold the default value as it cannot be
         * assigned straight away.
         */
        prcode(fp,
"        %B a%ddef = ",ad,argnr);

        generateExpression(ad->defval,fp);

        prcode(fp,";\n"
            );
    }

    /* Adjust the type so we have the type that will really handle it. */

    orig = *ad;

    switch (atype)
    {
    case sstring_type:
    case ustring_type:
    case string_type:
    case wstring_type:
        if (!isReference(ad))
            if (ad->nrderefs == 2)
                ad->nrderefs = 1;
            else if (ad->nrderefs == 1 && isOutArg(ad))
                ad->nrderefs = 0;

        break;

    case mapped_type:
    case class_type:
    case void_type:
    case struct_type:
        ad->nrderefs = 1;
        break;

    default:
        ad->nrderefs = 0;
    }

    /* Array sizes are always integers. */
    if (isArraySize(ad))
        ad->atype = int_type;

    resetIsReference(ad);

    if (ad->nrderefs == 0)
        resetIsConstArg(ad);

    prcode(fp,
"        %B a%d",ad,argnr);

    if (atype == anyslot_type)
        prcode(fp, "Name");

    *ad = orig;

    generateDefaultValue(ad, argnr, fp);

    prcode(fp,";\n"
        );

    /* Some types have supporting variables. */
    if (isInArg(ad))
        switch (atype)
        {
        case class_type:
            if (ad->u.cd->convtocode != NULL && !isConstrained(ad))
                prcode(fp,
"        int a%dState = 0;\n"
                    ,argnr);

            if (isGetWrapper(ad))
                prcode(fp,
"        PyObject *a%dWrapper;\n"
                    ,argnr);

            break;

        case mapped_type:
            prcode(fp,
"        int a%dState = 0;\n"
                ,argnr);
            break;

        case anyslot_type:
            prcode(fp,
"        PyObject *a%dCallable", argnr);
            generateDefaultValue(ad, argnr, fp);
            prcode(fp, ";\n"
                );
            break;
        }
}


/*
 * Generate a default value.
 */
static void generateDefaultValue(argDef *ad, int argnr, FILE *fp)
{
    if (isInArg(ad) && ad->defval != NULL)
    {
        prcode(fp," = ");

        if ((ad->atype == class_type || ad->atype == mapped_type) &&
                (ad->nrderefs == 0 || isReference(ad)))
            prcode(fp, "&a%ddef", argnr);
        else
            generateExpression(ad->defval,fp);
    }
}


/*
 * Generate a simple function call.
 */
static void generateSimpleFunctionCall(fcallDef *fcd,FILE *fp)
{
    int i;

    prcode(fp, "%B(", &fcd->type);

    for (i = 0; i < fcd->nrArgs; ++i)
    {
        if (i > 0)
            prcode(fp,",");

        generateExpression(fcd->args[i],fp);
    }

    prcode(fp,")");
}


/*
 * Generate the type structure that contains all the information needed by the
 * metatype.  A sub-set of this is used to extend namespaces.
 */
static void generateTypeDefinition(sipSpec *pt, classDef *cd, FILE *fp)
{
    char *mname = pt->module->name;
    const char *sep;
    int is_slots, nr_methods, nr_enums;
    int is_inst_class, is_inst_voidp, is_inst_char, is_inst_string;
    int is_inst_int, is_inst_long, is_inst_ulong, is_inst_longlong;
    int is_inst_ulonglong, is_inst_double, is_inst_enum;
    memberDef *md;

    if (cd->supers != NULL)
    {
        classList *cl;

        prcode(fp,
"\n"
"\n"
"/* Define this type's super-types. */\n"
"static sipEncodedClassDef supers_%C[] = {",classFQCName(cd));

        for (cl = cd->supers; cl != NULL; cl = cl->next)
        {
            if (cl != cd->supers)
                prcode(fp,", ");

            generateEncodedClass(pt,cl->cd,(cl->next == NULL),fp);
        }

        prcode(fp,"};\n"
            );
    }

    /* Generate the slots table. */
    is_slots = FALSE;

    for (md = cd->members; md != NULL; md = md->next)
    {
        const char *stype;

        if (md->slot == no_slot)
            continue;

        if (!is_slots)
        {
            prcode(fp,
"\n"
"\n"
"/* Define this type's Python slots. */\n"
"static sipPySlotDef slots_%C[] = {\n"
                ,classFQCName(cd));

            is_slots = TRUE;
        }

        if ((stype = slotName(md->slot)) != NULL)
            prcode(fp,
"    {(void *)slot_%C_%s, %s},\n"
                ,classFQCName(cd),md->pyname->text,stype);
    }

    if (is_slots)
        prcode(fp,
"    {0, (sipPySlotType)0}\n"
"};\n"
            );

    /* Generate the attributes tables. */
    nr_methods = generateMethodTable(cd,fp);
    nr_enums = generateEnumMemberTable(pt,cd,fp);

    /* Generate each instance table. */
    is_inst_class = generateClasses(pt,cd,fp);
    is_inst_voidp = generateVoidPointers(pt,cd,fp);
    is_inst_char = generateChars(pt,cd,fp);
    is_inst_string = generateStrings(pt,cd,fp);
    is_inst_int = generateInts(pt,cd,fp);
    is_inst_long = generateLongs(pt,cd,fp);
    is_inst_ulong = generateUnsignedLongs(pt,cd,fp);
    is_inst_longlong = generateLongLongs(pt,cd,fp);
    is_inst_ulonglong = generateUnsignedLongLongs(pt,cd,fp);
    is_inst_double = generateDoubles(pt,cd,fp);
    is_inst_enum = generateEnums(pt,cd,fp);

    prcode(fp,
"\n"
"\n"
"sipTypeDef sipType_%s_%C = {\n"
"    0,\n"
"    ", mname, classFQCName(cd));

    sep = "";

    if (cd->userflags)
    {
        prcode(fp, "%s%x", sep, ((cd->userflags << TYPE_FLAGS_SHIFT) & TYPE_FLAGS_MASK));
        sep = "|";
    }

    if (isAbstractClass(cd))
    {
        prcode(fp, "%sSIP_TYPE_ABSTRACT", sep);
        sep = "|";
    }

    if (cd->subbase != NULL)
    {
        prcode(fp, "%sSIP_TYPE_SCC", sep);
        sep = "|";
    }

    if (*sep == '\0')
        prcode(fp, "0");

    prcode(fp, ",\n");

    if (cd->real != NULL)
        prcode(fp,
"    0,\n"
            );
    else if (cd->ecd != NULL && cd->ecd->real != NULL)
        prcode(fp,
"    \"%s.%P\",\n"
            , cd->ecd->real->iff->module->name, cd->ecd, cd->pyname);
    else
        prcode(fp,
"    \"%s.%P\",\n"
            , mname, cd->ecd, cd->pyname);

    if (isRenamedClass(cd))
        prcode(fp,
"    \"%S\",\n"
            , classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    prcode(fp,
"    ");

    if (cd->real != NULL)
        generateEncodedClass(pt, cd->real, 0, fp);
    else if (cd->ecd != NULL)
        generateEncodedClass(pt, cd->ecd, 0, fp);
    else
        prcode(fp, "{0, 0, 1}");

    prcode(fp, ",\n"
        );

    if (cd->supers != NULL)
        prcode(fp,
"    supers_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (is_slots)
        prcode(fp,
"    slots_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (nr_methods == 0)
        prcode(fp,
"    0, 0,\n"
            );
    else
        prcode(fp,
"    %d, methods_%C,\n"
            ,nr_methods,classFQCName(cd));

    if (nr_enums == 0)
        prcode(fp,
"    0, 0,\n"
            );
    else
        prcode(fp,
"    %d, enummembers_%C,\n"
            ,nr_enums,classFQCName(cd));

    if (hasVarHandlers(cd))
        prcode(fp,
"    variables_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (canCreate(cd))
        prcode(fp,
"    init_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (cd->travcode != NULL)
        prcode(fp,
"    traverse_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (cd->clearcode != NULL)
        prcode(fp,
"    clear_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (cd->readbufcode != NULL)
        prcode(fp,
"    getreadbuffer_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (cd->writebufcode != NULL)
        prcode(fp,
"    getwritebuffer_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (cd->segcountcode != NULL)
        prcode(fp,
"    getsegcount_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (cd->charbufcode != NULL)
        prcode(fp,
"    getcharbuffer_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (needDealloc(cd))
        prcode(fp,
"    dealloc_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    if (cd->iff->type == namespace_iface || generating_c)
        prcode(fp,
"    0,\n"
"    0,\n"
            );
    else
        prcode(fp,
"    cast_%C,\n"
"    release_%C,\n"
            , classFQCName(cd)
            , classFQCName(cd));

    if (cd->iff->type == namespace_iface)
        prcode(fp,
"    0,\n"
"    0,\n"
            );
    else
    {
        prcode(fp,
"    forceConvertTo_%C,\n"
            ,classFQCName(cd));

        if (cd->convtocode != NULL)
            prcode(fp,
"    convertTo_%C,\n"
                ,classFQCName(cd));
        else
            prcode(fp,
"    0,\n"
                );
    }

    if (!optNoEmitters(pt) && hasSigSlots(cd))
        prcode(fp,
"    signals_%C,\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    0,\n"
            );

    prcode(fp,
"    {");

    if (is_inst_class)
        prcode(fp,"classInstances_%C, ",classFQCName(cd));
    else
        prcode(fp,"0, ");

    if (is_inst_voidp)
        prcode(fp,"voidPtrInstances_%C, ",classFQCName(cd));
    else
        prcode(fp,"0, ");

    if (is_inst_char)
        prcode(fp,"charInstances_%C, ",classFQCName(cd));
    else
        prcode(fp,"0, ");

    if (is_inst_string)
        prcode(fp,"stringInstances_%C, ",classFQCName(cd));
    else
        prcode(fp,"0, ");

    if (is_inst_int)
        prcode(fp,"intInstances_%C, ",classFQCName(cd));
    else
        prcode(fp,"0, ");

    if (is_inst_long)
        prcode(fp,"longInstances_%C, ",classFQCName(cd));
    else
        prcode(fp,"0, ");

    if (is_inst_ulong)
        prcode(fp,"unsignedLongInstances_%C, ",classFQCName(cd));
    else
        prcode(fp,"0, ");

    if (is_inst_longlong)
        prcode(fp,"longLongInstances_%C, ",classFQCName(cd));
    else
        prcode(fp,"0, ");

    if (is_inst_ulonglong)
        prcode(fp,"unsignedLongLongInstances_%C, ",classFQCName(cd));
    else
        prcode(fp,"0, ");

    if (is_inst_double)
        prcode(fp,"doubleInstances_%C, ",classFQCName(cd));
    else
        prcode(fp,"0, ");

    if (is_inst_enum)
        prcode(fp,"enumInstances_%C",classFQCName(cd));
    else
        prcode(fp,"0");

    prcode(fp,"},\n"
"    0\n"
"};\n"
        );
}


/*
 * Return the sip module's string equivalent of a slot.
 */
static const char *slotName(slotType st)
{
    const char *sn;

    switch (st)
    {
    case str_slot:
        sn = "str_slot";
        break;

    case int_slot:
        sn = "int_slot";
        break;

    case long_slot:
        sn = "long_slot";
        break;

    case float_slot:
        sn = "float_slot";
        break;

    case len_slot:
        sn = "len_slot";
        break;

    case contains_slot:
        sn = "contains_slot";
        break;

    case add_slot:
        sn = "add_slot";
        break;

    case concat_slot:
        sn = "concat_slot";
        break;

    case sub_slot:
        sn = "sub_slot";
        break;

    case mul_slot:
        sn = "mul_slot";
        break;

    case repeat_slot:
        sn = "repeat_slot";
        break;

    case div_slot:
        sn = "div_slot";
        break;

    case mod_slot:
        sn = "mod_slot";
        break;

    case and_slot:
        sn = "and_slot";
        break;

    case or_slot:
        sn = "or_slot";
        break;

    case xor_slot:
        sn = "xor_slot";
        break;

    case lshift_slot:
        sn = "lshift_slot";
        break;

    case rshift_slot:
        sn = "rshift_slot";
        break;

    case iadd_slot:
        sn = "iadd_slot";
        break;

    case iconcat_slot:
        sn = "iconcat_slot";
        break;

    case isub_slot:
        sn = "isub_slot";
        break;

    case imul_slot:
        sn = "imul_slot";
        break;

    case irepeat_slot:
        sn = "irepeat_slot";
        break;

    case idiv_slot:
        sn = "idiv_slot";
        break;

    case imod_slot:
        sn = "imod_slot";
        break;

    case iand_slot:
        sn = "iand_slot";
        break;

    case ior_slot:
        sn = "ior_slot";
        break;

    case ixor_slot:
        sn = "ixor_slot";
        break;

    case ilshift_slot:
        sn = "ilshift_slot";
        break;

    case irshift_slot:
        sn = "irshift_slot";
        break;

    case invert_slot:
        sn = "invert_slot";
        break;

    case call_slot:
        sn = "call_slot";
        break;

    case getitem_slot:
        sn = "getitem_slot";
        break;

    case setitem_slot:
        sn = "setitem_slot";
        break;

    case delitem_slot:
        sn = "delitem_slot";
        break;

    case lt_slot:
        sn = "lt_slot";
        break;

    case le_slot:
        sn = "le_slot";
        break;

    case eq_slot:
        sn = "eq_slot";
        break;

    case ne_slot:
        sn = "ne_slot";
        break;

    case gt_slot:
        sn = "gt_slot";
        break;

    case ge_slot:
        sn = "ge_slot";
        break;

    case cmp_slot:
        sn = "cmp_slot";
        break;

    case nonzero_slot:
        sn = "nonzero_slot";
        break;

    case neg_slot:
        sn = "neg_slot";
        break;

    case pos_slot:
        sn = "pos_slot";
        break;

    case abs_slot:
        sn = "abs_slot";
        break;

    case repr_slot:
        sn = "repr_slot";
        break;

    case hash_slot:
        sn = "hash_slot";
        break;

    default:
        sn = NULL;
    }

    return sn;
}


/*
 * Generate the code to register a class as a Qt metatype.
 */
static void generateRegisterMetaType(classDef *cd, FILE *fp)
{
    int pub_def_ctor, pub_copy_ctor;
    ctorDef *ct;

    /*
     * We register types with Qt if the class is not abstract, has a public
     * default ctor, a public copy ctor, a public dtor and isn't one of the
     * internally supported types.
     */
    if (isAbstractClass(cd))
        return;

    if (!isPublicDtor(cd))
        return;

    if (classFQCName(cd)->next == NULL)
    {
        if (strcmp(classBaseName(cd), "QChar") == 0)
            return;

        if (strcmp(classBaseName(cd), "QString") == 0)
            return;

        if (strcmp(classBaseName(cd), "QByteArray") == 0)
            return;
    }

    pub_def_ctor = pub_copy_ctor = FALSE;

    for (ct = cd->ctors; ct != NULL; ct = ct->next)
    {
        if (ct->cppsig == NULL || !isPublicCtor(ct))
            continue;

        if (ct->cppsig->nrArgs == 0)
            pub_def_ctor = TRUE;
        else if (ct->cppsig->nrArgs == 1)
        {
            argDef *ad = &ct->cppsig->args[0];

            if (ad->atype == class_type && ad->u.cd == cd && isReference(ad) &&
                isConstArg(ad) && ad->nrderefs == 0 && ad->defval == NULL)
                pub_copy_ctor = TRUE;
        }
    }

    if (pub_def_ctor && pub_copy_ctor)
        prcode(fp,
"    qRegisterMetaType<%S>(\"%S\");\n"
            , classFQCName(cd), classFQCName(cd));
}


/*
 * Generate the initialisation function or cast operators for the type.
 */
static void generateTypeInit(sipSpec *pt, classDef *cd, FILE *fp)
{
    ctorDef *ct;
    int need_self, need_owner;

    /*
     * See if we need to name the self and owner arguments so that we can
     * avoid a compiler warning about an unused argument.
     */
    need_self = (generating_c || hasShadow(cd));
    need_owner = generating_c;

    for (ct = cd->ctors; ct != NULL; ct = ct->next)
    {
        int a;

        if (usedInCode(ct->methodcode, "sipSelf"))
            need_self = TRUE;

        for (a = 0; a < ct->pysig.nrArgs; ++a)
            if (isThisTransferred(&ct->pysig.args[a]))
            {
                need_owner = TRUE;
                break;
            }
    }

    prcode(fp,
"\n"
"\n"
        );

    if (!generating_c)
        prcode(fp,
"extern \"C\" {static void *init_%C(sipWrapper *, PyObject *, sipWrapper **, int *);}\n"
            , classFQCName(cd));

    prcode(fp,
"static void *init_%C(sipWrapper *%s,PyObject *sipArgs,sipWrapper **%s,int *sipArgsParsed)\n"
"{\n"
        ,classFQCName(cd),(need_self ? "sipSelf" : ""),(need_owner ? "sipOwner" : ""));

    if (hasShadow(cd))
        prcode(fp,
"    sip%C *sipCpp = 0;\n"
            ,classFQCName(cd));
    else
        prcode(fp,
"    %U *sipCpp = 0;\n"
            ,cd);

    if (tracing)
        prcode(fp,
"\n"
"    sipTrace(SIP_TRACE_INITS,\"init_%C()\\n\");\n"
            ,classFQCName(cd));

    /*
     * Generate the code that parses the Python arguments and calls the
     * correct constructor.
     */
    for (ct = cd->ctors; ct != NULL; ct = ct->next)
    {
        int needSecCall, error_flag = FALSE;

        if (isPrivateCtor(ct))
            continue;

        prcode(fp,
"\n"
"    if (!sipCpp)\n"
"    {\n"
            );

        if (ct->methodcode != NULL && needErrorFlag(ct->methodcode))
        {
            prcode(fp,
"        int sipIsErr = 0;\n"
                );

            error_flag = TRUE;
        }

        needSecCall = generateArgParser(pt, &ct->pysig, cd, ct, NULL, FALSE, fp);
        generateConstructorCall(cd,ct,error_flag,fp);

        if (needSecCall)
        {
                prcode(fp,
"    }\n"
"\n"
"    if (!sipCpp)\n"
"    {\n"
                );

            if (error_flag)
                prcode(fp,
"        int sipIsErr = 0;\n"
                    );

            generateArgParser(pt, &ct->pysig, cd, ct, NULL, TRUE, fp);
            generateConstructorCall(cd,ct,error_flag,fp);
        }

        prcode(fp,
"    }\n"
            );
    }

    if (hasShadow(cd))
        prcode(fp,
"\n"
"    if (sipCpp)\n"
"        sipCpp->sipPySelf = sipSelf;\n"
            );

    prcode(fp,
"\n"
"    return sipCpp;\n"
"}\n"
        );
}


/*
 * Count the number of virtual members in a class.
 */
static int countVirtuals(classDef *cd)
{
    int nrvirts;
    virtOverDef *vod;
 
    nrvirts = 0;
 
    for (vod = cd->vmembers; vod != NULL; vod = vod->next)
        if (!isPrivate(&vod->o))
            ++nrvirts;
 
    return nrvirts;
}

 
/*
 * Generate the try block for a call.
 */
static void generateTry(throwArgs *ta,FILE *fp)
{
    /*
     * Generate the block if there was no throw specifier, or a non-empty
     * throw specifier.
     */
    if (exceptions && (ta == NULL || ta->nrArgs > 0))
        prcode(fp,
"            try\n"
"            {\n"
            );
}


/*
 * Generate the catch block for a call.
 */
static void generateCatch(throwArgs *ta, signatureDef *sd, FILE *fp)
{
    /*
     * Generate the block if there was no throw specifier, or a non-empty
     * throw specifier.
     */
    if (exceptions && (ta == NULL || ta->nrArgs > 0))
    {
        prcode(fp,
"            }\n"
            );

        if (ta == NULL)
        {
            prcode(fp,
"            catch (...)\n"
"            {\n"
                );

            deleteTemps(sd, fp);

            if (release_gil)
                prcode(fp,
"                Py_BLOCK_THREADS\n"
"\n"
                    );

            prcode(fp,
"                sipRaiseUnknownException();\n"
"                return NULL;\n"
"            }\n"
                );
        }
        else
        {
            int a;

            for (a = 0; a < ta->nrArgs; ++a)
            {
                exceptionDef *xd = ta->args[a];
                scopedNameDef *ename = xd->iff->fqcname;

                prcode(fp,
"            catch (%S &%s)\n"
"            {\n"
                    ,ename,(xd->cd != NULL || usedInCode(xd->raisecode, "sipExceptionRef")) ? "sipExceptionRef" : "");

                deleteTemps(sd, fp);

                if (xd->cd != NULL)
                {
                    /* The exception is a wrapped class. */

                    prcode(fp,
"                /* Hope that there is a valid copy ctor. */\n"
"                %S *sipExceptionCopy = new %S(sipExceptionRef);\n"
"\n"
                        ,ename,ename);

                    if (release_gil)
                        prcode(fp,
"                Py_BLOCK_THREADS\n"
"\n"
                            );

                    prcode(fp,
"                sipRaise%sClassException(sipClass_%C,sipExceptionCopy);\n"
                        ,(xd->cd->subbase != NULL ? "Sub" : ""),ename);
                }
                else
                    generateCppCodeBlock(xd->raisecode,fp);

                prcode(fp,
"\n"
"                return NULL;\n"
"            }\n"
                    );
            }
        }
    }
}


/*
 * Generate a throw specifier.
 */
static void generateThrowSpecifier(throwArgs *ta,FILE *fp)
{
    if (exceptions && ta != NULL)
    {
        int a;

        prcode(fp," throw(");

        for (a = 0; a < ta->nrArgs; ++a)
        {
            if (a > 0)
                prcode(fp,",");

            prcode(fp,"%S",ta->args[a]->iff->fqcname);
        }

        prcode(fp,")");
    }
}


/*
 * Generate a single constructor call.
 */
static void generateConstructorCall(classDef *cd,ctorDef *ct,int error_flag,
                                    FILE *fp)
{
    prcode(fp,
"        {\n"
        );

    /* Call any pre-hook. */
    if (ct->prehook != NULL)
        prcode(fp,
"            sipCallHook(\"%s\");\n"
"\n"
            ,ct->prehook);

    if (ct->methodcode != NULL)
        generateCppCodeBlock(ct->methodcode,fp);
    else if (generating_c)
        prcode(fp,
"            sipCpp = sipMalloc(sizeof (%S));\n"
            ,classFQCName(cd));
    else
    {
        int rgil = ((release_gil || isReleaseGILCtor(ct)) && !isHoldGILCtor(ct));

        if (rgil)
            prcode(fp,
"            Py_BEGIN_ALLOW_THREADS\n"
                );

        generateTry(ct->exceptions,fp);

        if (hasShadow(cd))
            prcode(fp,
"            sipCpp = new sip%C(",classFQCName(cd));
        else
            prcode(fp,
"            sipCpp = new %U(",cd);

        if (isCastCtor(ct))
        {
            classDef *ocd;

            /* We have to fiddle the type to generate the correct code. */
            ocd = ct->pysig.args[0].u.cd;
            ct->pysig.args[0].u.cd = cd;
            prcode(fp, "a0->operator %B()", &ct->pysig.args[0]);
            ct->pysig.args[0].u.cd = ocd;
        }
        else
            generateArgs(ct->cppsig, Call, fp);

        prcode(fp,");\n"
            );

        generateCatch(ct->exceptions, &ct->pysig, fp);

        if (rgil)
            prcode(fp,
"            Py_END_ALLOW_THREADS\n"
                );
    }

    gc_ellipsis(&ct->pysig, fp);

    deleteTemps(&ct->pysig, fp);

    if (error_flag)
        prcode(fp,
"\n"
"            if (sipIsErr)\n"
"                return 0;\n"
            );

    /* Call any post-hook. */
    if (ct->posthook != NULL)
        prcode(fp,
"\n"
"            sipCallHook(\"%s\");\n"
            ,ct->posthook);

    prcode(fp,
"        }\n"
        );
}


/*
 * See if a member overload should be skipped.
 */
static int skipOverload(overDef *od,memberDef *md,classDef *cd,classDef *ccd,
            int want_local)
{
    /* Skip if it's not the right name. */
    if (od->common != md)
        return TRUE;

    /* Skip if it's a signal. */
    if (isSignal(od))
        return TRUE;

    /* Skip if it's a private abstract. */
    if (isAbstract(od) && isPrivate(od))
        return TRUE;

    /*
     * If we are disallowing them, skip if it's not in the current class
     * unless it is protected.
     */
    if (want_local && !isProtected(od) && ccd != cd)
        return TRUE;

    return FALSE;
}


/*
 * Generate a class member function.
 */
static void generateFunction(sipSpec *pt,memberDef *md,overDef *overs,
                 classDef *cd,classDef *ocd,FILE *fp)
{
    overDef *od;
    int need_method, need_self, need_args, need_selfarg;

    /*
     * Check that there is at least one overload that needs to be handled.
     * See if we can avoid naming the "self" argument (and suppress a
     * compiler warning).  Finally see if we need to remember if "self" was
     * explicitly passed as an argument.
     */
    need_method = need_self = need_args = need_selfarg = FALSE;

    for (od = overs; od != NULL; od = od->next)
    {
        /*
         * Skip protected methods if we don't have the means to handle
         * them.
         */
        if (isProtected(od) && !hasShadow(cd))
            continue;

        if (!skipOverload(od,md,cd,ocd,TRUE))
        {
            need_method = TRUE;

            if (!isPrivate(od))
            {
                need_args = TRUE;

                if (!isStatic(od))
                {
                    need_self = TRUE;

                    if (isAbstract(od) || isVirtual(od) || isVirtualReimp(od) || usedInCode(od->methodcode, "sipSelfWasArg"))
                        need_selfarg = TRUE;
                }
            }
        }
    }

    if (need_method)
    {
        char *pname = md->pyname->text;

        prcode(fp,
"\n"
"\n"
            );

        if (!generating_c)
            prcode(fp,
"extern \"C\" {static PyObject *meth_%C_%s(PyObject *, PyObject *);}\n"
            , classFQCName(cd), pname);

        prcode(fp,
"static PyObject *meth_%C_%s(PyObject *%s,PyObject *%s)\n"
"{\n"
            ,classFQCName(cd),pname,(need_self ? "sipSelf" : ""),(need_args ? "sipArgs" : ""));

        if (tracing)
            prcode(fp,
"    sipTrace(SIP_TRACE_METHODS,\"meth_%C_%s()\\n\");\n"
"\n"
                ,classFQCName(cd),pname);

        if (need_args)
            prcode(fp,
"    int sipArgsParsed = 0;\n"
                );

        if (need_selfarg)
            prcode(fp,
"    bool sipSelfWasArg = !sipSelf;\n"
                );

        for (od = overs; od != NULL; od = od->next)
        {
            /*
             * If we are handling one variant then we must handle
             * them all.
             */
            if (skipOverload(od,md,cd,ocd,FALSE))
                continue;

            if (isPrivate(od))
                continue;

            generateFunctionBody(pt,od,cd,ocd,TRUE,fp);
        }

        prcode(fp,
"\n"
"    /* Raise an exception if the arguments couldn't be parsed. */\n"
"    sipNoMethod(%s,%N,%N);\n"
"\n"
"    return NULL;\n"
"}\n"
            ,(need_args ? "sipArgsParsed" : "0"),cd->iff->name,md->pyname);
    }
}


/*
 * Generate the function calls for a particular overload.
 */
static void generateFunctionBody(sipSpec *pt,overDef *od,classDef *cd,
                 classDef *ocd,int deref,FILE *fp)
{
    int needSecCall;
    signatureDef saved;

    prcode(fp,
"\n"
"    {\n"
        );

    /* In case we have to fiddle with it. */
    saved = od->pysig;

    if (isNumberSlot(od->common))
    {
        /*
         * Number slots must have two arguments because we parse them
         * slightly differently.
         */
        if (od->pysig.nrArgs == 1)
        {
            od->pysig.nrArgs = 2;
            od->pysig.args[1] = od->pysig.args[0];

            /* Insert self as the first argument. */
            od->pysig.args[0].atype = class_type;
            od->pysig.args[0].name = NULL;
            od->pysig.args[0].argflags = ARG_IS_REF|ARG_IN;
            od->pysig.args[0].nrderefs = 0;
            od->pysig.args[0].defval = NULL;
            od->pysig.args[0].u.cd = ocd;
        }

        generateArgParser(pt, &od->pysig, cd, NULL, od, FALSE, fp);
        needSecCall = FALSE;
    }
    else if (isIntArgSlot(od->common) || isZeroArgSlot(od->common))
        needSecCall = FALSE;
    else
        needSecCall = generateArgParser(pt, &od->pysig, cd, NULL, od, FALSE, fp);

    generateFunctionCall(cd,ocd,od,deref,fp);

    if (needSecCall)
    {
        prcode(fp,
"    }\n"
"\n"
"    {\n"
            );

        generateArgParser(pt, &od->pysig, cd, NULL, od, TRUE, fp);
        generateFunctionCall(cd,ocd,od,deref,fp);
    }

    prcode(fp,
"    }\n"
        );

    od->pysig = saved;
}


/*
 * Generate the code to handle the result of a call to a member function.
 */
static void generateHandleResult(overDef *od,int isNew,char *prefix,FILE *fp)
{
    char *vname, vnamebuf[50];
    int a, nrvals, only, has_owner;
    argDef *res, *ad;

    res = &od->pysig.result;

    if (res->atype == void_type && res->nrderefs == 0)
        res = NULL;

    /* See if we are returning 0, 1 or more values. */
    nrvals = 0;

    if (res != NULL)
    {
        only = -1;
        ++nrvals;
    }

    has_owner = FALSE;

    for (a = 0; a < od->pysig.nrArgs; ++a)
    {
        if (isOutArg(&od->pysig.args[a]))
        {
            only = a;
            ++nrvals;
        }

        if (isThisTransferred(&od->pysig.args[a]))
            has_owner = TRUE;
    }

    /* Handle the trivial case. */
    if (nrvals == 0)
    {
        prcode(fp,
"            Py_INCREF(Py_None);\n"
"            %s Py_None;\n"
            ,prefix);

        return;
    }

    /* Handle results that are classes or mapped types separately. */
    if (res != NULL)
        if (res->atype == mapped_type)
        {
            prcode(fp,
"            PyObject *sipResObj = sipConvertFromMappedType(");

            if (isConstArg(res))
                prcode(fp,"const_cast<%b *>(sipRes)",res);
            else
                prcode(fp,"sipRes");

            prcode(fp,",sipMappedType_%T,%s);\n"
                , res, isResultTransferredBack(od) ? "Py_None" : "NULL");

            if (isNew)
                prcode(fp,
"            delete sipRes;\n"
                    );

            /* Shortcut if this is the only value returned. */
            if (nrvals == 1)
            {
                prcode(fp,
"\n"
"            %s sipResObj;\n"
                    ,prefix);

                return;
            }
        }
        else if (res->atype == class_type)
        {
            classDef *cd = res->u.cd;

            if (isNew || isFactory(od))
            {
                prcode(fp,
"            %s sipConvertFromNewInstance(",(nrvals == 1 ? prefix : "PyObject *sipResObj ="));

                if (isConstArg(res))
                    prcode(fp,"const_cast<%b *>(sipRes)",res);
                else
                    prcode(fp,"sipRes");

                prcode(fp,",sipClass_%C,%s);\n"
                    ,classFQCName(cd),((has_owner && isFactory(od)) ? "(PyObject *)sipOwner" : "NULL"));

                /*
                 * Shortcut if this is the only value returned.
                 */
                if (nrvals == 1)
                    return;
            }
            else
            {
                prcode(fp,
"            %s sipConvertFromInstance(",(nrvals == 1 ? prefix : "PyObject *sipResObj ="));

                if (isConstArg(res))
                    prcode(fp,"const_cast<%b *>(sipRes)",res);
                else
                    prcode(fp,"sipRes");

                prcode(fp, ",sipClass_%C,%s);\n"
                    , classFQCName(cd), (isResultTransferredBack(od) ? "Py_None" : "NULL"));

                /*
                 * Shortcut if this is the only value returned.
                 */
                if (nrvals == 1)
                    return;
            }
        }

    /* If there are multiple values then build a tuple. */
    if (nrvals > 1)
    {
        prcode(fp,
"            %s sipBuildResult(0,\"(",prefix);

        /* Build the format string. */
        if (res != NULL)
            prcode(fp,"%c",((res->atype == mapped_type || res->atype == class_type) ? 'R' : getBuildResultFormat(res)));

        for (a = 0; a < od->pysig.nrArgs; ++a)
        {
            argDef *ad = &od->pysig.args[a];

            if (isOutArg(ad))
                prcode(fp,"%c",getBuildResultFormat(ad));
        }

        prcode(fp,")\"");

        /* Pass the values for conversion. */
        if (res != NULL)
        {
            prcode(fp,",sipRes");

            if (res->atype == mapped_type || res->atype == class_type)
                prcode(fp,"Obj");
            else if (res->atype == enum_type && res->u.ed->fqcname != NULL)
                prcode(fp,",sipEnum_%C",res->u.ed->fqcname);
        }

        for (a = 0; a < od->pysig.nrArgs; ++a)
        {
            argDef *ad = &od->pysig.args[a];

            if (isOutArg(ad))
            {
                prcode(fp,",a%d",a);

                if (ad->atype == mapped_type)
                    prcode(fp, ",sipMappedType_%T,%s", ad, (isTransferredBack(ad) ? "Py_None" : "NULL"));
                else if (ad->atype == class_type)
                    prcode(fp, ",sipClass_%C,%s", classFQCName(ad->u.cd), (isTransferredBack(ad) ? "Py_None" : "NULL"));
                else if (ad->atype == enum_type && ad->u.ed->fqcname != NULL)
                    prcode(fp,",sipEnum_%C",ad->u.ed->fqcname);
            }
        }

        prcode(fp,");\n"
            );

        /* All done for multiple values. */
        return;
    }

    /* Deal with the only returned value. */
    if (only < 0)
    {
        ad = res;
        vname = "sipRes";
    }
    else
    {
        ad = &od->pysig.args[only];

        sprintf(vnamebuf,"a%d",only);
        vname = vnamebuf;
    }

    switch (ad->atype)
    {
    case mapped_type:
        prcode(fp,
"            %s sipConvertFromMappedType(", prefix);

        if (isConstArg(ad))
            prcode(fp,"const_cast<%b *>(%s)",ad,vname);
        else
            prcode(fp,"%s",vname);

        prcode(fp,",sipMappedType_%T,%s);\n"
            , ad, (isTransferredBack(ad) ? "Py_None" : "NULL"));

        break;

    case class_type:
        {
            classDef *cd = ad->u.cd;
            int needNew = needNewInstance(ad);

            if (needNew)
                prcode(fp,
"            %s sipConvertFromNewInstance(", prefix);
            else
                prcode(fp,
"            %s sipConvertFromInstance(", prefix);

            if (isConstArg(ad))
                prcode(fp,"const_cast<%b *>(%s)",ad,vname);
            else
                prcode(fp,"%s",vname);

            prcode(fp,",sipClass_%C,",classFQCName(cd));

            if (needNew)
                prcode(fp,"NULL");
            else
                prcode(fp,"%s\n"
                    , (isTransferredBack(ad) ? "Py_None" : "NULL"));

            prcode(fp,");\n"
                );
        }

        break;

    case bool_type:
    case cbool_type:
        prcode(fp,
"            %s PyBool_FromLong(%s);\n"
            ,prefix,vname);

        break;

    case sstring_type:
    case ustring_type:
    case string_type:
        if (ad->nrderefs == 0)
            prcode(fp,
"            %s PyString_FromStringAndSize(%s&%s,1);\n"
                ,prefix,(ad->atype != string_type) ? "(char *)" : "",vname);
        else
            prcode(fp,
"            if (%s == NULL)\n"
"            {\n"
"                Py_INCREF(Py_None);\n"
"                return Py_None;\n"
"            }\n"
"\n"
"            %s PyString_FromString(%s%s);\n"
            ,vname
            ,prefix,(ad->atype != string_type) ? "(char *)" : "",vname);

        break;

    case wstring_type:
        if (ad->nrderefs == 0)
            prcode(fp,
"            %s PyUnicode_FromWideChar(&%s,1);\n"
                , prefix, vname);
        else
            prcode(fp,
"            if (%s == NULL)\n"
"            {\n"
"                Py_INCREF(Py_None);\n"
"                return Py_None;\n"
"            }\n"
"\n"
"            %s PyUnicode_FromWideChar(%s,(SIP_SSIZE_T)wcslen(%s));\n"
            , vname
            , prefix, vname, vname);

        break;

    case enum_type:
        if (ad->u.ed->fqcname != NULL)
        {
            prcode(fp,
"            %s sipConvertFromNamedEnum(%s,sipEnum_%C);\n"
                ,prefix,vname,ad->u.ed->fqcname);

            break;
        }

        /* Drop through. */

    case short_type:
    case int_type:
    case cint_type:
        prcode(fp,
"            %s PyInt_FromLong(%s);\n"
            ,prefix,vname);

        break;

    case long_type:
        prcode(fp,
"            %s PyLong_FromLong(%s);\n"
            ,prefix,vname);

        break;

    case ushort_type:
    case uint_type:
    case ulong_type:
        prcode(fp,
"            %s PyLong_FromUnsignedLong(%s);\n"
            ,prefix,vname);

        break;

    case longlong_type:
        prcode(fp,
"            %s PyLong_FromLongLong(%s);\n"
            ,prefix,vname);

        break;

    case ulonglong_type:
        prcode(fp,
"            %s PyLong_FromUnsignedLongLong(%s);\n"
            ,prefix,vname);

        break;

    case void_type:
        prcode(fp,
"            %s sipConvertFromVoidPtr(", prefix);

        if (isConstArg(ad))
            prcode(fp, "const_cast<void *>(%s)", vname);
        else
            prcode(fp, "%s", vname);

        prcode(fp, ");\n"
            );

        break;

    case struct_type:
        prcode(fp,
"            %s sipConvertFromVoidPtr(",prefix);

        if (isConstArg(ad))
            prcode(fp,"const_cast<%b *>(%s)",ad,vname);
        else
            prcode(fp,"%s",vname);

        prcode(fp,");\n"
            );

        break;

    case float_type:
    case cfloat_type:
        prcode(fp,
"            %s PyFloat_FromDouble((double)%s);\n"
            ,prefix,vname);

        break;

    case double_type:
    case cdouble_type:
        prcode(fp,
"            %s PyFloat_FromDouble(%s);\n"
            ,prefix,vname);

        break;

    case pyobject_type:
    case pytuple_type:
    case pylist_type:
    case pydict_type:
    case pycallable_type:
    case pyslice_type:
    case pytype_type:
        prcode(fp,
"            %s %s;\n"
            ,prefix,vname);

        break;
    }
}


/*
 * Return the format character used by sipBuildResult() for a particular type.
 */
static char getBuildResultFormat(argDef *ad)
{
    switch (ad->atype)
    {
    case mapped_type:
        return 'D';

    case class_type:
        if (needNewInstance(ad))
            return 'B';

        return 'C';

    case bool_type:
    case cbool_type:
        return 'b';

    case sstring_type:
    case ustring_type:
    case string_type:
        return (ad->nrderefs > (isOutArg(ad) ? 1 : 0)) ? 's' : 'c';

    case wstring_type:
        return (ad->nrderefs > (isOutArg(ad) ? 1 : 0)) ? 'x' : 'w';

    case enum_type:
        return (ad->u.ed->fqcname != NULL) ? 'E' : 'e';

    case short_type:
        return 'h';

    case ushort_type:
        return 't';

    case int_type:
    case cint_type:
        return 'i';

    case uint_type:
        return 'u';

    case long_type:
        return 'l';

    case ulong_type:
        return 'm';

    case longlong_type:
        return 'n';

    case ulonglong_type:
        return 'o';

    case void_type:
    case struct_type:
        return 'V';

    case float_type:
    case cfloat_type:
        return 'f';

    case double_type:
    case cdouble_type:
        return 'd';

    case pyobject_type:
    case pytuple_type:
    case pylist_type:
    case pydict_type:
    case pycallable_type:
    case pyslice_type:
    case pytype_type:
        return 'R';
    }

    /* We should never get here. */
    return ' ';
}


/*
 * Generate a function call.
 */
static void generateFunctionCall(classDef *cd,classDef *ocd,overDef *od,
                 int deref, FILE *fp)
{
    int needsNew, error_flag = FALSE, newline, is_result, a, deltemps;
    argDef *res = &od->pysig.result, orig_res;

    prcode(fp,
"        {\n"
        );

    /*
     * If there is no shadow class then protected methods can never be
     * called.
     */
    if (isProtected(od) && !hasShadow(cd))
    {
        prcode(fp,
"            /* Never reached. */\n"
"        }\n"
            );

        return;
    }

    /* Save the full result type as we may want to fiddle with it. */
    orig_res = *res;

    /* See if we need to make a copy of the result on the heap. */
    if ((res->atype == class_type || res->atype == mapped_type) &&
        !isReference(res) &&
        res->nrderefs == 0)
    {
        needsNew = TRUE;
        resetIsConstArg(res);
    }
    else
        needsNew = FALSE;

    /* See if sipRes is needed. */
    is_result = (!isInplaceNumberSlot(od->common) &&
             !isInplaceSequenceSlot(od->common) &&
             (res->atype != void_type || res->nrderefs != 0));

    newline = FALSE;

    if (is_result)
    {
        prcode(fp,
"            ");

        generateNamedValueType(res,"sipRes",fp);

        /*
         * The typical %MethodCode usually causes a compiler warning,
         * so we initialise the result in that case to try and suppress
         * it.
         */
        if (od->methodcode != NULL)
        {
            prcode(fp," = ");

            generateCastZero(res,fp);
        }

        prcode(fp,";\n"
            );

        newline = TRUE;
    }

    deltemps = TRUE;

    for (a = 0; a < od->pysig.nrArgs; ++a)
    {
        argDef *ad = &od->pysig.args[a];

        /*
         * If we have an In,Out argument that has conversion code then we delay
         * the destruction of any temporary variables until after we have
         * converted the outputs.
         */
        if (isInArg(ad) && isOutArg(ad) && hasConvertToCode(ad) && deltemps)
        {
            deltemps = FALSE;

            prcode(fp,
"            PyObject *sipResult;\n"
                );

            newline = TRUE;
        }

        /*
         * If we are returning a class via an output only reference or pointer
         * then we need an instance on the heap.
         */
        if (needNewInstance(ad))
        {
            prcode(fp,
"            a%d = new %b();\n"
                ,a,ad);

            newline = TRUE;
        }
    }

    if (od->methodcode != NULL)
    {
        /* See if the handwritten code seems to be using the error flag. */
        if (needErrorFlag(od->methodcode))
        {
            prcode(fp,
"            int sipIsErr = 0;\n"
                );

            newline = TRUE;
            error_flag = TRUE;
        }
    }

    if (newline)
        prcode(fp,
"\n"
            );

    /* If it is abstract make sure that self was bound. */
    if (isAbstract(od))
        prcode(fp,
"            if (sipSelfWasArg)\n"
"            {\n"
"                sipAbstractMethod(%N,%N);\n"
"                return NULL;\n"
"            }\n"
"\n"
            , cd->iff->name, od->common->pyname);

    /* Call any pre-hook. */
    if (od->prehook != NULL)
        prcode(fp,
"            sipCallHook(\"%s\");\n"
"\n"
            ,od->prehook);

    if (od->methodcode != NULL)
        generateCppCodeBlock(od->methodcode,fp);
    else
    {
        int rgil = ((release_gil || isReleaseGIL(od)) && !isHoldGIL(od));

        if (needsNew && generating_c)
        {
            prcode(fp,
"            if ((sipRes = (%b *)sipMalloc(sizeof (%b))) == NULL)\n"
"        {\n"
                ,res,res);

            gc_ellipsis(&od->pysig, fp);

            prcode(fp,
"                return NULL;\n"
"            }\n"
"\n"
                );
        }

        if (rgil)
            prcode(fp,
"            Py_BEGIN_ALLOW_THREADS\n"
                );

        generateTry(od->exceptions,fp);

        prcode(fp,
"            ");

        if (od->common->slot != cmp_slot && is_result)
        {
            /* Construct a copy on the heap if needed. */
            if (needsNew)
            {
                if (generating_c)
                    prcode(fp,"*sipRes = ");
                else
                    prcode(fp,"sipRes = new %b(",res);
            }
            else
                prcode(fp,"sipRes = ");

            /* See if we need the address of the result. */
            if ((res->atype == class_type || res->atype == mapped_type) && isReference(res))
                prcode(fp,"&");
        }

        switch (od->common->slot)
        {
        case no_slot:
            generateCppFunctionCall(cd,ocd,od,fp);
            break;

        case getitem_slot:
            prcode(fp, "(*sipCpp)[");
            generateSlotArg(&od->pysig, 0, fp);
            prcode(fp,"]");
            break;

        case call_slot:
            prcode(fp, "(*sipCpp)(");
            generateArgs(od->cppsig,Call,fp);
            prcode(fp,")");
            break;

        case int_slot:
        case long_slot:
        case float_slot:
            prcode(fp, "*sipCpp");
            break;

        case add_slot:
            generateNumberSlotCall(od,"+",fp);
            break;

        case concat_slot:
            generateBinarySlotCall(od,"+",deref,fp);
            break;

        case sub_slot:
            generateNumberSlotCall(od,"-",fp);
            break;

        case mul_slot:
            generateNumberSlotCall(od,"*",fp);
            break;

        case repeat_slot:
            generateBinarySlotCall(od,"*",deref,fp);
            break;

        case div_slot:
            generateNumberSlotCall(od,"/",fp);
            break;

        case mod_slot:
            generateNumberSlotCall(od,"%",fp);
            break;

        case and_slot:
            generateNumberSlotCall(od,"&",fp);
            break;

        case or_slot:
            generateNumberSlotCall(od,"|",fp);
            break;

        case xor_slot:
            generateNumberSlotCall(od,"^",fp);
            break;

        case lshift_slot:
            generateNumberSlotCall(od,"<<",fp);
            break;

        case rshift_slot:
            generateNumberSlotCall(od,">>",fp);
            break;

        case iadd_slot:
        case iconcat_slot:
            generateBinarySlotCall(od,"+=",deref,fp);
            break;

        case isub_slot:
            generateBinarySlotCall(od,"-=",deref,fp);
            break;

        case imul_slot:
        case irepeat_slot:
            generateBinarySlotCall(od,"*=",deref,fp);
            break;

        case idiv_slot:
            generateBinarySlotCall(od,"/=",deref,fp);
            break;

        case imod_slot:
            generateBinarySlotCall(od,"%=",deref,fp);
            break;

        case iand_slot:
            generateBinarySlotCall(od,"&=",deref,fp);
            break;

        case ior_slot:
            generateBinarySlotCall(od,"|=",deref,fp);
            break;

        case ixor_slot:
            generateBinarySlotCall(od,"^=",deref,fp);
            break;

        case ilshift_slot:
            generateBinarySlotCall(od,"<<=",deref,fp);
            break;

        case irshift_slot:
            generateBinarySlotCall(od,">>=",deref,fp);
            break;

        case invert_slot:
            prcode(fp, "~(*sipCpp)");
            break;

        case lt_slot:
            generateBinarySlotCall(od,"<",deref,fp);
            break;

        case le_slot:
            generateBinarySlotCall(od,"<=",deref,fp);
            break;

        case eq_slot:
            generateBinarySlotCall(od,"==",deref,fp);
            break;

        case ne_slot:
            generateBinarySlotCall(od,"!=",deref,fp);
            break;

        case gt_slot:
            generateBinarySlotCall(od,">",deref,fp);
            break;

        case ge_slot:
            generateBinarySlotCall(od,">=",deref,fp);
            break;

        case neg_slot:
            prcode(fp, "-(*sipCpp)");
            break;

        case pos_slot:
            prcode(fp, "+(*sipCpp)");
            break;

        case cmp_slot:
            prcode(fp,"if ");
            generateBinarySlotCall(od,"<",deref,fp);
            prcode(fp,"\n"
"                sipRes = -1;\n"
"            else if ");
            generateBinarySlotCall(od,">",deref,fp);
            prcode(fp,"\n"
"                sipRes = 1;\n"
"            else\n"
"                sipRes = 0");

            break;
        }

        if (needsNew && !generating_c)
            prcode(fp,")");

        prcode(fp,";\n"
            );

        generateCatch(od->exceptions, &od->pysig, fp);

        if (rgil)
            prcode(fp,
"            Py_END_ALLOW_THREADS\n"
                );
    }

    /* Handle /TransferThis/ for non-factory methods. */
    if (!isFactory(od))
        for (a = 0; a < od->pysig.nrArgs; ++a)
        {
            argDef *ad = &od->pysig.args[a];

            if (!isInArg(ad))
                continue;

            if (isThisTransferred(ad))
            {
                prcode(fp,
"\n"
"            if (sipOwner)\n"
"                sipTransferTo(sipSelf, (PyObject *)sipOwner);\n"
"            else\n"
"                sipTransferBack(sipSelf);\n"
                );

                break;
            }
        }

    gc_ellipsis(&od->pysig, fp);

    if (deltemps)
        deleteTemps(&od->pysig, fp);

    prcode(fp,
"\n"
        );

    /* Handle sipIsErr if it was used. */
    if (error_flag)
        prcode(fp,
"            if (sipIsErr)\n"
"                return %s;\n"
"\n"
            ,((isVoidReturnSlot(od->common) || isIntReturnSlot(od->common) || isLongReturnSlot(od->common)) ? "-1" : "0"));

    /* Call any post-hook. */
    if (od->posthook != NULL)
        prcode(fp,
"\n"
"            sipCallHook(\"%s\");\n"
            ,od->posthook);

    if (isVoidReturnSlot(od->common))
        prcode(fp,
"            return 0;\n"
            );
    else if (isInplaceNumberSlot(od->common) || isInplaceSequenceSlot(od->common))
        prcode(fp,
"            Py_INCREF(sipSelf);\n"
"            return sipSelf;\n"
            );
    else if (isIntReturnSlot(od->common) || isLongReturnSlot(od->common))
        prcode(fp,
"            return sipRes;\n"
            );
    else
    {
        generateHandleResult(od,needsNew,(deltemps ? "return" : "sipResult ="),fp);

        /* Delete the temporaries now if we haven't already done so. */
        if (!deltemps)
        {
            deleteTemps(&od->pysig, fp);

            prcode(fp,
"\n"
"            return sipResult;\n"
                );
        }
    }

    prcode(fp,
"        }\n"
        );

    /* Restore the full type of the result. */
    *res = orig_res;
}


/*
 * Generate a call to a C++ function.
 */
static void generateCppFunctionCall(classDef *cd,classDef *ocd,overDef *od,
                    FILE *fp)
{
    char *mname = od->cppname;
    int parens = 1;

    /*
     * If the function is protected then call the public wrapper.  If it is
     * virtual then call explicit scoped function if "self" was passed as
     * the first argument.
     */

    if (cd == NULL)
        prcode(fp,"%s(",mname);
    else if (cd->iff->type == namespace_iface)
        prcode(fp,"%S::%s(",classFQCName(cd),mname);
    else if (isStatic(od))
    {
        if (isProtected(od))
            prcode(fp,"sip%C::sipProtect_%s(",classFQCName(cd),mname);
        else
            prcode(fp,"%S::%s(",classFQCName(ocd),mname);
    }
    else if (isProtected(od))
    {
        if (!isAbstract(od) && (isVirtual(od) || isVirtualReimp(od)))
        {
            prcode(fp, "sipCpp->sipProtectVirt_%s(sipSelfWasArg", mname);

            if (od->cppsig->nrArgs > 0)
                prcode(fp, ",");
        }
        else
            prcode(fp, "sipCpp->sipProtect_%s(", mname);
    }
    else if (!isAbstract(od) && (isVirtual(od) || isVirtualReimp(od)))
    {
        prcode(fp, "(sipSelfWasArg ? sipCpp->%U::%s(", ocd, mname);
        generateArgs(od->cppsig, Call, fp);
        prcode(fp, ") : sipCpp->%s(", mname);
        ++parens;
    }
    else
        prcode(fp, "sipCpp->%s(", mname);

    generateArgs(od->cppsig, Call, fp);

    while (parens--)
        prcode(fp, ")");
}


/*
 * Generate argument to a slot.
 */
static void generateSlotArg(signatureDef *sd, int argnr, FILE *fp)
{
    argDef *ad;
    int deref;

    ad = &sd->args[argnr];
    deref = ((ad->atype == class_type || ad->atype == mapped_type) && ad->nrderefs == 0);

    prcode(fp, "%sa%d", (deref ? "*" : ""), argnr);
}


/*
 * Generate the call to a binary (non-number) slot method.
 */
static void generateBinarySlotCall(overDef *od, char *op, int deref, FILE *fp)
{
    if (deref)
        prcode(fp, "((*sipCpp) %s ", op);
    else
        prcode(fp, "(sipCpp %s ", op);

    generateSlotArg(&od->pysig, 0, fp);
    prcode(fp, ")");
}


/*
 * Generate the call to a binary number slot method.
 */
static void generateNumberSlotCall(overDef *od, char *op, FILE *fp)
{
    prcode(fp, "(");
    generateSlotArg(&od->pysig, 0, fp);
    prcode(fp, " %s ", op);
    generateSlotArg(&od->pysig, 1, fp);
    prcode(fp, ")");
}


/*
 * Generate the argument variables for a member function/constructor/operator.
 */
static int generateArgParser(sipSpec *pt, signatureDef *sd, classDef *cd,
                 ctorDef *ct, overDef *od, int secCall, FILE *fp)
{
    int a, isQtSlot, optargs, arraylenarg, sigarg, handle_self;
    int slotconarg, slotdisarg, need_owner;

    /* If the class is just a namespace, then ignore it. */

    if (cd != NULL && cd->iff->type == namespace_iface)
        cd = NULL;

    handle_self = (od != NULL && od->common->slot == no_slot && !isStatic(od) && cd != NULL);

    /* Assume there isn't a Qt slot. */

    isQtSlot = FALSE;

    /*
     * Generate the local variables that will hold the parsed arguments and
     * values returned via arguments.
     */

    sigarg = -1;
    need_owner = FALSE;

    for (a = 0; a < sd->nrArgs; ++a)
    {
        argDef *ad = &sd->args[a];

        switch (ad->atype)
        {
        case signal_type:
            sigarg = a;
            break;

        case rxcon_type:
        case rxdis_type:
            isQtSlot = TRUE;
            break;

        case slotcon_type:
            slotconarg = a;
            break;

        case slotdis_type:
            slotdisarg = a;
            break;
        }

        if (isArraySize(ad))
            arraylenarg = a;

        generateVariable(ad,a,fp);

        if (isThisTransferred(ad))
            need_owner = TRUE;
    }

    if (od != NULL && need_owner)
        prcode(fp,
"        sipWrapper *sipOwner = 0;\n"
            );

    if (handle_self)
    {
        if (isProtected(od) && hasShadow(cd))
            prcode(fp,
"        sip%C *sipCpp;\n"
                ,classFQCName(cd));
        else
            prcode(fp,
"        %U *sipCpp;\n"
                ,cd);

        prcode(fp,
"\n"
            );
    }
    else if (sd->nrArgs != 0)
        prcode(fp,
"\n"
            );

    /* Generate the call to the parser function. */
    if (od != NULL && isNumberSlot(od->common))
        prcode(fp,
"        if (sipParsePair(%ssipArgsParsed,sipArg0,sipArg1,\"", (ct != NULL ? "" : "&"));
    else
        prcode(fp,
"        if (sipParseArgs(%ssipArgsParsed,sipArg%s,\"", (ct != NULL ? "" : "&"), (od == NULL || od->common->slot == no_slot || isMultiArgSlot(od->common)) ? "s" : "");

    /* Generate the format string. */
    optargs = FALSE;

    if (handle_self)
        prcode(fp,"%c",(isProtected(od) ? 'p' : 'B'));
    else if (isQtSlot && od == NULL)
        prcode(fp,"C");

    for (a = 0; a < sd->nrArgs; ++a)
    {
        char *fmt = "";
        argDef *ad = &sd->args[a];

        if (!isInArg(ad))
            continue;

        if (ad->defval != NULL && !optargs)
        {
            prcode(fp,"|");
            optargs = TRUE;
        }

        switch (ad->atype)
        {
        case sstring_type:
        case ustring_type:
        case string_type:
            if (ad->nrderefs == 0 || (isOutArg(ad) && ad->nrderefs == 1))
                fmt = "c";
            else if (isArray(ad))
                fmt = "a";
            else
                fmt = "s";

            break;

        case wstring_type:
            if (ad->nrderefs == 0 || (isOutArg(ad) && ad->nrderefs == 1))
                fmt = "w";
            else if (isArray(ad))
                fmt = "A";
            else
                fmt = "x";

            break;

        case enum_type:
            fmt = (ad->u.ed->fqcname != NULL) ? "E" : "e";
            break;

        case bool_type:
            fmt = "b";
            break;

        case cbool_type:
            fmt = "Xb";
            break;

        case int_type:
            if (!isArraySize(ad))
                fmt = "i";

            break;

        case uint_type:
            if (!isArraySize(ad))
                fmt = "u";

            break;

        case cint_type:
            fmt = "Xi";
            break;

        case short_type:
            if (!isArraySize(ad))
                fmt = "h";

            break;

        case ushort_type:
            if (!isArraySize(ad))
                fmt = "t";

            break;

        case long_type:
            if (!isArraySize(ad))
                fmt = "l";

            break;

        case ulong_type:
            if (!isArraySize(ad))
                fmt = "m";

            break;

        case longlong_type:
            if (!isArraySize(ad))
                fmt = "n";

            break;

        case ulonglong_type:
            if (!isArraySize(ad))
                fmt = "o";

            break;

        case struct_type:
        case void_type:
            fmt = "v";
            break;

        case float_type:
            fmt = "f";
            break;

        case cfloat_type:
            fmt = "Xf";
            break;

        case double_type:
            fmt = "d";
            break;

        case cdouble_type:
            fmt = "Xd";
            break;

        case signal_type:
            fmt = "G";
            break;

        case slot_type:
            fmt = "S";
            break;

        case anyslot_type:
            fmt = "U";
            break;

        case slotcon_type:
        case slotdis_type:
            fmt = (secCall ? "" : "S");
            break;

        case rxcon_type:
            fmt = (secCall ? "y" : "q");
            break;

        case rxdis_type:
            fmt = (secCall ? "Y" : "Q");
            break;

        case mapped_type:
            fmt = getSubFormatChar('M',ad);
            break;

        case class_type:
            fmt = getSubFormatChar('J', ad);
            break;

        case pyobject_type:
            fmt = getSubFormatChar('P',ad);
            break;

        case pytuple_type:
        case pylist_type:
        case pydict_type:
        case pyslice_type:
        case pytype_type:
            fmt = (isAllowNone(ad) ? "N" : "T");
            break;

        case pycallable_type:
            fmt = (isAllowNone(ad) ? "H" : "F");
            break;

        case qobject_type:
            fmt = "R";
            break;

        case ellipsis_type:
            fmt = "W";
            break;
        }

        prcode(fp,fmt);
    }

    prcode(fp,"\"");

    /* Generate the parameters corresponding to the format string. */

    if (handle_self)
        prcode(fp,",&sipSelf,sipClass_%C,&sipCpp",classFQCName(cd));
    else if (isQtSlot && od == NULL)
        prcode(fp,",sipSelf");

    for (a = 0; a < sd->nrArgs; ++a)
    {
        argDef *ad = &sd->args[a];

        if (!isInArg(ad))
            continue;

        switch (ad->atype)
        {
        case mapped_type:
            prcode(fp,",sipMappedType_%T,&a%d,&a%dState",ad,a,a);
            break;

        case class_type:
            prcode(fp, ",sipClass_%T,&a%d", ad, a);

            if (isThisTransferred(ad))
                prcode(fp, ",%ssipOwner", (ct != NULL ? "" : "&"));
            else if (isGetWrapper(ad))
                prcode(fp, ",&a%dWrapper", a);

            if (ad->u.cd->convtocode != NULL && !isConstrained(ad))
                prcode(fp, ",&a%dState", a);

            break;

        case rxcon_type:
            {
                if (sigarg > 0)
                    prcode(fp,",a%d",sigarg);
                else
                {
                    prcode(fp,",\"(");

                    generateArgs(sd->args[slotconarg].u.sa,Declaration,fp);

                    prcode(fp,")\"");
                }

                prcode(fp,",&a%d,&a%d",a,slotconarg);

                break;
            }

        case rxdis_type:
            {
                prcode(fp,",\"(");

                generateArgs(sd->args[slotdisarg].u.sa,Declaration,fp);

                prcode(fp,")\",&a%d,&a%d",a,slotdisarg);

                break;
            }

        case slotcon_type:
        case slotdis_type:
            if (!secCall)
                prcode(fp,",&a%d",a);

            break;

        case anyslot_type:
            prcode(fp, ",&a%dName,&a%dCallable", a, a);
            break;

        case pytuple_type:
            prcode(fp,",&PyTuple_Type,&a%d",a);
            break;

        case pylist_type:
            prcode(fp,",&PyList_Type,&a%d",a);
            break;

        case pydict_type:
            prcode(fp,",&PyDict_Type,&a%d",a);
            break;

        case pyslice_type:
            prcode(fp,",&PySlice_Type,&a%d",a);
            break;

        case pytype_type:
            prcode(fp,",&PyType_Type,&a%d",a);
            break;

        case enum_type:
            if (ad->u.ed->fqcname != NULL)
                prcode(fp,",sipEnum_%C",ad->u.ed->fqcname);

            prcode(fp,",&a%d",a);
            break;

        default:
            if (!isArraySize(ad))
                prcode(fp,",&a%d",a);

            if (isArray(ad))
                prcode(fp,",&a%d",arraylenarg);
        }
    }

    prcode(fp,"))\n");

    return isQtSlot;
}


/*
 * Get the format character string for something that has sub-formats.
 */

static char *getSubFormatChar(char fc,argDef *ad)
{
    static char fmt[3];
    char flags;

    fmt[0] = fc;

    flags = 0;

    if (fc != 'P' && ad->nrderefs == 0)
        flags |= 0x01;

    if (isTransferred(ad))
        flags |= 0x02;

    if (isTransferredBack(ad))
        flags |= 0x04;

    if (fc == 'J')
    {
        if (isThisTransferred(ad))
            flags |= 0x20;

        if (isGetWrapper(ad))
            flags |= 0x08;

        if (ad->u.cd->convtocode == NULL || isConstrained(ad))
            flags |= 0x10;
    }

    fmt[1] = '0' + flags;

    fmt[2] = '\0';

    return fmt;
}


/*
 * Return TRUE if a type has %ConvertToTypeCode.
 */
static int hasConvertToCode(argDef *ad)
{
    codeBlock *convtocode;

    if (ad->atype == class_type && !isConstrained(ad))
        convtocode = ad->u.cd->convtocode;
    else if (ad->atype == mapped_type)
        convtocode = ad->u.mtd->convtocode;
    else
        convtocode = NULL;

    return (convtocode != NULL);
}


/*
 * Garbage collect any ellipsis argument.
 */
static void gc_ellipsis(signatureDef *sd, FILE *fp)
{
    if (sd->nrArgs > 0 && sd->args[sd->nrArgs - 1].atype == ellipsis_type)
        prcode(fp,
"\n"
"            Py_DECREF(a%d);\n"
            , sd->nrArgs - 1);
}


/*
 * Delete any temporary variables on the heap created by type convertors.
 */
static void deleteTemps(signatureDef *sd, FILE *fp)
{
    int a, first = TRUE;

    for (a = 0; a < sd->nrArgs; ++a)
    {
        argDef *ad = &sd->args[a];

        if (!isInArg(ad))
            continue;

        if (ad->atype == wstring_type && ad->nrderefs == 1)
        {
            if (generating_c || !isConstArg(ad))
                prcode(fp,
"            sipFree(a%d);\n"
                    , a);
            else
                prcode(fp,
"            sipFree(const_cast<wchar_t *>(a%d));\n"
                    , a);
        }
        else if (hasConvertToCode(ad))
        {
            const char *fstr, *sstr;

            if (ad->atype == mapped_type)
                fstr = sstr = "MappedType";
            else
            {
                fstr = "Instance";
                sstr = "Class";
            }

            if (first)
            {
                prcode(fp,
"\n"
                    );

                first = FALSE;
            }

            if (generating_c || !isConstArg(ad))
                prcode(fp,
"            sipRelease%s(a%d,sip%s_%T,a%dState);\n"
                    , fstr, a, sstr, ad, a);
            else
                prcode(fp,
"            sipRelease%s(const_cast<%b *>(a%d),sip%s_%T,a%dState);\n"
                    , fstr, ad, a, sstr, ad, a);
        }
    }
}


/*
 * Generate a C++ code block.
 */
static void generateCppCodeBlock(codeBlock *code,FILE *fp)
{
    int reset_line = FALSE;
    codeBlock *cb;

    for (cb = code; cb != NULL; cb = cb->next)
    {
        char *cp;

        /*
         * Fragmented fragments (possibly created when applying
         * template types) don't have a filename.
         */
        if ((cp = cb->filename) != NULL)
        {
            reset_line = TRUE;

            prcode(fp,
"#line %d \"", cb->linenr);

            while (*cp != '\0')
            {
                prcode(fp, "%c", *cp);

                if (*cp == '\\')
                    prcode(fp, "\\");

                ++cp;
            }

            prcode(fp, "\"\n"
                );
        }

        prcode(fp, "%s", cb->frag);
    }

    if (reset_line)
    {
        char *bn;

        /* Just use the base name. */

        if ((bn = strrchr(currentFileName,'/')) != NULL)
            ++bn;
        else
            bn = currentFileName;

        prcode(fp,
"#line %d \"%s\"\n"
            ,currentLineNr + 1,bn);
    }
}


/*
 * Create a source file.
 */
static FILE *createCompilationUnit(sipSpec *pt, char *fname, char *description)
{
    FILE *fp = createFile(pt, fname, description);

    if (fp != NULL)
        generateCppCodeBlock(pt->unitcode, fp);

    return fp;
}


/*
 * Create a file with an optional standard header.
 */
static FILE *createFile(sipSpec *pt,char *fname,char *description)
{
    FILE *fp;

    /* Create the file. */

    if ((fp = fopen(fname,"w")) == NULL)
        fatal("Unable to create file \"%s\"\n",fname);

    /* The "stack" doesn't have to be very deep. */
    previousLineNr = currentLineNr;
    currentLineNr = 1;
    previousFileName = currentFileName;
    currentFileName = fname;

    if (description != NULL)
    {
        int needComment;
        codeBlock *cb;
        time_t now;

        /* Write the header. */
        now = time(NULL);

        prcode(fp,
"/*\n"
" * %s\n"
" *\n"
" * Generated by SIP %s on %s"
            ,description
            ,sipVersion,ctime(&now));

        if (pt->copying != NULL)
            prcode(fp,
" *\n"
                );

        needComment = TRUE;

        for (cb = pt->copying; cb != NULL; cb = cb->next)
        {
            char *cp;

            for (cp = cb->frag; *cp != '\0'; ++cp)
            {
                if (needComment)
                {
                    needComment = FALSE;
                    prcode(fp," * ");
                }

                prcode(fp,"%c",*cp);

                if (*cp == '\n')
                    needComment = TRUE;
            }
        }

        prcode(fp,
" */\n"
            );
    }

    return fp;
}


/*
 * Close a file and report any errors.
 */
static void closeFile(FILE *fp)
{
    if (ferror(fp))
        fatal("Error writing to \"%s\"\n",currentFileName);

    if (fclose(fp))
        fatal("Error closing \"%s\"\n",currentFileName);

    currentLineNr = previousLineNr;
    currentFileName = previousFileName;
}


/*
 * Print formatted code.
 */
void prcode(FILE *fp, const char *fmt, ...)
{
    char ch;
    va_list ap;

    prcode_last = fmt;

    va_start(ap,fmt);

    while ((ch = *fmt++) != '\0')
        if (ch == '%')
        {
            ch = *fmt++;

            switch (ch)
            {
            case 'c':
                {
                    char c = (char)va_arg(ap,int);

                    if (c == '\n')
                        ++currentLineNr;

                    fputc(c,fp);
                    break;
                }

            case 's':
                {
                    const char *cp = va_arg(ap,const char *);

                    while (*cp != '\0')
                    {
                        if (*cp == '\n')
                            ++currentLineNr;

                        fputc(*cp,fp);
                        ++cp;
                    }

                    break;
                }

            case 'l':
                fprintf(fp,"%ld",va_arg(ap,long));
                break;

            case 'u':
                fprintf(fp,"%u",va_arg(ap,unsigned));
                break;

            case 'd':
                fprintf(fp,"%d",va_arg(ap,int));
                break;

            case 'g':
                fprintf(fp,"%g",va_arg(ap,double));
                break;

            case 'x':
                fprintf(fp,"0x%08x",va_arg(ap,unsigned));
                break;

            case '\0':
                fputc('%',fp);
                --fmt;
                break;

            case '\n':
                fputc('\n',fp);
                ++currentLineNr;
                break;

            case 'b':
                {
                    argDef *ad, orig;

                    ad = va_arg(ap,argDef *);
                    orig = *ad;

                    resetIsConstArg(ad);
                    resetIsReference(ad);
                    ad->nrderefs = 0;

                    generateBaseType(ad,fp);

                    *ad = orig;

                    break;
                }

            case 'M':
                prcode_xml = !prcode_xml;
                break;

            case 'B':
                generateBaseType(va_arg(ap,argDef *),fp);
                break;

            case 'T':
                prTypeName(fp,va_arg(ap,argDef *),FALSE);
                break;

            case 'I':
                {
                    int indent = va_arg(ap,int);

                    while (indent-- > 0)
                        fputc('\t',fp);

                    break;
                }

            case 'N':
                {
                    nameDef *nd = va_arg(ap,nameDef *);

                    fprintf(fp,"sipNm_%s_%s",nd->module->name,nd->text);
                    break;
                }

            case 'E':
                {
                    enumDef *ed = va_arg(ap,enumDef *);

                    if (ed->fqcname == NULL)
                        fprintf(fp,"int");
                    else if (isProtectedEnum(ed))
                    {
                        fprintf(fp,"sip");
                        prScopedName(fp,classFQCName(ed->pcd),"_");

                        fprintf(fp,"::sip%s",scopedNameTail(ed->fqcname));
                    }
                    else
                        prScopedName(fp,ed->fqcname,"::");

                    break;
                }

            case 'F':
                prScopedName(fp,va_arg(ap,scopedNameDef *),"");
                break;

            case 'C':
                prScopedName(fp,va_arg(ap,scopedNameDef *),"_");
                break;

            case 'S':
                if (generating_c)
                    fprintf(fp,"struct ");

                prScopedName(fp,va_arg(ap,scopedNameDef *),"::");
                break;

            case 'U':
                if (generating_c)
                    fprintf(fp,"struct ");

                prScopedClassName(fp,va_arg(ap,classDef *),"::");
                break;

            case 'O':
                prOverloadName(fp, va_arg(ap, overDef *));
                break;

            case 'P':
                {
                    classDef *ecd = va_arg(ap, classDef *);
                    const char *pyname = va_arg(ap, const char *);

                    prScopedPythonName(fp, ecd, pyname);
                    break;
                }

            case 'X':
                generateThrowSpecifier(va_arg(ap,throwArgs *),fp);
                break;

            default:
                fputc(ch,fp);
            }
        }
        else if (ch == '\n')
        {
            fputc('\n',fp);
            ++currentLineNr;
        }
        else
            fputc(ch,fp);

    va_end(ap);
}


/*
 * Generate the C++ name of an overloaded function.
 */
void prOverloadName(FILE *fp, overDef *od)
{
    char *pt1, *pt2;

    pt1 = "operator";

    switch (od->common->slot)
    {
    case add_slot:
        pt2 = "+";
        break;

    case sub_slot:
        pt2 = "-";
        break;

    case mul_slot:
        pt2 = "*";
        break;

    case div_slot:
        pt2 = "/";
        break;

    case mod_slot:
        pt2 = "%";
        break;

    case and_slot:
        pt2 = "&";
        break;

    case or_slot:
        pt2 = "|";
        break;

    case xor_slot:
        pt2 = "^";
        break;

    case lshift_slot:
        pt2 = "<<";
        break;

    case rshift_slot:
        pt2 = ">>";
        break;

    case iadd_slot:
        pt2 = "+=";
        break;

    case isub_slot:
        pt2 = "-=";
        break;

    case imul_slot:
        pt2 = "*=";
        break;

    case idiv_slot:
        pt2 = "/=";
        break;

    case imod_slot:
        pt2 = "%=";
        break;

    case iand_slot:
        pt2 = "&=";
        break;

    case ior_slot:
        pt2 = "|=";
        break;

    case ixor_slot:
        pt2 = "^=";
        break;

    case ilshift_slot:
        pt2 = "<<=";
        break;

    case irshift_slot:
        pt2 = ">>=";
        break;

    case invert_slot:
        pt2 = "~";
        break;

    case call_slot:
        pt2 = "()";
        break;

    case getitem_slot:
        pt2 = "[]";
        break;

    case lt_slot:
        pt2 = "<";
        break;

    case le_slot:
        pt2 = "<=";
        break;

    case eq_slot:
        pt2 = "==";
        break;

    case ne_slot:
        pt2 = "!=";
        break;

    case gt_slot:
        pt2 = ">";
        break;

    case ge_slot:
        pt2 = ">=";
        break;

    default:
        pt1 = "";
        pt2 = od->cppname;
    }

    fprintf(fp, "%s%s", pt1, pt2);
}


/*
 * Generate a scoped name with the given separator string.
 */
static void prScopedName(FILE *fp,scopedNameDef *snd,char *sep)
{
    while (snd != NULL)
    {
        fprintf(fp,"%s",snd->name);

        if ((snd = snd->next) != NULL)
            fprintf(fp,"%s",sep);
    }
}


/*
 * Generate a scoped class name with the given separator string.  At the moment
 * this provides (probably) broken support for protected classes.
 */
static void prScopedClassName(FILE *fp,classDef *cd,char *sep)
{
    scopedNameDef *snd = classFQCName(cd);

    while (snd != NULL)
    {
        if (isProtectedClass(cd))
            fprintf(fp,"sip");

        fprintf(fp,"%s",snd->name);

        if ((snd = snd->next) != NULL)
            fprintf(fp,"%s",sep);
    }
}


/*
 * Generate a scoped Python name.
 */
void prScopedPythonName(FILE *fp, classDef *scope, const char *pyname)
{
    if (scope != NULL)
    {
        prScopedPythonName(fp, scope->ecd, NULL);
        fprintf(fp, "%s.", scope->pyname);
    }

    if (pyname != NULL)
        fprintf(fp, "%s", pyname);
}


/*
 * Generate a type name to be used as part of an identifier name.
 */
static void prTypeName(FILE *fp,argDef *ad,int intmpl)
{
    if (intmpl)
    {
        char buf[10];
        int flgs;

        /* We use numbers so they don't conflict with names. */

        sprintf(buf,"%02d",ad->atype);

        flgs = 0;

        if (isConstArg(ad))
            flgs += 1;

        if (isReference(ad))
            flgs += 2;

        prcode(fp,"%s%d%d",buf,flgs,ad->nrderefs);
    }

    /* Now add a name if there is one associated with the type. */

    switch (ad->atype)
    {
    case struct_type:
        prcode(fp,"%C",ad->u.sname);
        break;

    case defined_type:
        prcode(fp,"%C",ad->u.snd);
        break;

    case enum_type:
        prcode(fp,"%C",ad->u.ed->fqcname);
        break;

    case mapped_type:
        prTypeName(fp,&ad->u.mtd->type,intmpl);
        break;

    case class_type:
        prcode(fp,"%C",classFQCName(ad->u.cd));
        break;

    case template_type:
        {
            int a;
            templateDef *td = ad->u.td;

            prcode(fp,"%C",td->fqname);

            for (a = 0; a < td->types.nrArgs; ++a)
            {
                prcode(fp,"_");
                prTypeName(fp,&td->types.args[a],TRUE);
            }

            break;
        }
    }
}


/*
 * Return TRUE if handwritten code uses the error flag.
 */
static int needErrorFlag(codeBlock *cb)
{
    return usedInCode(cb, "sipIsErr");
}


/*
 * Return TRUE if the argument type means an instance needs to be created on
 * the heap to pass back to Python.
 */
static int needNewInstance(argDef *ad)
{
    return ((ad->atype == mapped_type || ad->atype == class_type) &&
        ((isReference(ad) && ad->nrderefs == 0) || (!isReference(ad) && ad->nrderefs == 1)) &&
        !isInArg(ad) && isOutArg(ad));
}


/*
 * Reset and save any argument flags so that the signature will be rendered
 * exactly as defined in C++.
 */
static void normaliseArgs(signatureDef *sd)
{
    int a;
    argDef *ad = sd->args;

    for (a = 0; a < sd->nrArgs; ++a)
    {
        if (ad->atype == class_type && isProtectedClass(ad->u.cd))
        {
            resetIsProtectedClass(ad->u.cd);
            setWasProtectedClass(ad->u.cd);
        }
        else if (ad->atype == enum_type && isProtectedEnum(ad->u.ed))
        {
            resetIsProtectedEnum(ad->u.ed);
            setWasProtectedEnum(ad->u.ed);
        }

        ++ad;
    }
}


/*
 * Restore any argument flags modified by normaliseArgs().
 */
static void restoreArgs(signatureDef *sd)
{
    int a;
    argDef *ad = sd->args;

    for (a = 0; a < sd->nrArgs; ++a)
    {
        if (ad->atype == class_type && wasProtectedClass(ad->u.cd))
        {
            resetWasProtectedClass(ad->u.cd);
            setIsProtectedClass(ad->u.cd);
        }
        else if (ad->atype == enum_type && wasProtectedEnum(ad->u.ed))
        {
            resetWasProtectedEnum(ad->u.ed);
            setIsProtectedEnum(ad->u.ed);
        }

        ++ad;
    }
}


/*
 * Return TRUE if a dealloc function is needed for a class.
 */
static int needDealloc(classDef *cd)
{
    if (cd->iff->type == namespace_iface)
        return FALSE;

    /* All of these conditions cause some code to be generated. */

    if (tracing)
        return TRUE;

    if (generating_c)
        return TRUE;

    if (cd->dealloccode != NULL)
        return TRUE;

    if (isPublicDtor(cd))
        return TRUE;

    if (hasShadow(cd))
        return TRUE;

    return FALSE;
}


/*
 * Return the argument name to use in a function definition for handwritten
 * code.
 */
static const char *argName(const char *name, codeBlock *cb)
{
    static const char noname[] = "";

    /* Always use the name in C code. */
    if (generating_c)
        return name;

    /* Use the name if it is used in the handwritten code. */
    if (usedInCode(cb, name))
        return name;

    /* Don't use the name and avoid a compiler warning. */
    return noname;
}


/*
 * Returns TRUE if a string is used in a code block.
 */
static int usedInCode(codeBlock *code, const char *str)
{
    while (code != NULL)
    {
        if (strstr(code->frag, str) != NULL)
            return TRUE;

        code = code->next;
    }

    return FALSE;
}


/*
 * Generate an assignment statement from a void * variable to a class instance
 * variable.
 */
static void generateClassFromVoid(classDef *cd, const char *cname,
        const char *vname, FILE *fp)
{
    if (generating_c)
        prcode(fp, "%S *%s = (%S *)%s", classFQCName(cd), cname, classFQCName(cd), vname);
    else
        prcode(fp, "%S *%s = reinterpret_cast<%S *>(%s)", classFQCName(cd), cname, classFQCName(cd), vname);
}


/*
 * Generate an assignment statement from a void * variable to a mapped type
 * variable.
 */
static void generateMappedTypeFromVoid(mappedTypeDef *mtd, const char *cname,
        const char *vname, FILE *fp)
{
    if (generating_c)
        prcode(fp, "%b *%s = (%b *)%s", &mtd->type, cname, &mtd->type, vname);
    else
        prcode(fp, "%b *%s = reinterpret_cast<%b *>(%s)", &mtd->type, cname, &mtd->type, vname);
}
