file modules/classparse.py

[No description available]

Namespaces

Name
modules
modules::classparse

Source code

############################
#                          #
#  Class parsing for BOSS  #
#                          #
############################

from __future__ import print_function
from collections import OrderedDict
import os

import modules.active_cfg as active_cfg
exec("import configs." + active_cfg.module_name + " as cfg")
import modules.gb as gb
import modules.utils as utils
import modules.classutils as classutils
import modules.funcutils as funcutils
import modules.infomsg as infomsg

#
# Module-level globals
#

template_done   = []
templ_spec_done = []
added_parent    = []

includes = OrderedDict()


# ====== run ========

# Main function for parsing classes

def run():

    # Clear the module-level dict that keeps track of include statements
    includes.clear()

    #
    # Loop over all classes 
    #
    
    for class_name_long, class_el in gb.loaded_classes_in_xml.items():

        # Clear all info messages
        infomsg.clearInfoMessages()

        # Generate dicts with different variations of the class name
        class_name       = classutils.getClassNameDict(class_el)
        abstr_class_name = classutils.getClassNameDict(class_el, abstract=True)

        # Print current class
        print()
        print('  ' + utils.modifyText('Class:','underline') + ' ' + class_name['long_templ'])

        # Check if this is a template class
        is_template = utils.isTemplateClass(class_el)


        # Make list of all types used in this class
        all_types_in_class = utils.getAllTypesInClass(class_el, include_parents=True)

        # Set a bunch of generally useful variables 
        original_class_file_el   = gb.id_dict[class_el.get('file')]
        original_file_name       = original_class_file_el.get('name')
        original_file_name_base  = os.path.basename(original_file_name)
        original_class_file_dir  = os.path.split(original_file_name)[0]
        extras_src_file_name     = os.path.join(gb.boss_output_dir, gb.general_src_file_prefix + class_name['short'] + cfg.source_extension)

        short_abstr_class_fname  = gb.new_header_files[class_name['long']]['abstract']
        abstr_class_fname        = os.path.join(gb.boss_output_dir, short_abstr_class_fname)

        # namespaces    = class_name['long'].split('::')[:-1]
        namespaces    = utils.getNamespaces(class_el, include_self=False)
        has_namespace = bool(len(namespaces))

        has_copy_constructor, copy_constructor_id         = classutils.checkCopyConstructor(class_el, return_id=True)
        has_assignment_operator, assignment_is_artificial = classutils.checkAssignmentOperator(class_el)
        
        if has_assignment_operator or assignment_is_artificial:
            construct_assignment_operator = True
        else:
            construct_assignment_operator = False


        # Register paths of original files in global dict
        gb.original_file_paths[original_file_name_base] = original_file_name


        # Read content of original class file
        f = open(original_file_name, 'r')
        original_file_content = f.read()
        f.close()
        original_file_content_nocomments = utils.removeComments(original_file_content, insert_blanks=True)


        # Prepare entries in gb.new_code and includes
        if abstr_class_fname not in gb.new_code.keys():
            gb.new_code[abstr_class_fname] = {'code_tuples':[], 'add_include_guard':True}
            gb.new_code[abstr_class_fname + '.FOR_GAMBIT'] = {'code_tuples':[], 'add_include_guard':True}
        if original_file_name not in gb.new_code.keys():
            gb.new_code[original_file_name] = {'code_tuples':[], 'add_include_guard':False}
        if original_file_name not in includes.keys():
            includes[original_file_name] = []
        if extras_src_file_name not in gb.new_code.keys():
            gb.new_code[extras_src_file_name] = {'code_tuples':[], 'add_include_guard':False}


        # Treat the first specialization of a template class differently
        if is_template and class_name['long'] not in template_done:
            template_bracket, template_types = utils.getTemplateBracket(class_el)
            
            empty_templ_class_decl = ''
            empty_templ_class_decl += classutils.constrEmptyTemplClassDecl(abstr_class_name['short'], namespaces, template_bracket, indent=cfg.indent)
            empty_templ_class_decl += classutils.constrTemplForwDecl(class_name['short'], namespaces, template_bracket, indent=cfg.indent)

            gb.new_code[abstr_class_fname]['code_tuples'].append( (0, empty_templ_class_decl) )


        # Get template arguments for specialization, 
        # and check that they are acceptable
        if is_template and class_name['long'] not in templ_spec_done:
            spec_template_types = utils.getSpecTemplateTypes(class_el)
            for template_type in spec_template_types:
                if (template_type not in gb.accepted_types):
                    raise Exception("The template specialization type '" + template_type + "' for class " + class_name['long'] + " is not among accepted types.")


        #
        # For the backend: Construct code for the abstract class header file and register it
        #
        
        constrAbstractClassHeaderCode(class_el, class_name, abstr_class_name, namespaces, is_template, 
                                      has_copy_constructor, construct_assignment_operator, abstr_class_fname, file_for_gambit=False)

        #
        # For GAMBIT: Construct code for the abstract class header file and register it
        #
        
        constrAbstractClassHeaderCode(class_el, class_name, abstr_class_name, namespaces, is_template, 
                                      has_copy_constructor, construct_assignment_operator, abstr_class_fname, file_for_gambit=True)



        #
        # Add abstract class to inheritance list of original class
        #

        addAbsClassToInheritanceList(class_el, class_name, abstr_class_name, is_template,
                                     original_file_name, original_file_content_nocomments)
        

        #
        # Generate code for #include statements in orginal header/source file 
        #

        addIncludesToOriginalClassFile(class_el, namespaces, is_template, original_file_name, 
                                       original_file_content_nocomments, original_file_content,
                                       short_abstr_class_fname)



        #
        # Generate additional member functions in the original class:
        # - Abstract class versions of member functions that make use of loaded types.
        # - Extra versions of functions that use default value arguments.
        # - Functions for returning references to public member variables. 
        # Declarations go in the original class header while implementations go in a separate source file.
        #

        generateClassMemberInterface(class_el, class_name, abstr_class_name, namespaces,
                                     original_file_name, original_file_content_nocomments, 
                                     original_class_file_el, extras_src_file_name,
                                     has_copy_constructor, construct_assignment_operator)
        

        #
        # Generate factory functions source file
        #

        generateFactoryFunctions(class_el, class_name, is_template)


        #
        # Generate a header containing the GAMBIT wrapper class
        #

        generateWrapperHeader(class_el, class_name, abstr_class_name, namespaces, short_abstr_class_fname,
                              construct_assignment_operator, has_copy_constructor, copy_constructor_id)


        #
        # Construct utility functions for dealing with pointer-to-wrapper from Abstract class.
        # ('wrapper_creator', 'wrapper_deleter', 'set_delete_BEptr')
        #

        constrWrapperUtils(class_name)


        #
        # Add typedef to 'abstracttypedefs.hpp'
        #

        addAbstractTypedefs(abstr_class_name, namespaces)


        #
        # Add typedef to 'wrappertypdefs.hpp'
        #

        addWrapperTypedefs(class_name, namespaces)


        #
        # Add include guards to the original headers
        #
        gb.new_code[original_file_name]['add_include_guard'] = True
        gb.new_code[original_file_name]['include_guard_prefix'] = 'boss'

        #
        # Keep track of classes done
        #

        gb.classes_done.append(class_name)
        if is_template: 
            if class_name['long'] not in template_done:
                template_done.append(class_name['long'])
            if class_name['long'] not in templ_spec_done:
                templ_spec_done.append(class_name['long'])
        

        print()

    #
    # END: Loop over all classes in gb.loaded_classes_in_xml
    #    

# ====== END: run ========



# ====== constrAbstractClassHeaderCode ========

# Construct code for the abstract class header file and register it

def constrAbstractClassHeaderCode(class_el, class_name, abstr_class_name, namespaces, is_template,
                                  has_copy_constructor, construct_assignment_operator, abstr_class_fname, file_for_gambit=False):

    if file_for_gambit:
        abstr_class_fname = abstr_class_fname + '.FOR_GAMBIT'

    class_decl = ''

    # Add include statements
    include_statements  = []
    include_statements += ['#include <cstddef>']
    if gb.debug_mode or file_for_gambit: 
        include_statements += ['#include <iostream>']
    include_statements += ['#include "' + os.path.join(gb.gambit_backend_incl_dir, 'abstractbase.hpp') + '"']
    include_statements += ['#include "' + gb.frwd_decls_abs_fname + cfg.header_extension + '"']
    include_statements += ['#include "' + gb.frwd_decls_wrp_fname + cfg.header_extension + '"']
    include_statements += utils.getIncludeStatements(class_el, convert_loaded_to='wrapper_decl', exclude_types=[class_name], include_parents=True, use_full_path=False, forward_declared='include')

    include_statements = utils.orderIncludeStatements(include_statements)
    include_statements_code = '\n'.join(include_statements) + 2*'\n'
    class_decl += include_statements_code

    # # Add include statement for the enum declaration header. Put this inside a #ifndef ... #endif block
    # # to avoid multiple declaration when the abstract class header is included from the original class header. 
    # enum_include_statement_code  = ''
    # enum_include_statement_code += '#ifndef ENUMS_DECLARED\n'
    # enum_include_statement_code += '#define ENUMS_DECLARED\n'
    # enum_include_statement_code += '#include "' + gb.enum_decls_wrp_fname + cfg.header_extension + '"\n'
    # # enum_include_statement_code += '#include "' + os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, gb.enum_decls_wrp_fname + cfg.header_extension) + '"\n'
    # enum_include_statement_code += '#endif\n'
    # enum_include_statement_code += '\n'
    # class_decl += enum_include_statement_code

    # Add the the code for the abstract class
    if (is_template == True) and (class_name['long'] in templ_spec_done):
        pass
    elif (is_template == True) and (class_name['long'] not in templ_spec_done):
        class_decl += classutils.constrAbstractClassDecl(class_el, class_name, abstr_class_name, namespaces, 
                                                         indent=cfg.indent, file_for_gambit=file_for_gambit, 
                                                         template_types=spec_template_types, 
                                                         has_copy_constructor=has_copy_constructor,
                                                         construct_assignment_operator=construct_assignment_operator)
        class_decl += '\n'
    else:
        class_decl += classutils.constrAbstractClassDecl(class_el, class_name, abstr_class_name, namespaces, 
                                                         indent=cfg.indent, file_for_gambit=file_for_gambit,
                                                         has_copy_constructor=has_copy_constructor,
                                                         construct_assignment_operator=construct_assignment_operator)
        class_decl += '\n'

    # - Register code
    gb.new_code[abstr_class_fname]['code_tuples'].append( (-1, class_decl) )

# ====== END: constrAbstractClassHeaderCode ========



# ====== addAbsClassToInheritanceList ========

# Add abstract class to inheritance list of original class

def addAbsClassToInheritanceList(class_el, class_name, abstr_class_name, is_template,
                                 original_file_name, original_file_content_nocomments):

    # Find positions in the original file
    line_number    = int(class_el.get('line'))
    class_name_pos = classutils.findClassNamePosition(class_el, original_file_content_nocomments)
    newline_pos    = utils.findNewLinePos(original_file_content_nocomments, line_number)


    # Special preparations for template classes:
    if is_template:
    
        # - Determine whether this is the source for the general template 
        #   or for a specialization (look for '<' after class name)
        temp_pos = class_name_pos + len(class_name['short'])
        while True:
            next_char = original_file_content_nocomments[temp_pos]
            if next_char not in [' ', '\t', '\n']:
                break
            else:
                temp_pos += 1
        if next_char == '<':
            src_is_specialization = True
        else:
            src_is_specialization = False

        # - Prepare the template bracket string
        if src_is_specialization:
            add_template_bracket = '<' + ','.join(spec_template_types) + '>'
        else:
            add_template_bracket = '<' + ','.join(template_types) + '>'


    # If no previous parent classes:
    if ('bases' not in class_el.keys()) and (class_name['long'] not in added_parent):

        # - Calculate insert position
        insert_pos = class_name_pos + len(class_name['short'])
        if is_template and src_is_specialization:
            insert_pos += len(add_template_bracket)

        # - Generate code
        add_code = ' : public virtual ' + abstr_class_name['short']
        if is_template == True:
            add_code += add_template_bracket

    # If there are previous parent classes
    else:

        # - Get colon position
        if is_template and src_is_specialization:
            temp_pos = class_name_pos + len(class_name['short']) + len(add_template_bracket)
        else:
            temp_pos = class_name_pos + len(class_name['short'])
        colon_pos = temp_pos + original_file_content_nocomments[temp_pos:newline_pos].find(':')

        # - Calculate insert position
        insert_pos = colon_pos + 1

        # - Generate code
        add_code = ' public virtual ' + abstr_class_name['short']
        if is_template == True:
            add_code += add_template_bracket
        add_code += ','

    # - Register new code
    gb.new_code[original_file_name]['code_tuples'].append( (insert_pos, add_code) )

    # - Update added_parent dict
    added_parent.append(class_name['long'])

# ====== END: addAbsClassToInheritanceList ========



# ====== addIncludesToOriginalClassFile ========

# Generate code for #include statements in orginal header/source file

def addIncludesToOriginalClassFile(class_el, namespaces, is_template, original_file_name, 
                                   original_file_content_nocomments, original_file_content,
                                   short_abstr_class_fname):

    # Generate include statement for abstract class header
    include_line = '#include "' + os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, short_abstr_class_fname ) + '"'

    # Check that we haven't included that statement already
    if include_line in includes[original_file_name]:
        return

    # Check for namespace
    has_namespace = bool(len(namespaces))

    # Find class name position in the original file
    class_name_pos = classutils.findClassNamePosition(class_el, original_file_content_nocomments)


    # Find insert position
    if is_template == True:
        insert_pos = original_file_content_nocomments[:class_name_pos].rfind('template')
    else:
        insert_pos = max(original_file_content_nocomments[:class_name_pos].rfind('class'), original_file_content_nocomments[:class_name_pos].rfind('struct'))
    # - Adjust for the indentation
    use_indent = ''
    while insert_pos > 0:
        char = original_file_content[insert_pos-1]
        if char in [' ','\t']:
            use_indent += char
            insert_pos -= 1
        else:
            break

    # Construct code
    include_code = ''
    include_code += use_indent

    for ns in namespaces:
        include_code += '} '

    include_code += '\n'*has_namespace
    include_code += use_indent + '#define ENUMS_DECLARED\n'
    include_code += use_indent + include_line + '\n'
    include_code += use_indent + '#include "' + os.path.join(gb.gambit_backend_incl_dir, gb.abstract_typedefs_fname + cfg.header_extension) +  '"\n'
    include_code += use_indent + '#include "' + os.path.join(gb.gambit_backend_incl_dir, gb.wrapper_typedefs_fname + cfg.header_extension) +  '"\n'
    include_code += use_indent

    for ns in namespaces:
        include_code += 'namespace ' + ns + ' { '

    include_code += '\n'*has_namespace

    # Register code
    gb.new_code[original_file_name]['code_tuples'].append( (insert_pos, include_code) )

    # Register include line
    includes[original_file_name].append(include_line)

# ====== END: addIncludesToOriginalClassFile ========



# ====== generateClassMemberInterface ========

# Generate additional member functions in the original class:
# - Abstract class versions of member functions that make use of loaded types.
# - Extra versions of functions that use default value arguments.
# - Functions for returning references to public member variables. 
# Declarations go in the original class header while implementations go in a separate source file.
    
def generateClassMemberInterface(class_el, class_name, abstr_class_name, namespaces,
                                 original_file_name, original_file_content_nocomments,
                                 original_class_file_el, extras_src_file_name,
                                 has_copy_constructor, construct_assignment_operator):

    # Find class name position in the original file
    class_name_pos = classutils.findClassNamePosition(class_el, original_file_content_nocomments)

    # Create lists of all public, 'non-artificial' members of the class
    member_methods   = []
    member_variables = []
    member_operators = []
    if 'members' in class_el.keys():
        for mem_id in class_el.get('members').split():
            el = gb.id_dict[mem_id]
            if not 'artificial' in el.keys():
                if el.get('access') == 'public':
                    if (el.tag == 'Method'): # and (not funcutils.ignoreFunction(el)):
                        member_methods.append(el)
                    elif (el.tag == 'OperatorMethod'): #and (not funcutils.ignoreFunction(el)):
                        if funcutils.usesNativeType(el):
                            member_operators.append(el)
                    elif (el.tag in ('Field', 'Variable')):
                        if classutils.isAcceptedMemberVariable(el):
                            member_variables.append(el)

    # Determine insert position
    rel_pos_start, rel_pos_end = utils.getBracketPositions(original_file_content_nocomments[class_name_pos:], delims=['{','}'])
    class_body_start = class_name_pos + rel_pos_start
    class_body_end   = class_name_pos + rel_pos_end
    insert_pos = class_body_end

    # Generate code for wrapper functions for each each member function.
    # A declaration goes into the original class header, 
    # while implementations are put in a new source file.

    declaration_code     = '\n'
    implementation_code  = '\n'
    current_access = None
    for method_el in member_methods:

        # We need to generate as many overloaded versions as there are arguments with default values
        n_overloads = funcutils.numberOfDefaultArgs(method_el)
        
        # Check for native types
        uses_native_type = funcutils.usesNativeType(method_el)

        # If no native types are used and no arguments have default values, we don't need a wrapper
        if (not uses_native_type) and (n_overloads == 0):
            continue

        # Generate wrapper code
        for remove_n_args in range(n_overloads+1):
            
            # Check that function is acceptable
            if funcutils.ignoreFunction(method_el, remove_n_args=remove_n_args):
                continue

            if (remove_n_args==0) and (not uses_native_type):
                continue

            # The declaration is put inside the original class
            method_access = method_el.get('access')
            if method_access != current_access:
                declaration_code += ' '*(len(namespaces)+1)*cfg.indent + method_access +':\n'
                current_access = method_access
            declaration_code += classutils.constrWrapperFunction(method_el, indent=cfg.indent, n_indents=len(namespaces)+2, 
                                                                 remove_n_args=remove_n_args, only_declaration=True)
            declaration_code += '\n'

            
            # The implementation goes into a new source file
            implementation_code += classutils.constrWrapperFunction(method_el, indent=cfg.indent, n_indents=0, 
                                                                    remove_n_args=remove_n_args, include_full_namespace=True)
            implementation_code += 2*'\n'

    # - Register code
    gb.new_code[original_file_name]['code_tuples'].append( (insert_pos, declaration_code) )            
    gb.new_code[extras_src_file_name]['code_tuples'].append( (-1, implementation_code) )            


    # Generate code for each member operator
    operator_declaration_code    = '\n'
    operator_implementation_code = '\n'
    for operator_el in member_operators:
        operator_access = operator_el.get('access')
        if operator_access != current_access:
            operator_declaration_code += ' '*(len(namespaces)+1)*cfg.indent + operator_access +':\n'
            current_access = operator_access

        # If default arguments are used, we need several overloads
        n_overloads = funcutils.numberOfDefaultArgs(operator_el)
        for remove_n_args in range(n_overloads+1):

            # Put declaration in original class
            operator_declaration_code += classutils.constrWrapperFunction(operator_el, indent=cfg.indent, n_indents=len(namespaces)+2, 
                                                                          remove_n_args=remove_n_args, only_declaration=True)
            operator_declaration_code += '\n'


            # Put implementation in a new source file
            operator_implementation_code += classutils.constrWrapperFunction(operator_el, indent=cfg.indent, n_indents=0, 
                                                                             remove_n_args=remove_n_args, include_full_namespace=True)
            operator_implementation_code += 2*'\n'


    # - Register code
    gb.new_code[original_file_name]['code_tuples'].append( (insert_pos, operator_declaration_code) )            
    gb.new_code[extras_src_file_name]['code_tuples'].append( (-1, operator_implementation_code) )            


    # Generate a reference-returning method for each (public) member variable:
    ref_func_declaration_code    = ''
    ref_func_implementation_code = ''
    if len(member_variables) > 0:
        n_indents = len(namespaces)
        ref_func_declaration_code += '\n'
        ref_func_declaration_code += ' '*cfg.indent*(n_indents+1) + 'public:\n'
        for var_el in member_variables:

            # Put declaration in original code
            ref_func_declaration_code += classutils.constrVariableRefFunction(var_el, virtual=False, indent=cfg.indent, n_indents=n_indents+2, 
                                                                              only_declaration=True, add_return_type_suffix=True)
            ref_func_declaration_code += '\n'

            # Put implementation in a new source file
            ref_func_implementation_code += classutils.constrVariableRefFunction(var_el, virtual=False, indent=cfg.indent, n_indents=0,
                                                                                 include_full_namespace=True, add_return_type_suffix=True) 
            ref_func_implementation_code += '\n'


    # - Register code
    if ref_func_declaration_code != '':
        gb.new_code[original_file_name]['code_tuples'].append( (insert_pos, ref_func_declaration_code) )            
        gb.new_code[extras_src_file_name]['code_tuples'].append( (-1, ref_func_implementation_code) )            



    # Generate pointer-based copy and assignment functions

    # If class contains pure virtual members, do not generate any factory functions
    if class_name['long_templ'] in gb.contains_pure_virtual_members:
        reason = "Contains pure virtual member functions."
        infomsg.NoPointerCopyAndAssignmentFunctions(class_name['long_templ'], reason).printMessage()
    else:

        n_indents = len(namespaces)
        ptr_declaration_code = '\n'
        ptr_implementation_code = '\n'

        if has_copy_constructor or construct_assignment_operator:
            ptr_declaration_code += ' '*cfg.indent*(n_indents+1) + 'public:\n'

        if has_copy_constructor:
            ptr_declaration_code += classutils.constrPtrCopyFunc(class_el, abstr_class_name['short'], class_name['short'], virtual=False, indent=cfg.indent, n_indents=n_indents+2, only_declaration=True)
            ptr_declaration_code += '\n'

        if construct_assignment_operator:
            ptr_declaration_code += ' '*cfg.indent*(n_indents+2) + 'using ' + abstr_class_name['short'] + '::pointer_assign' + gb.code_suffix + ';\n'
            ptr_declaration_code += classutils.constrPtrAssignFunc(class_el, abstr_class_name['short'], class_name['short'], virtual=False, indent=cfg.indent, n_indents=n_indents+2, only_declaration=True)
        
        ptr_implementation_code += '#include "' + os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full,'identification.hpp') + '"\n'
        ptr_implementation_code += '\n'
        if has_copy_constructor:
          ptr_implementation_code += classutils.constrPtrCopyFunc(class_el, abstr_class_name['short'], class_name['short'], virtual=False, indent=cfg.indent, n_indents=0, include_full_namespace=True)
          ptr_implementation_code += '\n'
        if construct_assignment_operator:
          ptr_implementation_code += classutils.constrPtrAssignFunc(class_el, abstr_class_name['short'], class_name['short'], virtual=False, indent=cfg.indent, n_indents=0, include_full_namespace=True)
          ptr_implementation_code += '\n'
        ptr_implementation_code += '#include "' + os.path.join(gb.gambit_backend_incl_dir, 'backend_undefs.hpp') + '"\n'

        # - Generate include statements for the new source file
        include_statements = []
        include_statements += utils.getIncludeStatements(class_el, convert_loaded_to='none', input_element='class', use_full_path=True, forward_declared='only')
        include_statements += utils.getIncludeStatements(class_el, convert_loaded_to='wrapper', input_element='class', use_full_path=True, forward_declared='exclude')
        include_statements.append('#include "' + os.path.join(gb.gambit_backend_incl_dir, gb.abstract_typedefs_fname + cfg.header_extension) + '"')
        include_statements.append('#include "' + os.path.join(gb.gambit_backend_incl_dir, gb.wrapper_typedefs_fname + cfg.header_extension) + '"')

        if utils.isHeader(original_class_file_el):
            use_path = utils.shortenHeaderPath(original_file_name)
            include_statements.append( '#include "' + use_path + '"')

        include_statements = list( OrderedDict.fromkeys(include_statements) )
        include_statements = utils.orderIncludeStatements(include_statements)
        include_statements_code = '\n'.join(include_statements) + '\n'

        # - Register the code
        gb.new_code[original_file_name]['code_tuples'].append( (insert_pos, ptr_declaration_code) )

        gb.new_code[extras_src_file_name]['code_tuples'].append( (0, include_statements_code) )            
        gb.new_code[extras_src_file_name]['code_tuples'].append( (-1, ptr_implementation_code) )


# ====== END: generateClassMemberInterface ========



# ====== generateFactoryFunctions ========

# Generate factory functions source file

def generateFactoryFunctions(class_el, class_name, is_template):

    # If class contains pure virtual members, do not generate any factory functions
    if class_name['long_templ'] in gb.contains_pure_virtual_members:
        reason = "Contains pure virtual member functions."
        infomsg.NoFactoryFunctions(class_name['long_templ'], reason).printMessage()
        return 

    # Generate factory file content
    factory_file_content  = ''

    if is_template:
        spec_template_types = utils.getSpecTemplateTypes(class_el)
        factory_file_content += classutils.constrFactoryFunctionCode(class_el, class_name, indent=cfg.indent, template_types=spec_template_types, skip_copy_constructors=True, use_wrapper_return=False, use_wrapper_args=True)
    else:
        factory_file_content += classutils.constrFactoryFunctionCode(class_el, class_name, indent=cfg.indent, skip_copy_constructors=True, use_wrapper_return=False, use_wrapper_args=True)
    factory_file_content += '\n'

    # If no file content has been generated (no public constructors), return without doing anything
    if factory_file_content.strip() == '':
        return

    # Generate factory file name
    dir_name = gb.boss_output_dir
    factory_file_name = os.path.join(dir_name, gb.factory_file_prefix + class_name['short'] + cfg.source_extension)

    # Register code
    if factory_file_name not in gb.new_code.keys():
        gb.new_code[factory_file_name] = {'code_tuples':[], 'add_include_guard':False}
    gb.new_code[factory_file_name]['code_tuples'].append( (-1, factory_file_content) )

    # Register that this class has a factory file
    gb.class_factory_file_dict[class_name['long_templ']] = factory_file_name

# ====== END: generateFactoryFunctions ========



# ====== generateWrapperHeader ========

# Generate a header containing the GAMBIT wrapper class

def generateWrapperHeader(class_el, class_name, abstr_class_name, namespaces, short_abstr_class_fname,
                          construct_assignment_operator, has_copy_constructor, copy_constructor_id):

    # Set file names and paths
    wrapper_decl_header_fname = gb.new_header_files[class_name['long']]['wrapper_decl']
    wrapper_def_header_fname  = gb.new_header_files[class_name['long']]['wrapper_def']
    wrapper_header_fname      = gb.new_header_files[class_name['long']]['wrapper']

    wrapper_decl_header_path = os.path.join(gb.boss_output_dir, wrapper_decl_header_fname)
    wrapper_def_header_path  = os.path.join(gb.boss_output_dir, wrapper_def_header_fname)
    wrapper_header_path      = os.path.join(gb.boss_output_dir, wrapper_header_fname)
    
    # Get code for the declaration and implementation headers
    wrapper_decl_header_content, wrapper_def_header_content = classutils.generateWrapperHeaderCode(class_el, class_name, abstr_class_name,
                                                                                                   namespaces, short_abstr_class_fname, 
                                                                                                   construct_assignment_operator, has_copy_constructor,
                                                                                                   copy_constructor_id=copy_constructor_id)

    # Code for the overall header file
    wrapper_decl_header_include_path = gb.new_header_files[class_name['long']]['wrapper_decl']
    wrapper_def_header_include_path  = gb.new_header_files[class_name['long']]['wrapper_def']
    wrapper_header_content  = '\n'
    wrapper_header_content += '#include "' + wrapper_decl_header_include_path + '"\n'
    wrapper_header_content += '#include "' + wrapper_def_header_include_path + '"\n'
    wrapper_header_content += '\n'


    # Register code
    if wrapper_decl_header_path not in gb.new_code.keys():
        gb.new_code[wrapper_decl_header_path] = {'code_tuples':[], 'add_include_guard':True}
    gb.new_code[wrapper_decl_header_path]['code_tuples'].append( (0, wrapper_decl_header_content) )

    if wrapper_def_header_path not in gb.new_code.keys():
        gb.new_code[wrapper_def_header_path] = {'code_tuples':[], 'add_include_guard':True}
    gb.new_code[wrapper_def_header_path]['code_tuples'].append( (0, wrapper_def_header_content) )

    if wrapper_header_path not in gb.new_code.keys():
        gb.new_code[wrapper_header_path] = {'code_tuples':[], 'add_include_guard':True}
    gb.new_code[wrapper_header_path]['code_tuples'].append( (0, wrapper_header_content) )

# ====== END: generateWrapperHeader ========


# ====== constrWrapperUtils ========

# Construct functions for dealing with wrapper pointer from abstract class 
# ('wrapper_creator', 'wrapper_deleter', 'set_delete_BEptr')

def constrWrapperUtils(class_name):

    wrapper_class_name = classutils.toWrapperType(class_name['long'], include_namespace=True)
    abstr_class_name = classutils.toAbstractType(class_name['long'], include_namespace=True)

    # Include statement for the header file
    wrapper_include_statement_decl = '#include "' + gb.new_header_files[class_name['long']]['wrapper_fullpath'] + '"\n'

    wr_utils_decl = ''
    wr_utils_impl = ''


    #
    # wrapper_creator
    #

    # Function declaration
    wr_utils_decl  = '\n'
    wr_utils_decl += wrapper_class_name + '* wrapper_creator(' + abstr_class_name + '*);\n'

    # Function implementation
    wr_utils_impl  = '\n'
    wr_utils_impl += wrapper_class_name + '* wrapper_creator(' + abstr_class_name + '* abs_ptr)\n'
    wr_utils_impl += '{\n'
    wr_utils_impl += ' '*cfg.indent + 'return new ' + wrapper_class_name + '(abs_ptr);\n'
    wr_utils_impl += '}\n'

    # #
    # # wrapper_creator
    # #

    # # Function declaration
    # wr_utils_decl  = '\n'
    # wr_utils_decl += 'void wrapper_creator(' + abstr_class_name + '*);\n'

    # # Function implementation
    # wr_utils_impl  = '\n'
    # wr_utils_impl += 'void wrapper_creator(' + abstr_class_name + '* abs_ptr)\n'
    # wr_utils_impl += '{\n'
    # wr_utils_impl += ' '*cfg.indent + 'abs_ptr->set_wptr( new ' + wrapper_class_name + '(abs_ptr) );\n'
    # wr_utils_impl += '}\n'


    #
    # wrapper_deleter
    #

    # Function declaration
    wr_utils_decl += '\n'
    wr_utils_decl += 'void wrapper_deleter(' + wrapper_class_name + '*);\n'

    # Function implementation
    wr_utils_impl += '\n'
    wr_utils_impl += 'void wrapper_deleter(' + wrapper_class_name + '* wptr)\n'
    wr_utils_impl += '{\n'
    wr_utils_impl += ' '*cfg.indent + 'wptr->set_delete_BEptr(false);\n'
    wr_utils_impl += ' '*cfg.indent + 'delete wptr;\n'
    wr_utils_impl += '}\n'


    #
    # set_delete_BEptr
    #

    # Function declaration
    wr_utils_decl += '\n'
    wr_utils_decl += 'void set_delete_BEptr(' + wrapper_class_name + '*, bool);\n'

    # Function implementation
    wr_utils_impl += '\n'
    wr_utils_impl += 'void set_delete_BEptr(' + wrapper_class_name + '* wptr, bool setting)\n'
    wr_utils_impl += '{\n'
    wr_utils_impl += ' '*cfg.indent + 'wptr->set_delete_BEptr(setting);\n'
    wr_utils_impl += '}\n'


    # Register code
    w_creator_header_path = os.path.join(gb.boss_output_dir, gb.wrapper_utils_fname + cfg.header_extension)
    w_creator_source_path = os.path.join(gb.boss_output_dir, gb.wrapper_utils_fname + cfg.source_extension)

    if w_creator_header_path not in gb.new_code.keys():
        gb.new_code[w_creator_header_path] = {'code_tuples':[], 'add_include_guard':True}

        gb.new_code[w_creator_header_path]['code_tuples'].append( (0, '#include "' + os.path.join(gb.gambit_backend_incl_dir, gb.wrapper_typedefs_fname + cfg.header_extension) + '"\n') )
        gb.new_code[w_creator_header_path]['code_tuples'].append( (0, '#include "' + os.path.join(gb.gambit_backend_incl_dir, gb.abstract_typedefs_fname + cfg.header_extension) + '"\n') )

    gb.new_code[w_creator_header_path]['code_tuples'].append( (0, wrapper_include_statement_decl) )        
    gb.new_code[w_creator_header_path]['code_tuples'].append( (-1, wr_utils_decl) )        

    if w_creator_source_path not in gb.new_code.keys():
        w_creator_include = '#include "' + os.path.join(gb.gambit_backend_incl_dir, gb.wrapper_utils_fname + cfg.header_extension) + '"\n'
        gb.new_code[w_creator_source_path] = {'code_tuples':[(0,w_creator_include)], 'add_include_guard':False}
    gb.new_code[w_creator_source_path]['code_tuples'].append( (-1, wr_utils_impl) )        

# ====== END: constrWrapperUtils ========




# ====== addAbstractTypedefs ========

# Add typedef to 'abstracttypedefs.hpp'

def addAbstractTypedefs(abstr_class_name, namespaces):

    indent = ' '*cfg.indent*len(namespaces)
    abstr_typedef_code  = ''
    abstr_typedef_code += utils.constrNamespace(namespaces, 'open', indent=cfg.indent)

    temp_namespace_list = [gb.gambit_backend_namespace] + namespaces
    abstr_typedef_code += indent + 'typedef ' + '::'.join(temp_namespace_list) + '::' + abstr_class_name['short'] + ' ' + abstr_class_name['short'] + ';\n'

    abstr_typedef_code += utils.constrNamespace(namespaces, 'close', indent=cfg.indent)
    abstr_typedef_code += '\n'

    frw_decl_include_statement       = '#include "' + os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, gb.frwd_decls_abs_fname + cfg.header_extension) + '"\n'
    identification_include_statement = '#include "' + os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, 'identification.hpp') + '"\n\n'
    undef_include_statement          = '#include "' + os.path.join(gb.gambit_backend_incl_dir, 'backend_undefs.hpp') + '"\n'

    abstracts_typedefs_header_path = os.path.join(gb.boss_output_dir, gb.abstract_typedefs_fname + cfg.header_extension)
    if abstracts_typedefs_header_path not in gb.new_code.keys():
        gb.new_code[abstracts_typedefs_header_path] = {'code_tuples':[], 'add_include_guard':False}

        gb.new_code[abstracts_typedefs_header_path]['code_tuples'].append( (0,  frw_decl_include_statement) ) 
        gb.new_code[abstracts_typedefs_header_path]['code_tuples'].append( (len(frw_decl_include_statement), identification_include_statement) ) 
        gb.new_code[abstracts_typedefs_header_path]['code_tuples'].append( (-1, undef_include_statement) ) 

    gb.new_code[abstracts_typedefs_header_path]['code_tuples'].append( (-len(undef_include_statement), abstr_typedef_code) )

# ====== END: addAbstractTypedefs ========



# ====== addWrapperTypedefs ========

# Add typedef to 'wrappertypdefs.hpp'

def addWrapperTypedefs(class_name, namespaces):

    short_wrapper_class_name = classutils.toWrapperType(class_name['short'])

    indent = ' '*cfg.indent*len(namespaces)

    wrapper_typedef_code  = ''
    wrapper_typedef_code += utils.constrNamespace(namespaces,'open')

    temp_namespace_list = [gb.gambit_backend_namespace] + namespaces
    wrapper_typedef_code += indent + 'typedef ' + '::'.join(temp_namespace_list) + '::' + class_name['short'] + ' ' + short_wrapper_class_name + ';\n'

    wrapper_typedef_code += utils.constrNamespace(namespaces,'close')
    wrapper_typedef_code += '\n'

    frw_decl_include_statement       = '#include "' + os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, gb.frwd_decls_wrp_fname + cfg.header_extension) + '"\n'
    identification_include_statement = '#include "' + os.path.join(gb.backend_types_basedir, gb.gambit_backend_name_full, 'identification.hpp') + '"\n\n'
    undef_include_statement          = '#include "' + os.path.join(gb.gambit_backend_incl_dir, 'backend_undefs.hpp') + '"\n'

    wrapper_typedefs_path = os.path.join(gb.boss_output_dir, gb.wrapper_typedefs_fname + cfg.header_extension)

    if wrapper_typedefs_path not in gb.new_code.keys():
        gb.new_code[wrapper_typedefs_path] = {'code_tuples':[], 'add_include_guard':False}

        gb.new_code[wrapper_typedefs_path]['code_tuples'].append( (0,  frw_decl_include_statement) ) 
        gb.new_code[wrapper_typedefs_path]['code_tuples'].append( (len(frw_decl_include_statement), identification_include_statement) ) 
        gb.new_code[wrapper_typedefs_path]['code_tuples'].append( (-1, undef_include_statement) ) 

    gb.new_code[wrapper_typedefs_path]['code_tuples'].append( (-len(undef_include_statement), wrapper_typedef_code) )

# ====== END: addWrapperTypedefs ========

Updated on 2022-08-03 at 12:58:21 +0000