| 1 | # Copyright (C) 2006,2007 Andreas Schaefer <gentryx@gmx.de> |
|---|
| 2 | # |
|---|
| 3 | # This program is free software; you can redistribute it and/or modify |
|---|
| 4 | # it under the terms of the GNU General Public License as published by |
|---|
| 5 | # the Free Software Foundation; either version 2 of the License, or |
|---|
| 6 | # (at your option) any later version. |
|---|
| 7 | # |
|---|
| 8 | # This program is distributed in the hope that it will be useful, but |
|---|
| 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 11 | # General Public License for more details. |
|---|
| 12 | # |
|---|
| 13 | # You should have received a copy of the GNU General Public License |
|---|
| 14 | # along with this program; if not, write to the Free Software |
|---|
| 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|---|
| 16 | # 02110-1301 USA. |
|---|
| 17 | |
|---|
| 18 | require 'pathname' |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | # Here we generate the C++ code that will in turn create the typemaps |
|---|
| 22 | # for MPI. |
|---|
| 23 | class MPIGenerator |
|---|
| 24 | def initialize(template_path="./", namespace=nil) |
|---|
| 25 | @path = Pathname.new(template_path) |
|---|
| 26 | if namespace |
|---|
| 27 | @namespace_guard = namespace.downcase + "_" |
|---|
| 28 | @namespace_begin = "namespace #{namespace} {\n" |
|---|
| 29 | @namespace_end = "};\n" |
|---|
| 30 | else |
|---|
| 31 | @namespace_guard = "" |
|---|
| 32 | @namespace_begin = "" |
|---|
| 33 | @namespace_end = "" |
|---|
| 34 | end |
|---|
| 35 | end |
|---|
| 36 | |
|---|
| 37 | def simple_name(name) |
|---|
| 38 | name.gsub(/[ :<>]+/, '_') |
|---|
| 39 | end |
|---|
| 40 | |
|---|
| 41 | # returns the code of a method suitable to create a typemap for the |
|---|
| 42 | # given klass. |
|---|
| 43 | def generate_single_map(klass, members, parents) |
|---|
| 44 | ret = File.read(@path + "template_generatesinglemap.cc"); |
|---|
| 45 | |
|---|
| 46 | ret.gsub!(/KLASS_NAME/, simple_name(klass)) |
|---|
| 47 | ret.gsub!(/KLASS/, klass) |
|---|
| 48 | ret.gsub!(/NUM_MEMBERS/, members.size.to_s) |
|---|
| 49 | |
|---|
| 50 | member_specs1 = members.map do |name, properties| |
|---|
| 51 | " MemberSpec(MPI::Get_address(&obj->#{name}), #{properties[:type]}, #{properties[:cardinality]})" |
|---|
| 52 | end |
|---|
| 53 | member_specs2 = [] |
|---|
| 54 | if parents |
|---|
| 55 | member_specs2 = parents.map do |name, mpiname| |
|---|
| 56 | " MemberSpec(MPI::Get_address((#{name}*)obj), #{mpiname}, 1)" |
|---|
| 57 | end |
|---|
| 58 | end |
|---|
| 59 | member_specs = member_specs1 + member_specs2 |
|---|
| 60 | ret.sub!(/ *MEMBERSPECS/, member_specs.join(",\n")) |
|---|
| 61 | end |
|---|
| 62 | |
|---|
| 63 | # The Typemap Class needs a header file, declaring all the static |
|---|
| 64 | # variables, macros and so on. This method will generate it's code. |
|---|
| 65 | def generate_header(classes, datatype_map, headers, header_pattern=nil, header_replacement=nil) |
|---|
| 66 | ret = File.read(@path + "template_typemaps.h"); |
|---|
| 67 | ret.gsub!(/HEADERS/, map_headers(headers, header_pattern, header_replacement)) |
|---|
| 68 | ret.gsub!(/NAMESPACE_GUARD/, @namespace_guard) |
|---|
| 69 | ret.gsub!(/NAMESPACE_BEGIN\n/, @namespace_begin) |
|---|
| 70 | ret.gsub!(/NAMESPACE_END\n/, @namespace_end) |
|---|
| 71 | |
|---|
| 72 | class_vars = classes.map do |klass| |
|---|
| 73 | klass_name = datatype_map[klass].sub(/MPI::/, "") |
|---|
| 74 | " extern Datatype #{klass_name};" |
|---|
| 75 | end |
|---|
| 76 | ret.sub!(/.*CLASS_VARS/, class_vars.join("\n")) |
|---|
| 77 | |
|---|
| 78 | mapgens = classes.map do |klass| |
|---|
| 79 | " static MPI::Datatype generateMap#{simple_name(klass)}();" |
|---|
| 80 | end |
|---|
| 81 | ret.sub!(/.*MAPGEN_DECLARATIONS/, mapgens.join("\n")) |
|---|
| 82 | |
|---|
| 83 | lookup_types = (Datatype.new.keys.sort + classes).uniq |
|---|
| 84 | lookups = lookup_types.map do |klass| |
|---|
| 85 | " static inline MPI::Datatype lookup(#{klass}*) { return #{datatype_map[klass]}; }" |
|---|
| 86 | end |
|---|
| 87 | ret.sub!(/.*LOOKUP_DEFINITIONS/, lookups.join("\n")) |
|---|
| 88 | |
|---|
| 89 | return ret |
|---|
| 90 | end |
|---|
| 91 | |
|---|
| 92 | def map_headers(headers, header_pattern, header_replacement) |
|---|
| 93 | h = headers.map do |header| |
|---|
| 94 | header_name = header |
|---|
| 95 | if !header_replacement.nil? |
|---|
| 96 | header_name = header.gsub(header_pattern, header_replacement) |
|---|
| 97 | end |
|---|
| 98 | "#include <#{header_name}>" |
|---|
| 99 | end |
|---|
| 100 | return h.join("\n") |
|---|
| 101 | end |
|---|
| 102 | |
|---|
| 103 | # The Typemap Class needs a source file, containing all the method |
|---|
| 104 | # bodys and variable definitions. This methods creates the code. |
|---|
| 105 | def generate_source(topological_class_sortation, datatype_map, resolved_classes, resolved_parents) |
|---|
| 106 | ret = File.read(@path + "template_typemaps.cpp"); |
|---|
| 107 | |
|---|
| 108 | class_vars = topological_class_sortation.map do |klass| |
|---|
| 109 | klass_name = datatype_map[klass].sub(/MPI::/, "") |
|---|
| 110 | " Datatype #{klass_name};" |
|---|
| 111 | end |
|---|
| 112 | ret.sub!(/ *CLASS_VARS/, class_vars.join("\n")) |
|---|
| 113 | ret.sub!(/NAMESPACE_BEGIN\n/, @namespace_begin) |
|---|
| 114 | ret.sub!(/NAMESPACE_END\n/, @namespace_end) |
|---|
| 115 | |
|---|
| 116 | methods = topological_class_sortation.map do |klass| |
|---|
| 117 | generate_single_map(klass, resolved_classes[klass], resolved_parents[klass]) |
|---|
| 118 | end |
|---|
| 119 | ret.sub!(/METHOD_DEFINITIONS/, methods.join("\n")) |
|---|
| 120 | |
|---|
| 121 | assignments = topological_class_sortation.map do |klass| |
|---|
| 122 | " #{datatype_map[klass]} = generateMap#{simple_name(klass)}();" |
|---|
| 123 | end |
|---|
| 124 | ret.sub!(/.+ASSIGNMENTS/, assignments.join("\n")) |
|---|
| 125 | |
|---|
| 126 | return ret; |
|---|
| 127 | end |
|---|
| 128 | |
|---|
| 129 | # wraps the code generation for multiple typemaps. |
|---|
| 130 | def generate_forest(resolved_classes, resolved_parents, datatype_map, topological_class_sortation, headers, header_pattern=nil, header_replacement=nil) |
|---|
| 131 | return [generate_header(topological_class_sortation, datatype_map, headers, header_pattern, header_replacement), |
|---|
| 132 | generate_source(topological_class_sortation, datatype_map, resolved_classes, resolved_parents)] |
|---|
| 133 | end |
|---|
| 134 | end |
|---|