#! /usr/bin/perl
#
# This file builds candidate interface files from the descriptions in 
# mpi.h
#
# Here are the steps:
# 1) Find the prototypes in mpi.h.in (Look for *Begin Prototypes*)
# 2) For each function, match the name and args:
#    int MPI_xxxx( ... )
# 3) By groups, create a new file with the name {catname}.h containing 
#    Copyright
#    For each function in the group, the expansion of the method
#
# Each MPI routine is assigned to a group.  Within each group,
# a particular argument is (usually) eliminated from the C++ call.
# E.g., in MPI::Send, the communicator argument is removed from the
# call sequence.
# Routines that have out parameters (e.g., the request in MPI_Isend)
# remove them as well.  Other routines return void.
#
# The replacement text will look something like
#    void Name( args ) const {
#      MPIX_CALL( MPI_Name( args, with (cast)((class).the_real_(class)) ); }
#
# A capability of this approach is that a stripped-down interface that 
# implements only the required routines can be created.
#
# Data structures
#   %<class>_members (e.g., mpi1comm): keys are names of routines.
#            Values are string indicating processing:
#            returnvalue-arg (0 if void, type if unique, position if not)
#   Pass by reference to process routine
#    
# TODO:
#    The derived classes (such as Intracomm) must *not* have their own
#    protected the_real_intracomm; instead, the must refer to the 
#    parent class's private storage. - DONE
#
#    The pack, unpack, packsize, init, and finalize routines must be 
#    placed in initcpp.cpp. - DONE
#
#    externs for the predefined objects need to be added to the
#    end of mpicxx.h - DONE
#
#    The optional no-status versions need to be created for 
#    methods such as Recv, Test, and Sendrecv . - DONE
#
# Setup global variables
$buildfiles = 1;
$build_sep_files = 0; # If false, build one huge mpicxx.h file
$build_io = 1;        # If false, exclude the MPI-IO routines
$indent = "    ";
$print_line_len = 0;
$debug = 0;
@mpilevels = ( 'mpi1' , 'mpi2' );
#feature variables
$do_subdecls = 1;


# Process arguments
#
# Args
# -feature={logical,fint,subdecls,weak,bufptr}, separated by :, value given 
# by =on or =off, eg
# -feature=logical=on:fint=off
# The feature names mean:
#    subdecls - Declarations for PC-C++ compilers added
# -routines=name  - provide a list of routines or a file that
# lists the routines to use.  The names must be in the same form as the 
# the class_xxx variables.  E.g., comm-Send, dtype-Commit.
$routine_list = "";
foreach $_ (@ARGV) {
    if (/-feature=(.*)/) {
	foreach $feature (split(/:/,$1)) {
	    print STDERR "Processing feature $feature\n" if $debug;
	    # Feature values are foo=on,off
	    ($name,$value) = split(/=/,$feature);
	    if ($value eq "on") { $value = 1; } 
	    elsif ($value eq "off") { $value = 0; }
	    # Set the variable based on the string
	    $varname = "do_$name";
	    $$varname = $value;
	}
    }
    elsif (/-nosep/) { $build_sep_files = 0; $indent = "    "; }
    elsif (/-sep/) { $build_sep_files = 1; $indent = ""; }
    elsif (/-noromio/) { $build_io = 0; }
    elsif (/-debug/) { $debug = 1; }
    elsif (/-routines=(.*)/) {
	$routine_list = $1;
    }
    else {
	print STDERR "Unrecognized argument $_\n";
    }
}

if (! -d "../../mpi/romio") { $build_io = 0; }
# ----------------------------------------------------------------------------
# 
# The following hashes define each of the methods that belongs to each class.
# To allow us to differentiate between MPI-1 and MPI-2, the methods for
# are separated.  The hash names have the form
# class_mpi<1 or 2><short classname>
# The value of each key is the POSITION (from 1) of the return argument 
# if an integer is used or the MPI-1 type (e.g., MPI_Request) if a string is
# used.  The position form is normally used to return an int or other value
# whose type does not give an unambiguous argument.  A value of 0 indicates
# that the routine does not return a value.
# Value of the hash is the argument of the routine that returns a value
# ToDo:
# Add to the value of each routine any special instructions on 
# processing the arguments.  See the Fortran version of buildiface.
# Needed are:
#   in:array, out:array    - Convert array of class members to/from
#                            arrays of the_real_xxx.  Question: for
#                            simplicity, should we have just in:reqarray,
#                            inout:reqarray, out:reqarray?  Answer: the 
#                            current approach uses separate routines for
#                            each array type.
#   in:const               - Add const in the C++ declaration (e.g., 
#                            in send, make the buf const void * instead
#                            of just void *)
#   in:bool,out:bool       - Convert value from bool to/from int
#   
# We'll indicate these with to fields returnvalue:argnum:... 
# For each method with special processing for an arg, there is 
# methodname-argnum.
# Eg, Isend is
#  Isend => 'MPI_Request:1', Isend-1 => 'in:const'
# and Send is
#  Send => '0:1', Send-1 => 'in:const'
# The mappings for the arguments are kept in a 
# separate hash, %routine_arg_maps.
#
%class_mpi1comm = ( Send => '0:1', Recv => 0, 
		    Bsend => '0:1', Ssend => '0:1', 
		    Rsend => '0:1', Isend => 'MPI_Request:1', 
		    Irsend => 'MPI_Request:1', Issend => 'MPI_Request:1', 
		    Ibsend => 'MPI_Request:1', Irecv => MPI_Request, 
		    Iprobe => 'int;bool', Probe => 0, 
		    Send_init => 'MPI_Request:1', 
		    Ssend_init => 'MPI_Request:1', 
		    Bsend_init => 'MPI_Request:1', 
		    Rsend_init => 'MPI_Request:1', Recv_init => MPI_Request, 
		    Sendrecv => 0, Sendrecv_replace => 0, Get_size => 'int', 
		    Get_rank => 'int', Free => 0, Get_topology => 2, 
		    Get_group => MPI_Group,
		    Compare => 'static:int', 
		    Abort => 0,
		    Set_errhandler => 0,
		    Get_errhandler => MPI_Errhandler,
		    Is_inter => '2;bool',
		   );
%routine_arg_maps = (
		    'Send-1' => 'in:const',
		    'Bsend-1' => 'in:const',
		    'Rsend-1' => 'in:const',
		    'Ssend-1' => 'in:const',
		    'Irsend-1' => 'in:const',
		    'Isend-1' => 'in:const',
		    'Ibsend-1' => 'in:const',
		    'Issend-1' => 'in:const',
		    'Send_init-1' => 'in:const',
		    'Ssend_init-1' => 'in:const',
		    'Bsend_init-1' => 'in:const',
		    'Rsend_init-1' => 'in:const',

		    'Free_keyval-1' =>  'in:refint',
		    
		     'Waitany-2' => 'inout:reqarray:1',
		     'Waitsome-2' => 'inout:reqarray:1',
		     'Waitsome-5' => 'out:statusarray:1', # or 4?
		     'Waitall-2' => 'inout:reqarray:1',
		     'Waitall-3' => 'out:statusarray:1',
		     'Testany-2' => 'inout:reqarray:1',
		     'Testany-3' => 'in:refint',
		     'Testsome-2' => 'inout:reqarray:1',
		     'Testsome-5' => 'out:statusarray:1', # or 4?
		     'Testall-2' => 'inout:reqarray:1',
		     'Testall-4' => 'out:statusarray:1',
		     'Startall-2' => 'inout:preqarray:1',
		     'Pack-1' => 'in:const',
		     'Unpack-1' => 'in:const',
		     'Pack-6' => 'in:refint',
		     'Unpack-5' => 'in:refint',

		     'Create_struct-4' => 'in:dtypearray:1',

		     'Merge-2' => 'in:bool',
		     'Create_cart-4' => 'in:boolarray:2',
		     'Create_cart-5' => 'in:bool',
		     'Create_graph-5' => 'in:bool',
		     'cart-Get_topo-4' => 'out:boolarray:2',
		     'Sub-2' => 'in:boolarray:-10', # Use -10 for immediate number
		     'Shift-4' => 'in:refint',
		     'Shift-5' => 'in:refint',
		     # Bug - there are cartcomm map and graphcomm map.  The
		     # call routine will find this 
		     'cart-Map-4' => 'in:boolarray:2',

		     'Get_processor_name-2' => 'in:refint',

		     'info-Set-2' => 'in:const',
		     'info-Set-3' => 'in:const',
		     'info-Get-2' => 'in:const',
		     'Get_valuelen-2' => 'in:const',

		     'file-Open-2' => 'in:const',
		     'file-Delete-1' => 'in:const',
		     'Set_view-4' => 'in:const',
		     'Write-2' => 'in:const',
		     'Write_all-2' => 'in:const',
		     'Iwrite_at-2' => 'in:const',
		     'Iwrite-2' => 'in:const',
		     'Write_at-3' => 'in:const',
		     'Write_at_all-3' => 'in:const',
		     'Write_at_all_begin-3' => 'in:const',
		     'Write_at_all_end-2' => 'in:const',
		     'Write_all_begin-2' => 'in:const',
		     'Write_all_end-2' => 'in:const',
		     'Write_ordered_begin-2' => 'in:const',
		     'Write_ordered_end-2' => 'in:const',
		     'Set_atomicity-2' => 'in:bool',

		     'Put-1' => 'in:const',
		     'Accumulate-1' => 'in:const',

		     'Detach_buffer-1' => 'inout:ptrref', 
		     );
%class_mpi1cart = ( 'Dup' => MPI_Comm,
		    'Get_dim' => 'int',
		    'Get_topo' => '0:4',
		    'Get_cart_rank' => '3',
		    'Get_coords' => 0,
		    'Shift' => '0:4:5',
		    'Sub' => 'MPI_Comm:2',
		    'Map' => '5:4',
);
%class_mpi1dtype = ( 'Create_contiguous' => 'MPI_Datatype',
		     'Create_vector' => 'MPI_Datatype', 
		     'Create_indexed' => 'MPI_Datatype', 
 		     'Create_struct' => 'static:5:4',
		     'Get_size' => 2, 
		     'Commit' => 0,
		     'Free' => 0, 
#		     'Pack' => '0:1:6', 
#		     'Unpack' => '0:1:5',
		     'Pack_size' => 4,
		     );
%class_mpi1errh = ( 'Free' => 0, 
		    # Init missing 
		    );
%class_mpi1graph = ( 'Get_dims' => 0, 
		     'Get_topo' => 0,
		     'Get_neighbors_count' => 'int', 
		     'Get_neighbors' => 0,
		     'Map' => 5,
		     );
# Range routines will require special handling
# The Translate_ranks, Union, Intersect, Difference, and Compare routines are 
# static and don't work on an instance of a group
%class_mpi1group = ( 'Get_size' => 'int',
		     'Get_rank' => 'int',
		     'Translate_ranks' => 'static:0',
		     'Compare' => 'static:int',
		     'Union' => 'static:MPI_Group',
		     'Intersect' => 'static:MPI_Group',
		     'Difference' => 'static:MPI_Group',
		     'Incl', MPI_Group,
		     'Excl', MPI_Group,
		     'Range_incl', MPI_Group,
		     'Range_excl', MPI_Group,
		     'Free' => 0,
);
%class_mpi1inter = ( 'Dup' => MPI_Comm, 
		     'Get_remote_size' => 'int', 
		     'Get_remote_group' => MPI_Group,
		     'Merge' => 'MPI_Comm:2',
		     );
%class_mpi1intra = ( 'Barrier' => 0, 
		     'Bcast' => 0, 
		     'Gather' => 0,
		     'Gatherv' => 0,
		     'Scatter' => 0,
		     'Scatterv' => 0,
		     'Allgather' => 0,
		     'Allgatherv' => 0,
		     'Alltoall' => 0,
		     'Alltoallv' => 0,
		     'Reduce' => 0,
		     'Allreduce' => 0,
		     'Reduce_scatter' => 0,
		     'Scan' => 0,
		     'Dup' => MPI_Comm,
		     'Create' => MPI_Comm,
		     'Split' => MPI_Comm,
		     'Create_intercomm' => MPI_Comm,
		     'Create_cart' => 'MPI_Comm:4:5',
		     'Create_graph' => 'MPI_Comm:5' 
);
%class_mpi1op = ( 'Free' => 0);
%class_mpi1preq = ( 'Start' => 0,
		    'Startall' => 'static:0:2' );
%class_mpi1req = ( 'Wait' => 0, 
		   'Test' => 'int;bool',
		   'Free' => 0, 
		   'Cancel' => 0,
		   'Waitall' => 'static:0:2:3',
		   'Waitany' => 'static:int:2',
		   'Waitsome' => 'static:3:2:5',
		   'Testall' => 'static:int;bool:2:4',
		   'Testany' => 'static:4;bool:2:3:4',
		   'Testsome' => 'static:3:2:5',
);
%class_mpi1st = ( 'Get_count' => 'int',
		  'Is_cancelled' => 'int;bool',
		  'Get_elements' => 'int',
		  # get/set source, tag, error have no C binding
		  );

# These are the routines that are in no class, minus the few that require
# special handling (Init, Wtime, and Wtick).
%class_mpi1base = ( 'Get_processor_name' => '0:2',
		    'Get_error_string' => 0,
		    'Get_error_class', => '2', 
		    'Compute_dims' => 0,
		    'Finalize' => 0,
		    'Is_initialized', => '1;bool',
		    'Attach_buffer' => 0,
		    'Detach_buffer' => '2:1',
		    'Pcontrol' => '0',
		    );
#
# Here are the MPI-2 methods
# WARNING: These are incomplete.  They primarily define only the
# MPI-2 routines implemented by MPICH2.
%class_mpi2comm = ( 'Get_attr' => 'int',
		    'Set_attr' => 0,
		    'Delete_attr' => 0,
#		    'Create_keyval' => 'int',
		    'Free_keyval' =>  'static:0:1',
		   );
%class_mpi2cart = ();
%class_mpi2dtype = ( 'Set_name' => 0, 
		     'Get_name' => 0
);
%class_mpi2errh = ();
%class_mpi2graph = ();
%class_mpi2group = ();
%class_mpi2inter = ( 'Barrier' => 0, # MPI-2 adds intercomm collective
		     'Bcast' => 0, 
		     'Gather' => 0,
		     'Gatherv' => 0,
		     'Scatter' => 0,
		     'Scatterv' => 0,
		     'Allgather' => 0,
		     'Allgatherv' => 0,
		     'Alltoall' => 0,
		     'Alltoallv' => 0,
		     'Reduce' => 0,
		     'Allreduce' => 0,
		     'Reduce_scatter' => 0,
		     'Scan' => 0,
		     'Exscan' => 0,
);

# Alltoallw uses an array of datatypes, which requires special handling
%class_mpi2intra = ( #'Alltoallw' => 0, 
		     'Exscan' => 0 );
%class_mpi2op = ();
%class_mpi2preq = ();
%class_mpi2req = ();
%class_mpi2greq = ( 'Complete' => 0 );
%class_mpi2st = ();
%class_mpi2file = ( );
if ($build_io) {
    %class_mpi2file = ( 
		   'Open' => 'static:MPI_File:2',
		   'Close' => 0,
		   'Delete' => 'static:0:1',
		   'Set_size' => 0,
		   'Preallocate' => 0,
		   'Get_size' => 'MPI_Offset',
		   'Get_group' => 'MPI_Group',
		   'Get_amode' => 'int',
		   'Set_info' => 0,
		   'Get_info' => 'MPI_Info',
		   'Set_view' => '0:4',
		   'Get_view' => 0,
		   'Read_at' => 0,
		   'Read_at_all' => 0,
		   'Write_at' => '0:3',
		   'Write_at_all' => '0:3',
		   'Iread_at' => 'MPI_Request', 
		   'Iwrite_at' => 'MPI_Request:2',
		   'Read' => 0,
		   'Read_all' => 0,
		   'Write' => '0:2',
		   'Write_all' => '0:2',
		   'Iread' => 'MPI_Request',
		   'Iwrite' => 'MPI_Request:2',
		   'Seek' => 0,
		   'Get_position' => 'MPI_Offset',
		   'Get_byte_offset' => 'MPI_Offset',
		   'Read_shared' => 0,
		   'Write_shared' => '0:2',
		   'Iread_shared' => 'MPI_Request',
		   'Iwrite_shared' => 'MPI_Request:2',
		   'Read_ordered' => 0,
		   'Write_ordered' => '0:2',
		   'Seek_shared' => 0,
		   'Get_position_shared' => 'MPI_Offset',
		   'Read_at_all_begin' => 0,
		   'Read_at_all_end' => 0,
		   'Write_at_all_begin' => '0:3',
		   'Write_at_all_end' => '0:2',
		   'Read_all_begin' => 0,
		   'Read_all_end' => 0,
		   'Write_all_begin' => '0:2',
		   'Write_all_end' => '0:2',
		   'Read_ordered_begin' => 0,
		   'Read_ordered_end' => 0,
		   'Write_ordered_begin' => '0:2',
		   'Write_ordered_end' => '0:2',
		   'Get_type_extent' => 'MPI_Aint',
		   'Set_atomicity' => '0:2',
		   'Get_atomicity' => 'int;bool',
		   'Sync' => 0,
		   'Get_errhandler' => 'MPI_Errhandler',
		   'Set_errhandler' => 0,
		   );		
#     %class_mpi2file = ( 
# 		   'File_open' => 'static:MPI_File:2',
# 		   'File_close' => 0,
# 		   'File_delete' => 'static:0:1',
# 		   'File_set_size' => 0,
# 		   'File_preallocate' => 0,
# 		   'File_get_size' => 'MPI_Offset',
# 		   'File_get_group' => 'MPI_Group',
# 		   'File_get_amode' => 'int',
# 		   'File_set_info' => 0,
# 		   'File_get_info' => 'MPI_Info',
# 		   'File_set_view' => '0:4',
# 		   'File_get_view' => 0,
# 		   'File_read_at' => 0,
# 		   'File_read_at_all' => 0,
# 		   'File_write_at' => '0:2',
# 		   'File_write_at_all' => '0:2',
# 		   'File_iread_at' => 'MPI_Request', 
# 		   'File_iwrite_at' => 'MPI_Request:1',
# 		   'File_read' => 0,
# 		   'File_read_all' => 0,
# 		   'File_write' => '0:1',
# 		   'File_write_all' => '0:1',
# 		   'File_iread' => 'MPI_Request',
# 		   'File_iwrite' => 'MPI_Request:1',
# 		   'File_seek' => 0,
# 		   'File_get_position' => 'MPI_Offset',
# 		   'File_get_byte_offset' => 'MPI_Offset',
# 		   'File_read_shared' => 0,
# 		   'File_write_shared' => 0,
# 		   'File_iread_shared' => 'MPI_Request',
# 		   'File_iwrite_shared' => 'MPI_Request:1',
# 		   'File_read_ordered' => 0,
# 		   'File_write_ordered' => '0:1',
# 		   'File_seek_shared' => 0,
# 		   'File_get_position_shared' => 'MPI_Offset',
# 		   'File_read_at_all_begin' => 0,
# 		   'File_read_at_all_end' => 0,
# 		   'File_write_at_all_begin' => '0:2',
# 		   'File_write_at_all_end' => '0:1',
# 		   'File_read_all_begin' => 0,
# 		   'File_read_all_end' => 0,
# 		   'File_write_all_begin' => '0:1',
# 		   'File_write_all_end' => '0:1',
# 		   'File_read_ordered_begin' => 0,
# 		   'File_read_ordered_end' => 0,
# 		   'File_write_ordered_begin' => '0:1',
# 		   'File_write_ordered_end' => '0:1',
# 		   'File_get_type_extent' => 'MPI_Aint',
# 		   'File_set_atomicity' => '0:1',
# 		   'File_get_atomicity' => 'bool',
# 		   'File_sync' => 0,
# 		   'File_set_errhandler' => 'MPI_Errhandler',
# 		   'File_get_errhandler' => 0,
# 		   );		
}
%class_mpi2win = (  'Put' => '0:1', 'Get' => 0, 
		    'Accumulate' => '0', 
		    'Create' => 'static:MPI_Win', 
		    'Free' => '0',
		    'Fence' => '0', 
		    'Get_group' => 'MPI_Group',
		    # 'Get_attr' => '?',
		    # 'Start' => '0',
		    # 'Complete' => '0',
		    # 'Post' => '0',
		    # 'Wait' => '0',
		    # 'Lock' => '0',
		    # 'Unlock' => '0',
		    # Others left until later
		    );
%class_mpi2info = ( 'Create' => 'static:1',
		    'Set' => '0:2:3',
		    'Delete' => '0:2',
		    'Get' => '5;bool:2',
		    'Get_valuelen' => '4;bool:2',
		    'Get_nkeys' => '2',
		    'Get_nthkey' => '0', 
		    'Dup' => '2',
		    'Free' => '0', 
		    );

# Name of classes, in the order in which they must be declared.  This
# includes all classes, by their short names
@classes = ( 
	           'except',
		   'dtype', 
		   'info', 
		   'st', 
		   'group', 
		   'op', 
		   'errh',
		   'req', 
		   'preq', 
                   'comm', 
		   'inter', 
		   'intra', 
		   'greq', 
		   'win', 
		   'file', 
		   'graph',
		   'cart', 
);

#
# Some classes have additional methods.  This hash on the classes (by 
# short name) gives the name of a routine that will add additional methods.
# Primarily used for the Status methods (get/set_tag etc) and for 
# Communicator clone methods.
%class_extra_fnc = ( 'st'        => 'Status_methods',
                     'except'    => 'Exception_methods',
		     'comm'      => 'Comm_methods',
		     'intercomm' => 'Intercomm_methods',
		     'intracomm' => 'Intracomm_methods',
		     'graphcomm' => 'Graphcomm_methods',
		     'cartcomm'  =>  'Cartcomm_methods',
		     'dtype'     => 'Datatype_methods',
		     'op'        => 'Op_methods',
		     );

# ----------------------------------------------------------------------------
# If there is a specific list of routines, replace the list with this
# list
%newclasses = ();
if ($routine_list ne "") {
    for $routine (split(/\s+/,$routine_list)) {
	print "$routine\n" if $debug;
	($class,$rname) = split(/-/,$routine);
	# Look up name in the class list
	$classvar = "class-mpi1$class";
	$result_type = 0;
	if (defined($$classvar{$rname})) {
	    $result_type = $$classvar{$rname};
	}
	else {
	    $classvar = "class-mpi2$class";
	    if (defined($$classvar{$rname})) {
		$result_type = $$classvar{$rname};
	    }
	}
	$newclasses{$class} .= " $rname=>$result_type";
    }
    # Now, clear all of the classes
    foreach $class (@classes) {
	$class_name = "class_mpi1$class";
	%$class_name = ();
	$class_name = "class_mpi2$class";
	%$class_name = ();
    }		  
    # And unpack newclasses
    foreach $class (keys(%newclasses)) {
	$class_name = "class_mpi1$class";
	foreach $rpair (split(/\s+/,$newclasses{$class})) {
	    if ($rpair eq "") { next; }
	    print "$rpair\n" if $debug;
	    ($routine, $rval) = split(/=>/,$rpair);
	    $$class_name{$routine} = $rval;
	}
    }
    # At this point, we should generate only the routines requested,
    # plus all of the classes (we may need the empty classes for the
    # predefined types)
}

# ----------------------------------------------------------------------------

# MPI objects
# dtypes gives all of the MPI datatypes whose C version are this name
# with MPI_ in front.  E.g., MPI::CHAR is the same as MPI_CHAR.
@dtypes = ( 'CHAR', 'UNSIGNED_CHAR', 'BYTE', 'SHORT', 'UNSIGNED_SHORT',
	    'INT', 'UNSIGNED', 'LONG', 'UNSIGNED_LONG', 'FLOAT', 
	    'DOUBLE', 'LONG_DOUBLE', 'LONG_LONG_INT', 'LONG_LONG', 
	    'PACKED', 'LB', 'UB', 'FLOAT_INT', 'DOUBLE_INT', 
	    'LONG_INT', 'SHORT_INT', 'LONG_DOUBLE_INT' );

#
# Still missing: C++ only types: BOOL, COMPLEX, DOUBLE_COMPLEX, 
# LONG_DOUBLE_COMPLEX. 

# ops is like dtypes
@ops = ( 'MAX', 'MIN', 'SUM', 'PROD', 'LAND', 'BAND', 'LOR', 'BOR', 
	 'LXOR', 'BXOR', 'MINLOC', 'MAXLOC', 'REPLACE' );
# errclasses is like dtypes.  Contains both MPI-1 and MPI-2 classes
@errclasses = ( 'SUCCESS', 'ERR_BUFFER', 'ERR_COUNT', 'ERR_TYPE',
		'ERR_TAG', 'ERR_COMM', 'ERR_RANK', 'ERR_REQUEST',
		'ERR_ROOT', 'ERR_GROUP', 'ERR_OP', 'ERR_TOPOLOGY',
		'ERR_DIMS', 'ERR_ARG', 'ERR_UNKNOWN', 'ERR_TRUNCATE',
		'ERR_OTHER', 'ERR_INTERN', 'ERR_PENDING', 'ERR_IN_STATUS',
		'ERR_LASTCODE', 
		'ERR_FILE', 'ERR_ACCESS', 'ERR_AMODE', 'ERR_BAD_FILE',
		'ERR_FILE_EXISTS', 'ERR_FILE_IN_USE', 'ERR_NO_SPACE',
		'ERR_NO_SUCH_FILE', 'ERR_IO', 'ERR_READ_ONLY', 
		'ERR_CONVERSION', 'ERR_DUP_DATAREP', 'ERR_UNSUPPORTED_DATAREP',
		'ERR_INFO', 'ERR_INFO_KEY', 'ERR_INFO_VALUE', 'ERR_INFO_NOKEY',
		'ERR_NAME', 'ERR_NO_MEM', 'ERR_NOT_SAME', 'ERR_PORT',
		'ERR_QUOTA', 'ERR_SERVICE', 'ERR_SPAWN',
		'ERR_UNSUPPORTED_OPERATION', 'ERR_WIN', 'ERR_BASE',
		'ERR_LOCKTYPE', 'ERR_KEYVAL', 'ERR_RMA_CONFLICT', 
		'ERR_RMA_SYNC', 'ERR_SIZE', 'ERR_DISP', 'ERR_ASSERT',
		);

#
# Special routines require special processing in C++
%special_routines = ( 'Init' => 1, 'Init_thread' => 1, 'Pcontrol' => '1' );

#
# Most routines can be processed automatically.  However, some
# require some special processing.  (See the Fortran version
# of buildiface)

$arg_string = join( ' ', @ARGV );

# ---------------------------------------------------------------------------
# Here begins more executable code.  Read the definitions of the 
# routines.  The argument list for routine xxx is placed into the hash
# mpi_routine{xxx}.
&ReadInterface( "../../include/mpi.h.in" );
# Special case:  Add Pcontrol
$mpi_routine{'Pcontrol'} = "int,...";

# if doing MPI2, we also need to read the MPI-2 protottypes
if ( -s "../../mpi/romio/include/mpio.h.in" ) { 
    &ReadInterface( "../../mpi/romio/include/mpio.h.in" );
}

# Class_type gives the C datatype for each class, except for the
# exception class, which has no C counterpart
%class_type = ( 'comm' => MPI_Comm, 
		'cart' => MPI_Comm,
		'dtype' => MPI_Datatype,
		'errh' => MPI_Errhandler,
		'graph' => MPI_Comm,
		'group' => MPI_Group,
		'inter' => MPI_Comm,
		'intra' => MPI_Comm,
		'op' => MPI_Op,
		'preq' => MPI_Request,
		'req' => MPI_Request,
		'greq' => MPI_Request,
		'st' => MPI_Status,
		'info' => MPI_Info,
		'win' => MPI_Win,
		'file' => MPI_File,
		'except' => 'int',
 );
#
# fullclassname gives the C++ binding class name for each shorthand version
%fullclassname = ( 'comm' => 'Comm',
		   'cart' => 'Cartcomm',
		   'dtype' => 'Datatype',
		   'errh' => 'Errhandler',
		   'graph' => 'Graphcomm',
		   'group' => 'Group',
		   'inter' => 'Intercomm',
		   'intra' => 'Intracomm',
		   'op' => 'Op',
		   'preq' => 'Prequest',
		   'req' => 'Request',
		   'st' => 'Status',
		   'greq' => 'Grequest',
		   'info' => 'Info',
		   'win' => 'Win',
		   'file' => 'File',
		   'except' => 'Exception',
);

#
# Each class may need to access internal elements of another class.
# This has gives the list of friends for each class (i.e., the
# classes that are allowed to directly access the protected members).
# The friends are the full class names
%class_friends = ( 'comm' => 'Cartcomm,Intercomm,Intracomm,Graphcomm,Datatype,Win,File',
		   'cart' => '',
		   'dtype' => 'Comm,Status,Intracomm,Intercomm,Win,File',
		   'errh' => 'Comm,File,Win',
		   'graph' => '',
		   'group' => 'Comm,Intracomm,Intercomm,Win,File',
		   'inter' => 'Intracomm',
		   'intra' => 'Cartcomm,Graphcomm,Datatype',
		   'op' => 'Intracomm,Intercomm,Win',
		   'preq' => '',
		   'req' => 'Comm,File',
		   'st' => 'Comm,File,Request',
		   'greq' => '',
		   'info' => 'File,Win,Comm',
		   'win' => '',
		   'file' => '',
 );

#
# We also need to know the derived classes.  This gives the class that
# a class is derived from.  Base classes are not included here.
%derived_class = ( 'graph' => 'Intracomm', 
		   'preq' => 'Request',
		   'greq' => 'Request',
		   'inter' => 'Comm',
		   'intra' => 'Comm',
		   'cart' => 'Intracomm',
		   );

#
# Maps all of the derived classes to their ultimate parent.  This is
# used to find the name of the correct protected element (the_real_xxx),
# used to store the C version of the class handle.
%mytopclass = ( 'graph'     => 'comm', 
		'graphcomm' => 'comm',
	        'intracomm' => 'comm', 
	        'intercomm' => 'comm',
	        'intra'     => 'comm', 
	        'inter'     => 'comm',
	        'cart'      => 'comm',
		'cartcomm'  => 'comm',
		'grequest'  => 'request',
		'prequest'  => 'request',
	        'greq'      => 'request',
  	        'preq'      => 'request' );

#
# Many of the C++ binding names are easily derived from the C name.
# For those names that are not so derived, this hash provides a mapping from
# the C names to the C++ names.
# WARNING: This list is incomplete
#
# These have the form <short-class-name>-<C++name> => <C-name>; i.e.,
# MPI_Comm_rank becomes 'comm-rank'.  Routines that are part of the MPI
# namespace but not in any class leave the class field blank, i.e., 
# -Attach_buffer .
%altname = ( '-Attach_buffer' => 'Buffer_attach',
	     '-Detach_buffer' => 'Buffer_detach',
	     '-Compute_dims' => 'Dims_create',
	     '-Get_error_class' => 'Error_class',
	     '-Get_error_string' => 'Error_string',
	     '-Is_initialized' => 'Initialized',
	     'comm-Sendrecv_replace' => 'Sendrecv_replace',
	     'comm-Get_topology' => 'Topo_test',
	     'comm-Get_rank' => 'Comm_rank',
	     'comm-Get_size' => 'Comm_size',
	     'comm-Get_group' => 'Comm_group',
	     'comm-Is_inter' => 'Comm_test_inter',
	     'dtype-Create_contiguous' => 'Type_contiguous',
	     'dtype-Create_vector' => 'Type_vector',
	     'dtype-Create_indexed' => 'Type_indexed',
	     'dtype-Create_struct' => 'Type_create_struct',
	     'dtype-Commit' => 'Type_commit',
	     'dtype-Pack' => 'Pack',
#	     'dtype-Unpack' => 'Unpack',
# Unpack is a special case because the C++ binding doesn't follow a simple
# rule to derive from the C binding
	     'dtype-Pack_size' => 'Pack_size',
	     'dtype-Free' => 'Type_free',
	     'dtype-Get_size' => 'Type_size',
	     'dtype-Get_name' => 'Type_get_name',
	     'dtype-Set_name' => 'Type_set_name',
	     'group-Get_size' => 'Group_size',
	     'group-Get_rank' => 'Group_rank',
	     'group-Intersect' => 'Group_intersection',
	     'intra-Create_intercomm' => 'Intercomm_create',
	     'inter-Get_remote_group' => 'Comm_remote_group',
	     'inter-Get_remote_size' => 'Comm_remote_size',
	     'inter-Dup' => 'Comm_dup',
	     'intra-Create' => 'Comm_create',
	     'intra-Dup' => 'Comm_dup',
	     'intra-Split' => 'Comm_split',
	     'intra-Create_cart' => 'Cart_create',
	     'intra-Create_graph' => 'Graph_create',
	     'st-Is_cancelled' => 'Test_cancelled',
	     'cart-Get_cart_rank' => 'Cart_rank',
	     'cart-Map' => 'Cart_map',
	     'cart-Get_topo' => 'Cart_get',
	     'cart-Shift' => 'Cart_shift',
	     'cart-Sub' => 'Cart_sub',
	     'cart-Dup' => 'Comm_dup',
	     'cart-Get_dim' => 'Cartdim_get',
	     'cart-Get_coords' => 'Cart_coords',
	     'cart-Get_rank' => 'Cart_rank',
	     'graph-Map' => 'Graph_map',
	     'graph-Get_topo' => 'Graph_get',
	     'graph-Get_neighbors' => 'Graph_neighbors',
	     'graph-Get_neighbors_count' => 'Graph_neighbors_count',
	     'graph-Get_dims' => 'Graphdims_get',
	     'graph-Dup' => 'Comm_dup',
	     );

# These routines must be defered because their implementations need 
# definitions of classes that must be made later than the class that they
# are in.  In particular, these need both datatypes and communicators.
%defer_definition = ( 'Pack' => Datatype, 
		      'Pack_size' => Datatype, 
		      'Unpack' => Datatype
		      );

# These classes (in the binding name) do not have a compare operation, or 
# use the parent class's compare operation.
# These use the Full class name.
%class_has_no_compare = ( 'Status' => 1,
			  'Intracomm' => 1,
			  'Intercomm' => 1,
			  'Cartcomm' => 1,
			  'Graphcomm' => 1,
			  'Prequest' => 1,
			  );
# These classes do not have a default intialization
# These use the Full class name
%class_has_no_default = ( 'Status' => 1 );

# If we aren't building separate files, create the master file
if (!$build_sep_files) {
    $filename = "mpicxx.h.in";
    $OUTFD = OUTFILEHANDLE;
    open ( $OUTFD, ">$filename" ) || die "Could not open $filename\n";
    # Use the derived file as a source
    $files[$#files+1] = "mpicxx.h";
    &print_header;
    print $OUTFD "namespace MPI {\n";
    print $OUTFD "#if \@HAVE_CXX_EXCEPTIONS\@
#define MPIX_CALL( fnc ) \\
{int err; err = fnc ; if (err) throw Exception(err);}
#else
#define MPIX_CALL( fnc ) (void)fnc
#endif\n";

#
# Within a "namespace" qualifier, the namespace name should not be used.
# Thus, we use Offset, not MPI::Offset.
print $OUTFD "
// Typedefs for basic int types
typedef MPI_Offset Offset;
typedef MPI_Aint   Aint;
typedef MPI_Fint   Fint;\n";

}

#
# Add the base routines.  Since these are not in any class, we
# place only their prototype in the header file.  The 
# implementation is then placed in the source file.  We can
# put these here because none of them use any of the other classes,
# and we'll want to use a few of them in the implementations of the
# other functions.
foreach $routine (keys(%class_mpi1base)) {
    # These aren't really a class, so they don't use Begin/EndClass
    $arginfo = $class_mpi1base{$routine};
    print $OUTFD "extern ";
    &PrintRoutineDef( $OUTFD, "", $routine, $arginfo, 1 );
}

#
# Here's the loop structure
# foreach class 
#   output class header
#   for mpi1, mpi2
#      for the routines in that class and choice of mpi1, mpi2
#   output any special methods
#

# Build the routines by class
foreach $class (@classes) {
    $shortclass = $class;
    $Class = $fullclassname{$class};
    #$mpi_type = $class_type{$class};

    # Special case to skip over the file routines (whose prototypes cause
    # us some problems).
    if ($class eq "file") {
        if (!$build_io) { next; }
	# Add a definition for MPI_FILE_NULL and MPI_File if none available
	print $OUTFD "#ifndef MPI_FILE_NULL\
#define MPI_FILE_NULL 0\
typedef int MPI_File;\
#endif\n";
    }
    
    # Begin the class, writing the common operations (destructors etc.)
    &BeginClass( $class );

    # Hack to ifdef out the file routines
    if ($class eq "file") {
	# Define the file type only if supported.
	print $OUTFD "#ifdef MPI_MODE_RDONLY\n";
    }

    foreach $mpilevel (@mpilevels) {
        $mpiclass = "$mpilevel$class";
        $class_hash = "class_$mpiclass";
	foreach $routine (keys(%$class_hash)) {
	    print STDERR "processing $routine\n" if $debug;

	    # info describes the return parameter and any special
	    # processing for this routine.
	    $arginfo = $$class_hash{$routine};
	    
	    &PrintRoutineDef( $OUTFD, $class, $routine, $arginfo, 0 );
	    
	    # Check for Status as an arg (handle MPI_STATUS_IGNORE 
	    # by providing a definition without using Status).
	    if ($args =~ /Status/ && $class ne "st") {
		&PrintRoutineDefNoStatus( $OUTFD, $class, 
					  $routine, $arginfo, 0 );
 	    }
	}
    }
    if (defined($class_extra_fnc{$class})) {
	$extrafnc = $class_extra_fnc{$class};
	&$extrafnc( $OUTFD );
    }

    # Hack to ifdef out the file routines
    if ($class eq "file") {
	# Define the file type only if supported.
	print $OUTFD "#endif\n";
    }
    &EndClass;

    # Special case.  Once we define a Datatype, add this typedef
    if ($class eq "dtype") {
        print $OUTFD "
    typedef void User_function(const void *, void*, int, const Datatype&); 
";
    }
}


    # As for the typedefs, we must leave off the MPI:: within an MPI 
    # namespace
    foreach $dtype (@dtypes) {
        print $OUTFD "extern Datatype $dtype;\n";
    }
    print $OUTFD "extern Datatype TWOINT;\n";
    print $OUTFD "extern Datatype DATATYPE_NULL;\n";

    # Fortran types
    print $OUTFD "
#ifdef HAVE_FORTRAN_BINDING
extern Datatype INTEGER;
extern Datatype REAL;
extern Datatype DOUBLE_PRECISION;
extern Datatype F_COMPLEX;
extern Datatype F_DOUBLE_COMPLEX;
extern Datatype LOGICAL;
extern Datatype CHARACTER;
extern Datatype TWOREAL;
extern Datatype TWODOUBLE_PRECISION;
extern Datatype TWOINTEGER;
#endif\n";

    # Initialize the operations
    foreach $op (@ops) {
	print $OUTFD "extern const Op $op;\n";
    }
    print $OUTFD "extern const Op OP_NULL;\n";

    # Predefined communicators and groups
    print $OUTFD "extern Intracomm COMM_WORLD;\n";
    print $OUTFD "extern Intracomm COMM_SELF;\n";
    print $OUTFD "extern const Comm COMM_NULL;\n";
    print $OUTFD "extern const Group GROUP_EMPTY;\n";
    print $OUTFD "extern const Group GROUP_NULL;\n";

    # Predefined requests
    print $OUTFD "extern const Request REQUEST_NULL;\n";

    # Predefined errhandlers
    print $OUTFD "extern const Errhandler ERRHANDLER_NULL;\n";
    print $OUTFD "extern const Errhandler ERRORS_RETURN;\n";
    print $OUTFD "extern const Errhandler ERRORS_ARE_FATAL;\n";
    print $OUTFD "extern const Errhandler ERRORS_THROW_EXCEPTIONS;\n";

    # Predefined integers.  Include MPI-2 predefined keyvals
    foreach $int (BSEND_OVERHEAD, KEYVAL_INVALID, CART, GRAPH,
		  IDENT, SIMILAR, CONGRUENT, UNEQUAL, PROC_NULL,
		  ANY_TAG, ANY_SOURCE, ROOT, TAG_UB, IO, HOST, WTIME_IS_GLOBAL,
                  UNIVERSE_SIZE, LASTUSEDCODE, APPNUM, 
		  MAX_PROCESSOR_NAME, MAX_ERROR_STRING,
		  MAX_NAME_STRING, MAX_PORT_NAME, MAX_OBJECT_NAME,
                  MAX_INFO_VAL, MAX_INFO_KEY, 
		  UNDEFINED, @errclasses ) {
	print $OUTFD "extern const int $int;\n";
    }

    # Predefined other
    print $OUTFD "extern const void *BOTTOM;\n";

    # Other routines
    print $OUTFD "extern void Init(void);\n";
    print $OUTFD "extern void Init(int &, char **& );\n";
    print $OUTFD "extern void Finalize(void);\n";
    print $OUTFD "extern Aint Get_address( void * );\n";
    print $OUTFD "extern double Wtime(void);\n";
    print $OUTFD "extern double Wtick(void);\n";

    print $OUTFD "} // namespace MPI\n";

    close ( $OUTFD );


# Build the special routines
&build_specials;

#
# This block can be used to create the Makefile
#
# This isn't quite right.  mpicxx.h isn't a regular kind of source file.
open ( MAKEFD, ">Makefile.sm" ) || die "Cannot create Makefile.sm";
print MAKEFD "# DO NOT EDIT\n# This file created by buildiface $arg_string\n";
# This line is unfortunately necessary to ensure that a working
# autoconf is used.
#print MAKEFD "smvar_autoconf = /home/gropp/bin/linux/autoconf\n";
#print MAKEFD "smvar_debug = 1\n";
print MAKEFD "smvar_do_dependencies = ignore\n";
&print_line(  MAKEFD, "mpi_sources = ", 80, "\\\n\t", 8 );
for ($i=0; $i<=$#files; $i++) {
    $name = $files[$i];
    &print_line( MAKEFD, "$name ", 80, "\\\n\t", 8 );
}
&print_endline( MAKEFD );

# No profile library for C++.  All routines call the MPI, not PMPI, routines.
print MAKEFD "lib\${MPILIBNAME}_a_SOURCES = \${mpi_sources}\
\
INCLUDES = -I../../include -I\${top_srcdir}/src/include\
maintainerclean-local:\
\trm -f \${mpi_sources}\
install_INCLUDE = mpicxx.h\
install_BIN     = mpicxx\
install_ETC     = mpicxx.conf\n";

# Since configure copies mpicxx to the bin dir, we need to remove it
# in a distclean step.  
print MAKEFD "distclean-local:\n";
print MAKEFD "\trm -f ../../../bin/mpicxx\n";

close( MAKEFD );

#
# ------------------------------------------------------------------------
# Procedures
# print_line( FD, line, count, continue, continuelen )
# Print line to FD; if line size > count, output continue string and
# continue.  Use print_endline to finish a line
sub print_line {
    my $FD = $_[0];
    my $line = $_[1];
    my $count = $_[2];
    my $continue = $_[3];
    my $continue_len = $_[4];
    
    $linelen = length( $line );
    #print "linelen = $linelen, print_line_len = $print_line_len\n";
    if ($print_line_len + $linelen > $count) {
	print $FD $continue;
	$print_line_len = $continue_len;
    }
    print $FD $line;
    $print_line_len += $linelen;
}
sub print_endline {
    my $FD = $_[0];
    print $FD "\n";
    $print_line_len = 0;
}

# Print the header of the file, containing the definitions etc.
sub print_header {
    print $OUTFD "/* -*- Mode: C++; c-basic-offset:4 ; -*- */\
/*  \
 *  (C) 2001 by Argonne National Laboratory.\
 *      See COPYRIGHT in top-level directory.\
 *\
 * This file is automatically generated by buildiface $arg_string\
 * DO NOT EDIT\
 */
/* style: c++ header */\
\n";
}

# Print the arguments for the routine DEFINITION.
# TODO : Remove any output parameters.  This is stored in info by position 
# if an integer or type (if a string).  If 0, there is no return object
sub print_args { 
    my $OUTFD = $_[0];
    my @parms = split(/\s*,\s*/, $_[1] );  # the original parameter list
    my $class_type = $_[2];                # ??
    my $arginfo = $_[3];                   # Value of <class>_hash{routine)}

    my $count = 1;
    my $last_args = "";
    $first = 1;
    my $args_printed = 0;
    my $is_static = 0;           # set to true if function is static

    my $special_args = "::";
    if ($arginfo =~ /^static:/) {
	$arginfo =~ s/^static://;
	$is_static = 1;
    }
    if ($arginfo =~ /(^[^:]+):(.*)/) {
	$returnarg = $1;
	$special_args = ":".$2.":";  # makes the numbers :\d+:...
	print "Routine $routine special args $special_args\n" if $debug;
    }

    # Special case: if the only parm is "void", remove it from the list
    print STDERR "Nparms = $#parms, parms = " . join(',',@parms) . "\n" if $debug;
    if ($#parms == 0 && $parms[0] eq "void") {
	$#parms = -1;
    }
    # class_pos is the position of the class variable in the argument list.
    # If specified by parm type, we must find it
    $class_pos = "";
    if ($class_pos eq "" && $class_type ne "" && !$is_static) {
	$class_pos = 0;
	$pos = 1;
	foreach $parm (@parms) {
	    if ($parm =~ /$class_type/) {
		# Found the type; set the position of the class variable
		$class_pos = $pos;
		last;
	    }
	    $pos++;
	}
    }

    # Output the list
    print $OUTFD "( ";
    foreach $parm (@parms) {
	$pos_check = ":" . $count . ":";
	print "parm = :$parm:\n" if $debug;

	# Check whether this argument has special processing
	# Otherwise, apply standardized rules (currently, this
	# is used only to prepend a qualifier, such as "const").
	if ($special_args =~ /$pos_check/) {
	    if (&DoSpecialArgProcessing( $OUTFD, $routine, $count, 
					 "methoddecl" ) ) {
		$args_printed ++;
		$count++;
		if ($first) { $first = 0; }
		next;
	    }
	}
	# Match type to replacement
	if ($count == $class_pos || $count == $return_parm_pos) {
	    print "Skipping parm $parm because of position or return\n" if $debug;
	    # Skip this arg in the definition 
	    ;
	}
	else {
	    $args_printed ++;
	    if ($first) { $first = 0; }
	    else { print $OUTFD ", "; }
	    
	    if ($parm =~/\[/) {
		# Argument type is array, so we need to 
		#  a) place parameter correctly
		# Split into raw type and []
		# Handle multidim arrays as well (Range_excl/incl)
		$parm =~ /\s*([^\s]*)\s*(\[\s*\])(.*)/;
		$basetype = $1;
		$foundbrack = $2;
		$extrabracks = $3;
		$otherdims = "";
		if ($extrabracks =~ /(\[[\d\s]*\])/) {
		    $otherdims = $1;
		}
		print $OUTFD "$basetype v$count\[\]$otherdims";
	    }
	    elsif ($parm =~ /\.\.\./) {
		# Special case for varargs.  Only ints!
		print $OUTFD $parm;
	    }
	    else {
		# Convert C to C++ types
		$cxxtype = $parm;
		if ($cxxtype =~ /MPI_/) {
		    $cxxtype =~ s/\*/\&/;
		}
		$cxxtype =~ s/MPI_//;
		print $OUTFD "${cxxtype} v$count";
	    }
	}
	$count++;
    }
    if ($args_printed == 0) { print $OUTFD "void"; }
    print $OUTFD " )";
}

# Print the arguments for the routine CALL.  
# Handle the special arguments
sub print_call_args {
    my @parms = split(/\s*,\s*/, $_[1] );
    my $OUTFD = $_[0];
    my $class_type = $_[2];    # ??
    my $arginfo = $_[3];       # Value of <class>_hash{routine)}
    my $count = 1;
    $first = 1;

    my $is_static = 0;

    if ($arginfo =~ /^static:/) { $is_static = 1; }

    print $OUTFD "( ";

    # Special case: if the only parm is "void", remove it from the list
    if ($#parms == 0 && $parms[0] eq "void") {
	$#parms = -1;
    }

    # class_pos is the position of the class variable in the argument list.
    # If specified by parm type, we must find it
    $class_pos = "";
    if ($class_pos eq "" && !$is_static) {
	$class_pos = 1;
	foreach $parm (@parms) {
	    if ($parm =~ /$class_type/) {
		last;
	    }
	    $class_pos++;
	}
    }

    my $lcclass = lc($fullclassname{$class});
    my $shortclass = $class; # ??? FIXME
    my $lctopclass = $lcclass;
    # For derived classes, we sometimes need to know the name of the
    # top-most class, particularly for the "the_real_xxx" name.
    if (defined($mytopclass{$lcclass})) {
	$lctopclass = $mytopclass{$lcclass};
    }
    print "$routine-$count\n" if $debug;
    foreach $parm (@parms) {
	if (!$first) { print $OUTFD ", "; } else { $first = 0; }

	# Special handling must preempt any other handling
	if (defined($routine_arg_maps{"${routine}-$count"}) ||
	    defined($routine_arg_maps{"${class}-${routine}-${count}"})) {
	    &DoSpecialArgProcessing( $OUTFD, $routine, $count, "call" );
	}
	elsif ($count == $return_parm_pos) {
	    # We may need to pass the address of a temporary object
	    # We'll unilateraly assume this for now
	    # This must be first, so that it has a priority over the
	    # class pos location.
	    if ($parm =~ /MPI_/ && !($parm =~ /MPI_Offset/) &&
		!($parm =~ /MPI_Aint/) ) {
		my $lctype = $real_return_type;
		# Convert class_type to the appropriate name
		$lctype = lc($lctype);
		if (defined($mytopclass{$lctype})) {
		    $lctype = $mytopclass{$lctype};
		}
		# Handle the MPIO_Request problem (temp until ROMIO uses
		# MPI_Requests)
		$cast = "";
		if ($parm =~ /MPI_Request/ &&
		    $class eq "file") {
		    $cast = "(MPIO_Request *)";
		}
		print $OUTFD "$cast&(v$count->the_real_$lctype)";
	    }
	    else {
		print $OUTFD "&v$count";
	    }
	}
	elsif ($count == $class_pos) {
	    # Skip this arg in the definition 
	    if ($parm =~ /\*/) {
		print $OUTFD "($parm) &the_real_$lctopclass";
	    }
	    else {
		print $OUTFD "($parm) the_real_$lctopclass";
	    }
	}
	elsif ($parm =~ /%%(.*)%%/) {
	    print $OUTFD "$1";
	}
	else {
	    # Convert to/from object type as required.  
	    if (defined($argsneedcast{$parm})) {
		$argval = "v$count";
		$callparm = $argsneedcast{$parm};
		$callparm =~ s/ARG/$argval/;
		
		print $OUTFD &HandleObjectParm( $parm, $argval );
	    }
	    else {
		print $OUTFD &HandleObjectParm( $parm, "v$count" );
	    }
	}
	$count++;
    }
    print $OUTFD " )";
}

# Print the option function attribute; this supports GCC, particularly 
# the __atribute__ weak option.
sub print_attr {
    if ($do_weak) {
	print $OUTFD "FUNC_ATTRIBUTES\n";
    }
}

#
# Look through $args for parameter names (foo\s\s*name)
# and remove them
sub clean_args {
    my $newargs = "";
    my $comma = "";
    for $parm (split(',',$args)) {
	# Remove any leading or trailing spaces
	$parm =~ s/^\s*//;
	$parm =~ s/\s*$//;
	# Handle parameters with parameter names
	# First if handles "int foo", second handles "int *foo"
	if ( ($parm =~ /^([A-Za-z0-9_]+)\s+[A-Za-z0-9_]+$/) ) {
	    $parm = $1;
	}
	elsif ( ($parm =~ /^([A-Za-z0-9_]+\s*\*)\s*[A-Za-z0-9_]+$/) ) {
	    $parm = $1;
	}
	$newargs .= "$comma$parm";
	$comma = ",";
    }
    print STDERR "$newargs\n" if $debug;
    $args = $newargs;
}

#
# Build the special routines
sub build_specials {
    # The init routine contains some configure-time values.
    open( $OUTFD, ">initcxx.cpp" ) || die "Cannot open initcxx.cpp\n";
    @files[$#files+1] = "initcxx.cpp";
    &print_header;
    print $OUTFD "#include \"mpi.h\"\n";
    print $OUTFD "#include \"mpicxx.h\"\n";
    print $OUTFD "#include <stdarg.h>\n";    # Required for pcontrol

    # Start the namespace
    print $OUTFD "namespace MPI {\n";

    # Initialize the datatypes.
    # We do not use MPI:: within the MPI namespace
    foreach $dtype (@dtypes) {
	print $OUTFD "Datatype $dtype(MPI_$dtype);\n";
    }
    # special case
    print $OUTFD "Datatype TWOINT(MPI_2INT);\n";
    print $OUTFD "Datatype DATATYPE_NULL;\n";

    # Fortran types
    print $OUTFD "
#ifdef HAVE_FORTRAN_BINDING
Datatype INTEGER(MPI_INTEGER);
Datatype REAL(MPI_REAL);
Datatype DOUBLE_PRECISION(MPI_DOUBLE_PRECISION);
Datatype F_COMPLEX(MPI_COMPLEX);
Datatype F_DOUBLE_COMPLEX(MPI_DOUBLE_COMPLEX);
Datatype LOGICAL(MPI_LOGICAL);
Datatype CHARACTER(MPI_CHARACTER);
Datatype TWOREAL(MPI_2REAL);
Datatype TWODOUBLE_PRECISION(MPI_2DOUBLE_PRECISION);
Datatype TWOINTEGER(MPI_2INTEGER);
#endif\n";
    # Still to do: Fortran optional types, integer1,2,4, real2,4,8,

    # Initialize the operations
    foreach $op (@ops) {
	print $OUTFD "const Op $op(MPI_$op);\n";
    }
    print $OUTFD "const Op OP_NULL;\n";

    # Predefined communicators and groups
    print $OUTFD "Intracomm COMM_WORLD(MPI_COMM_WORLD);\n";
    print $OUTFD "Intracomm COMM_SELF(MPI_COMM_SELF);\n";
    print $OUTFD "const Comm COMM_NULL;\n";
    print $OUTFD "const Group GROUP_EMPTY(MPI_GROUP_EMPTY);\n";
    print $OUTFD "const Group GROUP_NULL;\n";

    # Predefined requests
    print $OUTFD "const Request REQUEST_NULL;\n";

    # Predefined errhandlers
    print $OUTFD "const Errhandler ERRHANDLER_NULL;\n";
    print $OUTFD "const Errhandler ERRORS_RETURN(MPI_ERRORS_RETURN);\n";
    print $OUTFD "const Errhandler ERRORS_ARE_FATAL(MPI_ERRORS_ARE_FATAL);\n";
    # Errors_return is not quite right for errors-throw-exceptions,
    # but it is close.
    print $OUTFD "const Errhandler ERRORS_THROW_EXCEPTIONS(MPI_ERRORS_RETURN);\n";

    # Predefined integers
    foreach $int (BSEND_OVERHEAD, KEYVAL_INVALID, CART, GRAPH,
		  IDENT, SIMILAR, CONGRUENT, UNEQUAL, PROC_NULL,
		  ANY_TAG, ANY_SOURCE, ROOT, TAG_UB, IO, HOST, WTIME_IS_GLOBAL,
                  UNIVERSE_SIZE, LASTUSEDCODE, APPNUM, 
		  MAX_PROCESSOR_NAME, MAX_ERROR_STRING,
		  MAX_NAME_STRING, MAX_PORT_NAME, MAX_OBJECT_NAME,
                  MAX_INFO_VAL, MAX_INFO_KEY, 
		  UNDEFINED, @errclasses ) {
	print $OUTFD "const int $int = MPI_$int;\n";
    }

    # Predefined other
    print $OUTFD "const void *BOTTOM = MPI_BOTTOM;\n";

    print $OUTFD "void Init";
    $args = "";
    &print_args( $OUTFD, $args );
    &print_attr;
    print $OUTFD "{\n";
    print $OUTFD "    MPI_Init( 0, 0 );\n";
    print $OUTFD "}\n";

    #
    # The following may not be quite right because they don't include
    # any attributes that we may include with the definitions.  However,
    # this is easier than forcing the print_args routine to handle these
    # simple cases.
    #
    print $OUTFD "void Init( int &argc, char **&argv )
{
    MPI_Init( &argc, &argv );
}\n";

    print $OUTFD "void Finalize"; 
    $args = "";
    &print_args( $OUTFD, $args );
    &print_attr;
    print $OUTFD "{\n";
    print $OUTFD "    MPI_Finalize( );\n";
    print $OUTFD "}\n";

    print $OUTFD "bool Is_initialized(void)
    { 
	int flag;
	MPI_Initialized( &flag );
	return (flag == 1);
    }\n";

    print $OUTFD "void Compute_dims( int nnodes, int ndims, int dims[] )
    {
	MPIX_CALL( MPI_Dims_create( nnodes, ndims, dims ) );
    }\n";

    print $OUTFD "void Attach_buffer( void *buffer, int size )
    {
	MPIX_CALL( MPI_Buffer_attach( buffer, size ) );
    }\n";

    print $OUTFD "int Detach_buffer( void *&buffer )
    {
	int size;
	MPIX_CALL( MPI_Buffer_detach( &buffer, &size ) );
	return size;
    }\n";

    print $OUTFD "void Get_processor_name( char *name, int &resultlen ) 
    {
    MPIX_CALL( MPI_Get_processor_name( name, &resultlen ) );
    }\n";

    print $OUTFD "void Pcontrol( int v, ... )
    {
	va_list ap;
        va_start(ap,v);
	MPIX_CALL( MPI_Pcontrol( v, ap ) );
    }\n";

    print $OUTFD "int Get_error_class( int errcode ) 
    {
    int errclass;
    MPIX_CALL( MPI_Error_class( errcode, &errclass ) );
    return errclass;
    }\n";

    print $OUTFD "Aint Get_address( void *ptr )
    {
    MPI_Aint a;
    MPI_Address( ptr, &a );
    return (Aint)a;
    }\n";

    # Init is a difficult function because we must allow C to call a 
    # C++ function.  We do this by getting help from the MPI implementation
    # which invokes the MPIR_Call_op_fn routine, with a pointer to the 
    # C++ routine to invoke.
    #
    # Note: Some compilers complain about the cast to the 
    # (void (*)(void)) function, expecting an `extern "C"' as well, but
    # other compilers do not accept the extern "C".  Sigh.
    print $OUTFD "
extern \"C\" void MPIR_Op_set_cxx( MPI_Op, void (*)(void) );
extern \"C\" 
void MPIR_Call_op_fn( void *invec, void *outvec, int len, MPI_Datatype dtype,
		     User_function *uop )
{
    MPI::Datatype cxxdtype = dtype;
    (*uop)( invec, outvec, len, cxxdtype );
}
void Op::Init( User_function *f, bool commute )
    {
	MPIX_CALL( MPI_Op_create( (MPI_User_function *)f, 
				 (int) commute, &the_real_op ) ); 
	MPIR_Op_set_cxx( the_real_op, (void (*)(void)) MPIR_Call_op_fn );
    }\n";

    # Keyval and attribute routines
    print $OUTFD "
extern \"C\" int MPIR_Call_delfn( MPI_Comm comm, int keyval,
				   void *value, void *extra_state, 
				   MPI::Comm::Delete_attr_function *f )
{
    MPI::Comm cxxcomm = comm;
    int err;
    err = (*f)( cxxcomm, keyval, value, extra_state );
    return err;  
}
extern \"C\" int MPIR_Call_copyfn( MPI_Comm comm, int keyval,
				   void *extra_state, void *value,
				    void *new_value, int *cflag,
				   MPI::Comm::Copy_attr_function *f )
{
    MPI::Comm cxxcomm = comm;
    int err;
    bool flag;
    err=(*f)( cxxcomm, keyval, extra_state, value, new_value, flag );
    *cflag = flag;
      return err;
}
extern \"C\" void MPIR_Keyval_set_cxx( int, void (*)(void), void (*)(void) );
int Comm::Create_keyval( Copy_attr_function *cf, Delete_attr_function *df, 
			void *extra_state ) {
    int keyval;

    if (cf == MPI::Comm::NULL_COPY_FN) cf = 0;
    if (df == MPI::Comm::NULL_DELETE_FN) df = 0;
    MPIX_CALL( MPI_Comm_create_keyval( (MPI_Comm_copy_attr_function *)cf, 
				       (MPI_Comm_delete_attr_function *)df,
				      &keyval, extra_state ) );
    MPIR_Keyval_set_cxx( keyval, (void (*)(void)) MPIR_Call_delfn, 
			         (void (*)(void)) MPIR_Call_copyfn );
    return keyval;
}
";

    print $OUTFD "\
    void Datatype::Pack( const void *inbuf, int incount, void *outbuf, 
			int outsize, int &position, const Comm &comm ) {
	MPIX_CALL( MPI_Pack( (void *)inbuf, incount, the_real_datatype, outbuf, 
			    outsize, &position, comm.the_real_comm ) );
    }
    void Datatype::Unpack( const void *inbuf, int insize, void *outbuf,
			  int outcount, int &position, const Comm &comm ) {
	MPIX_CALL( MPI_Unpack( (void *)inbuf, insize, &position, outbuf, outcount, 
			      the_real_datatype, comm.the_real_comm ) );
    }
\n";

    print $OUTFD "double Wtime(void) { return MPI_Wtime(); }\n";
    print $OUTFD "double Wtick(void) { return MPI_Wtick(); }\n";

    print $OUTFD "} // namespace MPI\n";
    close ($OUTFD);
}

# Given an integer location of an argument, return the corresponding
# type, from the arg list
sub Convert_pos_to_type {
    my @parm = split( ',', $_[0] );
    my $loc = $_[1];

    return $parm[$loc-1];
}
sub Convert_type_to_pos {
    my @parm = split( ',', $_[0] );
    my $type = $_[1];
    my $loc = 1;
    
    for $parm (@parm) {
	if ($parm =~ /$type/) { return $loc; }
	$loc ++;
    }
    return 0;
}

# Print the class header 
# PrintClassHead( $OUTFD, class, mpitype, friends )
# E.g., PrintClassHead( $OUTFD, "Datatype", "MPI_Datatype", "Comm,Status" )
sub PrintClassHead {
    my $OUTFD = $_[0];
    my $class = $_[1];
    my $mpi_type = $_[2];
    my $friends  = $_[3];
    my $mpi_null_type = uc("${mpi_type}_NULL" );

    my $lcclass = lc($class);
    my $lctopclass = $lcclass;

    if (! ($mpi_type =~ /^MPI_/) ) {
	# The mpi_type isn't an MPI type after all.  Assume that
	# it is something (like an int) where we want the default to
	# be 0
	$mpi_null_type = "0";
    }
    # For derived classes, we sometimes need to know the name of the
    # top-most class, particularly for the "the_real_xxx" name.
    if (defined($mytopclass{$lcclass})) {
	$lctopclass = $mytopclass{$lcclass};
    }
    my $parent = "";

    if (defined($derived_class{$shortclass})) {
	$parent = ": public $derived_class{$shortclass}";
    }
    
    print $OUTFD "\nclass $class $parent {\n";
    if ($friends ne "") {
	foreach $name (split(/,/,$friends)) {
	    print $OUTFD "    friend class $name;\n";
	}
    }
    if ($lcclass eq $lctopclass) {
	print $OUTFD "\		
  protected:
    $mpi_type the_real_$lcclass;\n";
	# Check for special declarations
	$otherdeclfn = "$class" . "_extradecls";
	if (defined(&$otherdeclfn)) {
	    &$otherdeclfn( $OUTFD );
	}
    }
  print $OUTFD "\
  public:
    // new/delete
    inline $class($mpi_type obj) { the_real_$lctopclass = obj; }\n";
    if (defined($class_has_no_default{$class})) {
	print $OUTFD "    inline $class(void) {}\n";
    }
    else {
	print $OUTFD "    inline $class(void) {the_real_$lctopclass = $mpi_null_type;}\n";
    }
    
    # These had $class :: $class..., but pgCC complained,
    # so the $class :: was removed
    print $OUTFD "\
    virtual ~$class() {}
    // copy/assignment
    $class(const $class &obj) {
      the_real_$lctopclass = obj.the_real_$lctopclass; }
    $class& operator=(const $class &obj) {
      the_real_$lctopclass = obj.the_real_$lctopclass; return *this; }\n";

    if (!defined($class_has_no_compare{$class})) {
	# Some classes (e.g., Status) do not have compare operations
	# *or* they are derived classes that must use the parent's
	# comparison operations
	print $OUTFD "
    // logical
    bool operator== (const $class &obj) {
      return (the_real_$lctopclass == obj.the_real_$lctopclass); }
    bool operator!= (const $class &obj) {
      return (the_real_$lctopclass != obj.the_real_$lctopclass); }";
    }

    # These had $class :: $class..., but pgCC complained,
    # so the $class :: was removed on operator=
    print $OUTFD "
    // C/C++ cast and assignment
    inline operator $mpi_type*() { return &the_real_$lctopclass; }
    inline operator $mpi_type() const { return the_real_$lctopclass; }
    $class& operator=(const $mpi_type& obj) {
      the_real_$lctopclass = obj; return *this; }
";
}

sub PrintClassTail { 
    my $OUTFD = $_[0];
    print $OUTFD "};\n";
}

# -----------------------------------------------------------------------------
# Here will go routines for handling return values.  These need to move them
# from pointer arguments in the parameter list into a local declaration 
# (possibly using new)
#
# We process a binding *first* and set the global variables
#    return_type (type of return value, in the C binding)
#    return_actual_type (real return type, in the C++ binding)
#    return_parm_pos (number of location of arg in parm list; 0 if none)
# return_info is either a number or a type.  If a type, it does NOT include
# the * (e.g., int instead of int *), but the * must be in the parameter
# FindReturnInfo( return_info, args )
#    The return info may also contain a ;<actual type>, as in
#     3;bool
# This is used for the cases where the return type isn't obvious
# from the return type.  This is necessary for C++ returns of type bool
# that are int in C (since other int returns may in fact be ints).  
sub FindReturnInfo {
    my @parms = split(/,/,$_[1] );
    my $return_info = $_[0];

    $return_actual_type = "";
    if ($return_info =~ /(.*);(.*)/) {
	$return_info = $1;
	$return_actual_type = $2;
    }
    if ($return_info eq "0") {
	$return_type = "void";
	$return_parm_pos = 0;
    }
    elsif ($return_info =~ /^[0-9]/) {
	# We have the position but we need to find the type
	my $count = 1;
	for $parm (@parms) {
	    if ($count == $return_info) { 
		$return_type     = $parm;
		$return_type     =~ s/\s*\*$//;   # Remove *
		$return_parm_pos = $count;
	    }
	    $count ++;
	}
    }
    else {
	# Return info is a type. Find the matching location
	my $count = 1;
	$return_type = "";
	for $parm (@parms) {
	    if ($parm =~ /$return_info\s*\*/) {
		$return_parm_pos = $count;
		$return_type     = $return_info;
		last;
	    }
	    $count ++;
	}
	if ($return_type eq "") {
	    print STDERR "Warning: no return type found for $routine\n";
	}
    }
    if ($return_actual_type eq "") { $return_actual_type = $return_type; }
}
# -----------------------------------------------------------------------------
# Convert other arguments from C to C++ versions.  E.g., change the
# MPI_Datatype arg in Comm::Send from MPI_Datatype to Datatype.  Use
# (MPI_Datatype)datatype.the_real_datatype (always).
#
# HandleObjectParms( parmtype, parm )
# e.g., HandleObjectParms( MPI_Datatype, v7 )
# returns appropriate string.  If parmtype unknown, just return parm 
sub HandleObjectParm {
    my $parmtype = $_[0];
    my $parm     = $_[1];
    my $need_address = 0;
    my $newparm;

    # Check for the special case of MPI_Aint, MPI_Offset
    if ($parmtype =~ /MPI_/ && 
	! ($parmtype =~/MPI_Aint/ || $parmtype =~ /MPI_Offset/)) {
	$ctype = $parmtype;
	if ($ctype =~ /\*/) {
	    $need_address = 1;
	    $ctype =~ s/\*//;
	}
	$ctype =~ s/MPI_//;
	$lctype = lc( $ctype );
	# For derived classes, we sometimes need to know the name of the
	# top-most class, particularly for the "the_real_xxx" name.
	if (defined($mytopclass{$lctype})) {
	    $lctype = $mytopclass{$lctype};
	}

	if ($need_address) {
	    $newparm = "($parmtype)&($parm.the_real_$lctype)";
	}
	else {
	    $newparm = "($parmtype)($parm.the_real_$lctype)";
	}
	return $newparm;
    }
    elsif ($parmtype =~ /MPI_Offset\s*\*/) {
	$newparm = "&$parm";
	return $newparm;
    }
    return $parm;
}
# ----------------------------------------------------------------------------
#
# MUST DO BEFORE USABLE
# The initialization of the objects:
#   const Datatype MPI::<name>(MPI_<name>);
#   Intracomm MPI::COMM_WORLD(MPI_COMM_WORLD), SELF
#   const COMM MPI::COMM_NULL;
#   const Group MPI::GROUP_EMPTY(MPI_GROUP_EMPTY);
#   const Op MPI::<op>(MPI_<op>)
#   const int MPI::IDENT,CONGRUENT,SIMILAR,UNEQUAL
# (DONE!)
#
# static functions that are in no class  (init already done)
# Get_error_class, Wtime, Wtick, Finalize, Is_initialized
#
# Namespace wrapper
#
# Insert use of const.  Can we do this automatically, with some
# exceptions?  E.g., all Datatype, void *, Comm, Group etc.
# Only recv of void *, output of collective aren't const (?)
#
# Returned objects that are not simple types must be created with new, not
# just declared and returned.  In addition, make sure that the correct
# value is passed into the C version.  E.g.,
#   Request *v7 = new Request;
#   .... MPI_Isend( ..., &(v7->the_real_request) )
#   return *v7;
#
# ----------------------------------------------------------------------------
#
# ReadInterface( filename )
sub ReadInterface {
    my $filename =$_[0];
    open( FD, "<$filename" ) || die "Cannot open $filename\n";

    # Skip to prototypes
    while (<FD>) {
	if ( /\/\*\s*Begin Prototypes/ ) { last; }
    }

    # Read each one
    # Save as
    #$mpi_routine{name} = args;
    while (<FD>) {
	if (/\/\*\s*End Prototypes/ ) { last; }
	if (/^int\s+MPI_([A-Z][a-z0-9_]*)\s*\((.*)/) {
	    $routine_name = $1;
	    $args = $2;
	    while (! ($args =~ /;/)) {
		$args .= <FD>;
	    }
	    $args =~ s/\)\s*;//g;
	    $args =~ s/[\r\n]*//g;
	    # Special substitutions
	    $args =~ s/MPIO_Request/MPI_Request/g;
	    $lcname = lc($routine_name);
	    if (defined($special_routines{$routine_name})) {
		print "Skipping $routine_name\n" if $debug;
	    }
	    else {
		# Clear variables
		$clean_up = "";
		&clean_args;
		$mpi_routine{$routine_name} = $args;
		print "Saving $routine_name ( $args )\n" if $debug;
	    }			
	}
    }
    close( FD );
}
# ----------------------------------------------------------------------------
# Implementation of the extra functions
sub Status_methods {
    my $OUTFD = $_[0];
    
    print $OUTFD "\
    int Get_source(void) { return the_real_status.MPI_SOURCE; }
    int Get_tag(void) { return the_real_status.MPI_TAG; }
    int Get_error(void) { return the_real_status.MPI_ERROR; }
    void Set_source(int source) { the_real_status.MPI_SOURCE = source; }
    void Set_tag(int tag) { the_real_status.MPI_TAG = tag; }
    void Set_error(int error) { the_real_status.MPI_ERROR = error; }
";
}

# Clone method is a helper that adds the clone methods for the communicators
sub Clone_method {
    my $OUTFD = $_[0];
    my $classname = $_[1];
    print $OUTFD "    $classname & Clone(void) const { 
        MPI_Comm ncomm;
        MPI_Comm_dup( (MPI_Comm)the_real_comm, &ncomm); 
        $classname *clone = new $classname(ncomm); 
        return *clone; };\n";
}
sub Cartcomm_methods {
    my $OUTFD = $_[0];
    &Clone_method( $OUTFD, "Cartcomm" );
}
sub Comm_methods {
    my $OUTFD = $_[0];
    &Clone_method( $OUTFD, "Comm" );
    # Typedefs
    print $OUTFD  "
    typedef int Copy_attr_function(const Comm& oldcomm, int comm_keyval, void* extra_state, void* attribute_val_in, void* attribute_val_out, bool& flag); 
    typedef int Delete_attr_function(Comm& comm, int comm_keyval, void* attribute_val, void* extra_state); 
    typedef void Errhandler_fn(Comm &, int *, ... );

    static int Create_keyval( Copy_attr_function *, Delete_attr_function *,
                              void * );
          
    static int NULL_COPY_FN( const Comm &oldcomm, int keyval, void *ex,
    void *attr_in, void *attr_out, bool &flag ) { flag = 1; return 0;}
    static int NULL_DELETE_FN( Comm &comm, int keyval, void * attr, void *ex )
	{ return 0; }
    static int DUP_FN( const Comm &oldcomm, int keyval, void *ex,
    void *attr_in, void *attr_out, bool &flag ) { flag = 1; 
    *(void **)attr_out = attr_in; return 0;} \n";
}
sub Graphcomm_methods {
    my $OUTFD = $_[0];
    &Clone_method( $OUTFD, "Graphcomm" );
}
sub Intercomm_methods {
    my $OUTFD = $_[0];
    &Clone_method( $OUTFD, "Intercomm" );
}
sub Intracomm_methods {
    my $OUTFD = $_[0];
    &Clone_method( $OUTFD, "Intracomm" );
}

sub Op_methods {
    my $OUTFD = $_[0];
    
    print $OUTFD "
    void Init( User_function *, bool );
";
}
    
#
# To properly implement Get_error_string, we need another
# protected member in the Exception that will contain the 
# error string.
sub Exception_methods {
    my $OUTFD = $_[0];
    
    print $OUTFD "\
    int Get_error_code(void) { return the_real_exception; } 
    int Get_error_class(void) { return MPI::Get_error_class(the_real_exception); } 
    const char *Get_error_string(void) { return 0;}
";
}

sub Datatype_methods {
    my $OUTFD = $_[0];
    
    print $OUTFD "\
    void Unpack( const void *, int, void *, int, int &, const Comm & );
    void Pack( const void *, int, void *, int, int &, const Comm & );\n";
}
# ----------------------------------------------------------------------------
# We may eventually want to build separate files for each class rather than
# create a single header file.  These routines handle that, as well as
# the 
# ----------------------------------------------------------------------------
sub BeginClass {
    my $class = $_[0];
    if ($build_sep_files) {
	$filename = "${class}-cpp.h";
	open ( $OUTFD, ">$filename" ) || die "Could not open $filename\n";
	$files[$#files+1] = $filename;
	&print_header;
	print $OUTFD "namespace MPI {\n";
    }
    else {
	# Here is where we add (some) of the code to write the
	# class definition, including the destructor, assignment,
	# and compare operations.
	my $Class = $fullclassname{$class};
	my $mpi_type = $class_type{$class};
        &PrintClassHead( $OUTFD, $Class, $mpi_type, $class_friends{$class} );
    }
}

sub EndClass {
    if ($build_sep_files) {
	print $OUTFD "} // namespace MPI\n";
	close( $OUTFD );
    }
    else {
	&PrintClassTail( $OUTFD );
    }
}
# ----------------------------------------------------------------------------
# Build the replacement functions:
# 1) Generate the method definition
#      E.g., Send( void *v1, etc )
# 2) Generate the inlined method definition
#   a) Variable to hold return type, if any
#   b) Declare Temporary variables for argument processing (e.g., to hold a 
#      copy of an array)
#   c) Initialize any input temporaries (e.g., place values into the array)
#   d) Call the original MPI routine
#      using temporary variables as necessary
#   e) Copy out from any temporaries
#   f) return result value, if any
#   
# The handling of the temporary variables is done by calling a named routine
# for each parameter that identifies itself as requring special processing
# ----------------------------------------------------------------------------
#
# PrintRoutineDef( outfd, class, routine, arginfo )
sub PrintRoutineDef {
    my $OUTFD   = $_[0];
    my $class   = $_[1];
    my $routine = $_[2];
    my $arginfo = $_[3];
    my $defonly = $_[4];
    
    my $cArgs;      # The argument string of the C binding
    my $Croutine;   # Name of the MPI C binding routine to all;

    # Extract the information on the special arguments
    my $returnarg = $arginfo;
    if ($returnarg =~ /^static:/) { $returnarg =~ s/^static://; }
    my $special_args = "::";
    if ($returnarg =~ /(^[^:]+):(.*)/) {
	$returnarg = $1;
	$special_args = $2;
	$special_args = ":" . $special_args . ":";
	print "special args for $routine is $special_args\n" if $debug;
    }

    ($cArgs, $Croutine) = &GetCArgs( $class, $routine );

    &PrintMethodDef( $OUTFD, $class, $routine, $arginfo, $cArgs );    
    
    # Some methods cannot be defined yet.  In that case, we're done.
    if ($defonly || defined($defer_definition{$routine})) { 
	print $OUTFD ";\n";
	return; 
    }

    # output the body of the routine definition
    print $OUTFD "\n${indent}{\n";

    # Output any declaration needed for the return type
    &ReturnTypeDecl( $OUTFD );

#     if ($routine eq "Get_atomicity") {
# 	print STDERR "Croutine = $Croutine\n";
# 	print STDERR "cArgs = $cArgs\n";
# 	print STDERR "arginfo = $arginfo\n";
# 	print STDERR "special_args = $special_args\n";
#     }
    # Output any other declarations
    &RoutineTempDecls( $OUTFD, $routine, $cArgs, $special_args );

    # Output any initialization 
    &RoutineTempIn( $OUTFD, $routine, $cArgs, $special_args );
    
    # Output the routine call
    &PrintRoutineCall( $OUTFD, $Croutine, $class, $arginfo, $cArgs );

    # Output code for any out variables
    &RoutineTempOut( $OUTFD, $routine, $cArgs, $special_args );

    # Return any value
    &PrintReturnType( $OUTFD );
    
    # Close the definition
    print $OUTFD "${indent}}\n";
}

#
# The following is a version of PrintRoutineDef that handles the
# "MPI_STATUS_IGNORE" features.
sub PrintRoutineDefNoStatus {
    my $OUTFD   = $_[0];
    my $class   = $_[1];
    my $routine = $_[2];
    my $arginfo = $_[3];
    my $defonly = $_[4];
    
    my $cArgs;      # The argument string of the C binding
    my $Croutine;   # Name of the MPI C binding routine to all;

    &SetStatusIgnore;    # Tell the status array routine to ignore
                         # status arrays.
    # Extract the information on the special arguments
    my $returnarg = $arginfo;
    if ($returnarg =~ /^static:/) { $returnarg =~ s/^static://; }
    my $special_args = "::";
    if ($returnarg =~ /(^[^:]+):(.*)/) {
	$returnarg = $1;
	$special_args = $2;
	$special_args = ":" . $special_args . ":";
    }

    ($cArgs, $Croutine) = &GetCArgs( $class, $routine );

    $SavecArgs = $cArgs;
    $cArgs =~ s/,\s*MPI_Status\s*\*?//g;
    $cArgs =~ s/\s*MPI_Status\s*\*?\s*,//g;

    &PrintMethodDef( $OUTFD, $class, $routine, $arginfo, $cArgs );    
    
    # Some methods cannot be defined yet.  In that case, we're done.
    if ($defonly || defined($defer_definition{$routine})) { 
	print $OUTFD ";\n";
	return; 
    }

    # output the body of the routine definition
    print $OUTFD "\n${indent}{\n";

    # Output any declaration needed for the return type
    &ReturnTypeDecl( $OUTFD );

    # Output any other declarations
    &RoutineTempDecls( $OUTFD, $routine, $cArgs, $special_args );

    # Output any initialization 
    &RoutineTempIn( $OUTFD, $routine, $cArgs, $special_args );
    
    # Output the routine call
    $cArgs = $SavecArgs;
    $cArgs =~ s/\s*MPI_Status\s*\*?/%%MPI_STATUS_IGNORE%%/g;
    &PrintRoutineCall( $OUTFD, $Croutine, $class, $arginfo, $cArgs );

    # Output code for any out variables
    &RoutineTempOut( $OUTFD, $routine, $cArgs, $special_args );

    # Return any value
    &PrintReturnType( $OUTFD );
    
    # Close the definition
    print $OUTFD "${indent}}\n";

    &UnSetStatusIgnore;    # Tell the status array routine to stop ignoring
                           # status arrays.
}

# Print only the method definition
sub PrintMethodDef {
    my $OUTFD   = $_[0];
    my $class   = $_[1];
    my $routine = $_[2];
    my $arginfo = $_[3];
    my $cArgs   = $_[4];

    my $is_static = 0;
    # Process info for finding the return value info.
    # This sets global variables return_type and return_parm_pos
    my $returnarg = $arginfo;
    if ($returnarg =~ /^static:/) { 
	$returnarg =~ s/^static://; 
	$is_static = 1; 
    }
    my $special_args = "";
    if ($returnarg =~ /(^[^:]+):(.*)/) {
	$returnarg = $1;
	$special_args = $2;
    }
    &FindReturnInfo( $returnarg, $cArgs );

    $real_return_type = $return_actual_type;
    if ($return_type =~ /MPI_/) {
	$real_return_type =~ s/MPI_//;
    }
    if ($is_static) {
	print $OUTFD "${indent}static $real_return_type $routine";
    }
    else {
	print $OUTFD "${indent}$real_return_type $routine";
    }
    
    # OUTFD, C declaration, C datatype for Class, output info
    &print_args( $OUTFD, $cArgs, $class_type{$class}, $arginfo );
}

# Get the argument string of the C binding for this routine and the name
# of the C routine to use for this method
sub GetCArgs {
    my $class = $_[0];
    my $routine = $_[1];
    my $Class = $fullclassname{$class};

    print "Routine $routine in Class $class\n" if $debug;

    # Find the corresponding args.  Some C++ routines don't use the
    # natural names, so we check for that here
    $args = "";

    # Check for $Class_$routine
    my $trial_name = "${Class}_" . lc($routine);
    # We need to do this to separate MPI_Get from MPI_Info_get.
    if (defined($mpi_routine{$trial_name})) {
	$args = $mpi_routine{$trial_name};
	$mpi_routine_name = $trial_name;
	return ($args,$mpi_routine_name);
    }

    if (defined($mpi_routine{$routine})) {
	$args = $mpi_routine{$routine};
    }
    $mpi_routine_name = $routine;
    if ($args eq "") {
	# Check for an alternate name
	print STDERR "Checking for $class-$routine\n" if $debug;
	print STDERR "Trial = $trial_name\n" if $debug;
	if (defined($mpi_routine{$trial_name})) {
	    $mpi_routine_name = $trial_name;
	    $args = $mpi_routine{$mpi_routine_name};
	}
	elsif (defined($altname{"$class-$routine"})) {
	    $mpi_routine_name = $altname{"$class-$routine"};
	    $args = $mpi_routine{$mpi_routine_name};
	}
	elsif ($class eq "file") {
	    # File routines have a systematic name mapping
	    $lcroutine = lc($routine);
	    $mpi_routine_name = "File_$lcroutine";
	    $args = $mpi_routine{$mpi_routine_name};
	}
	else {
	    print STDERR "Name $routine has no known MPI routine\n";
	}
    }
    return ($args,$mpi_routine_name);
}

# Output any declaration needed for the return type
# This uses the globals $return_type and $return_parm_pos
$finalcast = "";
sub ReturnTypeDecl {
    my $OUTFD = $_[0];

    # If there is a return type, declare it here
    $finalcast = "";
    if ($return_parm_pos > 0) {
	if ($return_type =~ /MPI_/ && !($return_type =~ /MPI_Offset/) 
	    && !($return_type =~ /MPI_Aint/)) {
	    print $OUTFD "$indent    $real_return_type *v$return_parm_pos = new $real_return_type;\n";
	    $finalcast = "*";
	}
	else {
	    print $OUTFD "$indent    $return_type v$return_parm_pos;\n";
	}
    }
}
# Return value.  Uses return_parm_pos and finalcast.
sub PrintReturnType {
    my $OUTFD = $_[0];
    if ($return_parm_pos > 0) {
	print $OUTFD "$indent    return ${finalcast}v$return_parm_pos;\n";
    }
}


# Output any other declarations
sub RoutineTempDecls {
    my $OUTFD   = $_[0];
    my $routine = $_[1];
    my @parms   = split(/\s*,\s*/, $_[2] );  # the original parameter list
    my $special_args = $_[3];
    my $count   = 1;

    foreach $parm (@parms) {
	my $pos_check = ":" . $count . ":";
	if ($special_args =~ /$pos_check/) {
	    &DoSpecialArgProcessing( $OUTFD, $routine, $count, "decl" );
	}
	$count ++;
    }
}

# Output any initialization 
sub RoutineTempIn {
    my $OUTFD   = $_[0];
    my $routine = $_[1];
    my @parms   = split(/\s*,\s*/, $_[2] );  # the original parameter list
    my $special_args = $_[3];
    my $count   = 1;

    foreach $parm (@parms) {
	my $pos_check = ":" . $count . ":";
	if ($special_args =~ /$pos_check/) {
	    print "expecting $routine-$count cxxtoc\n" if $debug;
	    &DoSpecialArgProcessing( $OUTFD, $routine, $count, "cxxtoc" );
	}
	$count ++;
    }
}
    
# Output the routine call
sub PrintRoutineCall {
    my $OUTFD            = $_[0];
    my $mpi_routine_name = $_[1];
    my $class            = $_[2];
    my $arginfo          = $_[3];
    my $cArgs            = $_[4];

    print $OUTFD "$indent    MPIX_CALL( MPI_$mpi_routine_name";
    &print_call_args( $OUTFD, $cArgs, $class_type{$class}, $arginfo );
    print $OUTFD ");\n";
}

# Output code for any out variables
sub RoutineTempOut {
    my $OUTFD   = $_[0];
    my $routine = $_[1];
    my @parms   = split(/\s*,\s*/, $_[2] );  # the original parameter list
    my $special_args = $_[3];
    my $count   = 1;

    foreach $parm (@parms) {
	my $pos_check = ":" . $count . ":";
	if ($special_args =~ /$pos_check/) {
	    print "expecting $routine-$count ctocxx\n" if $debug;
	    &DoSpecialArgProcessing( $OUTFD, $routine, $count, "ctocxx" );
	}
	$count ++;
    }
}

# ----------------------------------------------------------------------------
# Routines for special processing
# ----------------------------------------------------------------------------
# This routine makes the call for a particular function for a particular
# argument position and operation
# DoSpecialArgProcessing( OUTFD, routine, arg-pos, operation )
sub DoSpecialArgProcessing {
    my $OUTFD   = $_[0];
    my $routine = $_[1];
    my $count   = $_[2];
    my $op      = $_[3];   # decl, arg, cxxtoc, ctocxx
    my $argdir;            # either in, out, inout

    print "Checking for $routine - $count\n" if $debug;
    $subname = $routine_arg_maps{"${routine}-$count"};
    if ($subname eq "") {
	$subname = $routine_arg_maps{"${class}-${routine}-$count"};
	print "Found class $class $routine $count\n" if $debug;
    }
    if ($subname =~ /([^:]*):([^:]*)(.*)/) {
	$argdir = $1;
	$subname = $2 . "_${argdir}_${op}";
	$otherarg = $3; 
	$otherarg =~ s/^://;
	print "expecting to find routine $subname\n" if $debug;
	if (defined(&$subname)) {
#	    if (op eq "methoddecl" || op eq "arg") {
		&$subname( $count );
		return 1;
#	    }
#	    else {
#		&$subname( "v$count", "l$count" );
#	    }
	}
    }
    return 0;
}
# ----------------------------------------------------------------------------
# const: added only to the declaration
# $parm is defined outside
sub const_in_methoddecl {
    my $count = $_[0];
    if (!$first) { print $OUTFD ", "; }
    print $OUTFD "const $parm v$count";
}
# We have to explicitly remove the cast
sub const_in_call {
    my $count = $_[0];
    print $OUTFD "($parm)v$count";
}
#
# bool
# convert from C int
sub bool_in_methoddecl {
    my $count = $_[0];
    if (!$first) { print $OUTFD ", "; }
    print $OUTFD "bool v$count";
}
sub bool_out_decl {
    my $count = $_[0];
    print $OUTFD "$indent    int l$count;\n";
}
sub bool_in_decl {
    my $count = $_[0];
    print $OUTFD "$indent    int l$count;\n";
}
sub bool_in_call {
    my $count = $_[0];
    print $OUTFD "l$count";
}
sub bool_out_ctocxx {
#    my $cinvar     = $_[0];
#    my $cxxoutvar  = $_[1];
    my $count      = $_[0];
    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;
    
    print $OUTFD "$indent    $cxxoutvar = $cinvar ? true : false;\n";
}
# conver to C int
sub bool_in_cxxtoc {
#    my $cxxinvar = $_[0];
#    my $coutvar  = $_[1];

    my $count     = $_[0];
    my $cxxinvar  = "v" . $count;
    my $coutvar   = "l" . $count;
    
    print $OUTFD "$indent     $coutvar = ($cxxinvar == true) ? 1 : 0;\n";
}
# ----------------------------------------------------------------------------
sub reqarray_inout_methoddecl {
    my $count = $_[0];
    if (!$first) { print $OUTFD ", "; }
    print $OUTFD "Request v$count\[]";
}
# We have to explicitly remove the cast
sub reqarray_inout_call {
    my $count = $_[0];
    print $OUTFD "l$count";
}
sub reqarray_inout_decl {
    my $count = $_[0];
    my $n     = "v$otherarg";
    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    MPI_Request *l$count = new MPI_Request[$n];\n";
}
sub reqarray_inout_cxxtoc {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    { 
            int i$count; 
            for (i$count=0;i$count<$n;i$count++) {
                l$count\[i$count] = v$count\[i$count].the_real_request;
            }
        }\n";
}
sub reqarray_inout_ctocxx {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    { 
            int i$count; 
            for (i$count=0;i$count<$n;i$count++) {
                v$count\[i$count].the_real_request = l$count\[i$count];
            }
            delete[] l$count;
        }\n";
}
# ----------------------------------------------------------------------------
$InStatusIgnore = 0;
sub SetStatusIgnore {
    $InStatusIgnore = 1;
}
sub UnSetStatusIgnore {
    $InStatusIgnore = 0;
}
sub statusarray_out_methoddecl {
    my $count = $_[0];
    if ($InStatusIgnore) { return; }
    if (!$first) { print $OUTFD ", "; }
    print $OUTFD "Status v$count\[]";
}
# We have to explicitly remove the cast
sub statusarray_out_call {
    my $count = $_[0];
    if ($InStatusIgnore) { 
	print $OUTFD "MPI_STATUSES_IGNORE";
    }
    else {
	print $OUTFD "l$count";
    }
}
sub statusarray_out_decl {
    my $count = $_[0];
    my $n     = "v$otherarg";
    if ($n =~ /-(\d*)/) { $n = $1; }
    if ($InStatusIgnore) { return; }
    print $OUTFD "$indent    MPI_Status *l$count = new MPI_Status[$n];\n";
}
sub statusarray_out_cxxtoc {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    if ($InStatusIgnore) { return; }

#    print $OUTFD "$indent    { 
#            int i$count; 
#            for (i$count=0;i$count<$n;i$count++) {
#                l$count\[i$count] = v$count\[i$count].the_real_request;
#            }
#        }\n";
}
sub statusarray_out_ctocxx {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    if ($InStatusIgnore) { return; }
    print $OUTFD "$indent    { 
            int i$count; 
            for (i$count=0;i$count<$n;i$count++) {
                v$count\[i$count].the_real_status = l$count\[i$count];
            }
            delete[] l$count;
        }\n";
}
# ----------------------------------------------------------------------------
sub boolarray_in_methoddecl {
    my $count = $_[0];
    if (!$first) { print $OUTFD ", "; }
    print $OUTFD "bool v$count\[]";
}
# We have to explicitly remove the cast
sub boolarray_in_call {
    my $count = $_[0];
    print $OUTFD "l$count";
}
sub boolarray_in_decl {
    my $count = $_[0];
    my $n     = "v$otherarg";
    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    int *l$count = new int[$n];\n";
}
sub boolarray_in_cxxtoc {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    { 
            int i$count; 
            for (i$count=0;i$count<$n;i$count++) {
                l$count\[i$count] = v$count\[i$count] == true ? 1 : 0;
            }
        }\n";
}
sub boolarray_in_ctocxx {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "
            delete[] l$count;\n";

}
# ----------------------------------------------------------------------------
sub boolarray_out_methoddecl {
    my $count = $_[0];
    if (!$first) { print $OUTFD ", "; }
    print $OUTFD "bool v$count\[]";
}
# We have to explicitly remove the cast
sub boolarray_out_call {
    my $count = $_[0];
    print $OUTFD "l$count";
}
sub boolarray_out_decl {
    my $count = $_[0];
    my $n     = "v$otherarg";
    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    int *l$count = new int[$n];\n";
}
sub boolarray_out_cxxtoc {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

}
sub boolarray_out_ctocxx {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    { 
            int i$count; 
            for (i$count=0;i$count<$n;i$count++) {
                v$count\[i$count] = l$count\[i$count];
            }
            delete[] l$count;
        }\n";
}
# ----------------------------------------------------------------------------
sub preqarray_inout_methoddecl {
    my $count = $_[0];
    if (!$first) { print $OUTFD ", "; }
    print $OUTFD "Prequest v$count\[]";
}
# We have to explicitly remove the cast
sub preqarray_inout_call {
    my $count = $_[0];
    print $OUTFD "l$count";
}
sub preqarray_inout_decl {
    my $count = $_[0];
    my $n     = "v$otherarg";
    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    MPI_Request *l$count = new MPI_Request[$n];\n";
}
sub preqarray_inout_cxxtoc {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    { 
            int i$count; 
            for (i$count=0;i$count<$n;i$count++) {
                l$count\[i$count] = v$count\[i$count].the_real_request;
            }
        }\n";
}
sub preqarray_inout_ctocxx {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    { 
            int i$count; 
            for (i$count=0;i$count<$n;i$count++) {
                v$count\[i$count].the_real_request = l$count\[i$count];
            }
            delete[] l$count;
        }\n";
}
# ----------------------------------------------------------------------------
sub dtypearray_in_methoddecl {
    my $count = $_[0];
    if (!$first) { print $OUTFD ", "; }
    print $OUTFD "Datatype *v$count";
}
# We have to explicitly remove the cast
sub dtypearray_in_call {
    my $count = $_[0];
    print $OUTFD "l$count";
}
sub dtypearray_in_decl {
    my $count = $_[0];
    my $n     = "v$otherarg";
    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    MPI_Datatype *l$count = new MPI_Datatype[$n];\n";
}
sub dtypearray_in_cxxtoc {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent    { 
            int i$count; 
            for (i$count=0;i$count<$n;i$count++) {
                l$count\[i$count] = v$count\[i$count].the_real_datatype;
            }
        }\n";
}
# Use this to delete the array
sub dtypearray_in_ctocxx {
    my $count      = $_[0];
    my $n          = "v$otherarg";

    my $cinvar     = "l" . $count;
    my $cxxoutvar  = "v" . $count;

    if ($n =~ /-(\d*)/) { $n = $1; }
    print $OUTFD "$indent                delete[] l$count;\n";
}
# ----------------------------------------------------------------------------
# These are used to convert int *foo into int &foo
sub refint_in_methoddecl {
    my $count = $_[0];
    if (!$first) { print $OUTFD ", "; }
    print $OUTFD "int &v$count";
}
# We have to explicitly remove the cast
sub refint_in_call {
    my $count = $_[0];
    print $OUTFD "&v$count";
}
# ----------------------------------------------------------------------------
sub ptrref_inout_methoddecl {
    my $count = $_[0];
    print $OUTFD "void *&v$count";
}
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
#
# ISSUES NOT YET HANDLED
# ----------------------------------------------------------------------------
# This tool becomes particularly interesting if it allows custom generation
# of a mpicxx.h header file that contains references to only the
# requested routines (and even classes; e.g., no Groups if no-one is using
# them).
#
# Pack_size, Pack, and Unpack cannot be defined within the Datatype
# class definition because they also need Comm, and Comm needs datatype.
# We need to replace this with
#   Just provide the Pack_size, Pack, Unpack prototypes in the Datatype
#   class definition
#   Add these to the end
#
# Routines with arrays of aggregate types (e.g., arrays of Datatypes) 
# really require special processing.  We need to either do something like
# is done for the Fortran routines (for any routine with special needs, 
# enumerate which args require special handling and name the routine) 
# or simply provide hand-written code for the internals of those operations.
#
# class Comm should be pure virtual.  This makes it hard to define
# COMM_NULL.  One possibility is to use a base class that contains 
# only the null function and operation, then Comm as pure virtual, then
# the various communicators.  We may also need methods to promote 
# cart to intracomm and graph to intracomm.  
#
#
# static functions.
# Rather than find an the class that is the input, the static functions
# don't have a current object.  These are in the class but
# don't have a "this".
# These are, however, members of the class.  
