# IfcOpenShell - IFC toolkit and geometry engine # Copyright (C) 2021 Thomas Krijnen # # This file is part of IfcOpenShell. # # IfcOpenShell is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # IfcOpenShell is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . import codegen import templates from schema import OrderedCaseInsensitiveDict from header import USE_VIRTUAL_INHERITANCE class Implementation(codegen.Base): def __init__(self, mapping): enumeration_functions = [] entity_implementations = [] schema_entity_statements = [] schema_name = mapping.schema.name.capitalize() schema_name_upper = mapping.schema.name.upper() stringify = lambda s: '"%s"' % s cat = lambda vs: "".join(vs) catc = lambda vs: ", ".join(vs) catnl = lambda vs: "\n".join(vs) cator = lambda vs: " || ".join(vs) nl = lambda s: "%s\n" % s if len(s) else s write = lambda str, **kwargs: enumeration_functions.append(str % kwargs) collections_by_type = ( ("entity", mapping.schema.entities), ("type_declaration", mapping.schema.simpletypes), ("select_type", mapping.schema.selects), ("enumeration_type", mapping.schema.enumerations), ) self.names = [] for _, collection in collections_by_type: self.names.extend(collection.keys()) self.names.sort(key=str.lower) for name, enum in mapping.schema.enumerations.items(): short_name = name[:-4] if name.endswith("Enum") else name context = locals() write( templates.enumeration_function, max_id=len(enum.values), name=name, schema_name=schema_name, schema_name_upper=schema_name_upper, index_in_schema=self.names.index(str(name)), values=catc(map(stringify, enum.values)), from_string_statements=catnl( templates.enum_from_string_stmt % dict(context, **locals()) for value in enum.values ), ) if USE_VIRTUAL_INHERITANCE: for name, enum in mapping.schema.selects.items(): write( templates.select_function, name=name, schema_name=schema_name, schema_name_upper=schema_name_upper, index_in_schema=self.names.index(str(name)), ) write = lambda str, **kwargs: entity_implementations.append(str % kwargs) for name, type in mapping.schema.entities.items(): parent_type_test = ( "" if not type.supertypes or len(type.supertypes) != 1 else templates.parent_type_test % (type.supertypes[0]) ) constructor_arguments = mapping.get_assignable_arguments(type, include_derived=True) constructor_arguments_str = catc( "%(full_type)s v%(index)d_%(name)s" % a for a in constructor_arguments if not a["is_derived"] ) attributes = [] constructor_implementations = [] write_attr = lambda str, **kwargs: attributes.append(str % kwargs) for arg in constructor_arguments: if not arg["is_inherited"] and not arg["is_derived"]: def find_template(arg): simple = mapping.schema.is_simpletype(arg["list_instance_type"]) select = arg["list_instance_type"] == "IfcUtil::IfcBaseClass" express = ( mapping.flatten_type_string(arg["list_instance_type"]) in mapping.express_to_cpp_typemapping ) if arg["is_enum"]: return templates.get_attr_stmt_enum elif arg["is_nested"] and arg["is_templated_list"]: return templates.get_attr_stmt_nested_array elif arg["is_templated_list"] and not (select or simple or express): return templates.get_attr_stmt_array elif arg["non_optional_type"].endswith("*"): return templates.get_attr_stmt_entity else: return templates.get_attr_stmt null_check = "" if arg["is_optional"]: attr_check = "if(get_attribute_value(%d).isNull()) { return %%s; }" % (arg["index"] - 1,) if "boost::optional" in arg["full_type"]: null_check = attr_check % "boost::none" else: null_check = attr_check % "nullptr" tmpl = find_template(arg) write_attr( templates.const_function, class_name=name, name=arg["name"], arguments="", schema_name=schema_name, schema_name_upper=schema_name_upper, return_type=arg["full_type"], body=tmpl % { "index": arg["index"] - 1, "type": arg["full_type"].replace("::Value", ""), "non_optional_type": arg["non_optional_type"].replace("::Value", ""), "non_optional_type_no_pointer": arg["non_optional_type"] .replace("::Value", "") .replace("*", ""), "list_instance_type": arg["list_instance_type"], "null_check": null_check, }, ) def find_template(arg): simple = mapping.schema.is_simpletype(arg["list_instance_type"]) select = arg["list_instance_type"] == "IfcUtil::IfcBaseClass" express = arg["list_instance_type"] in mapping.express_to_cpp_typemapping if arg["is_enum"]: return templates.set_attr_stmt_enum elif arg["is_templated_list"] and not (select or simple or express): return templates.set_attr_stmt_array elif arg["full_type"].endswith("*"): return templates.set_attr_instance else: return templates.set_attr_stmt tmpl = find_template(arg) write_attr( templates.function, class_name=name, name="set%s" % arg["name"], arguments="%s v" % arg["full_type"], return_type="void", schema_name=schema_name, schema_name_upper=schema_name_upper, body=tmpl % { "index": arg["index"] - 1, "type": arg["full_type"].replace("::Value", ""), "non_optional_type": arg["non_optional_type"].replace("::Value", ""), "star_if_optional": "*" if "boost::optional" in arg["full_type"] else "", "check_optional_set_begin": "if (v) {" if "boost::optional" in arg["full_type"] else "", "check_optional_set_else": ( "} else {" if "boost::optional" in arg["full_type"] else "if constexpr (false)" ), "check_optional_set_end": "}" if "boost::optional" in arg["full_type"] else "", }, ) if arg["is_derived"]: constructor_implementations.append(templates.constructor_stmt_derived % {"index": arg["index"] - 1}) else: is_optional_non_naked_ptr = arg["is_optional"] and not arg["non_optional_type"].endswith("*") arg_name = "v%(index)d_%(name)s" % arg deref_name = ("*%s" % arg_name) if is_optional_non_naked_ptr else arg_name tmpl = ( templates.constructor_stmt_array if arg["is_templated_list"] else ( templates.constructor_stmt_enum if arg["is_enum"] else ( templates.constructor_stmt_instance if arg["full_type"].endswith("*") else templates.constructor_stmt ) ) ) impl = tmpl % { "name": deref_name, "index": arg["index"] - 1, "type": arg["non_optional_type"].replace("::Value", ""), } if is_optional_non_naked_ptr: impl = templates.constructor_stmt_optional % { "name": arg_name, "index": arg["index"] - 1, "stmt": impl, } constructor_implementations.append(impl) def get_attribute_index(entity, attr_name): related_entity = mapping.schema.entities[entity] return [ a["name"].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True) ].index(attr_name.lower()) inverse = [ templates.const_function % { "class_name": name, "schema_name": schema_name, "schema_name_upper": schema_name_upper, "name": i.name, "arguments": "", "return_type": "::%s::%s::list::ptr" % (schema_name, i.entity), "body": templates.get_inverse % { "type": i.entity, "type_index": self.names.index(i.entity), "index": get_attribute_index(i.entity, i.attribute), "schema_name": schema_name, "schema_name_upper": schema_name_upper, }, } for i in type.inverse ] superclass = ( "%s(std::move(e))" % type.supertypes[0] if len(type.supertypes) == 1 else "IfcUtil::IfcBaseEntity(std::move(e))" ) superclass_num_attrs = ( "%s(IfcEntityInstanceData(in_memory_attribute_storage(%%d)))" % type.supertypes[0] if len(type.supertypes) == 1 else "IfcUtil::IfcBaseEntity(IfcEntityInstanceData(in_memory_attribute_storage(%d)))" ) % len(constructor_arguments) write( templates.entity_implementation, name=name, parent_type_test=parent_type_test, constructor_arguments=constructor_arguments_str, constructor_implementation=cat(constructor_implementations), attributes=nl(catnl(attributes)), inverse=nl(catnl(inverse)), superclass=superclass, superclass_num_attrs=superclass_num_attrs, schema_name=schema_name, schema_name_upper=schema_name_upper, index_in_schema=self.names.index(str(name)), ) selectable_simple_types = sorted( set(sum([b.values for a, b in mapping.schema.selects.items()], [])) & set(map(str, mapping.schema.types.keys())) ) schema_entity_statements += [ templates.schema_entity_stmt % locals() for name, type in mapping.schema.simpletypes.items() ] schema_entity_statements += [ templates.schema_entity_stmt % locals() for name, type in mapping.schema.enumerations.items() ] schema_entity_statements += [ templates.schema_entity_stmt % locals() for name, type in mapping.schema.entities.items() ] enumerable_types = sorted( set( [name for name, type in mapping.schema.types.items()] + [name for name, type in mapping.schema.entities.items()] ) ) max_len = max(map(len, enumerable_types)) type_name_strings = catc(map(stringify, enumerable_types)) string_map_statements = [ templates.string_map_statement % {"uppercase_name": name.upper(), "name": name, "padding": " " * (max_len - len(name))} for name in enumerable_types ] enumeration_index_by_str = OrderedCaseInsensitiveDict((j, i) for i, j in enumerate(enumerable_types)) def get_parent_id(s): e = mapping.schema.entities.get(s) if e and e.supertypes: return enumeration_index_by_str[e.supertypes[0]] else: return -1 parent_type_statements = ",".join(map(str, map(get_parent_id, enumerable_types))) max_id = len(enumerable_types) simple_type_statements = cator("v == Type::%s" % name for name in selectable_simple_types) simple_type_impl = [] for class_name, type in mapping.schema.simpletypes.items(): type_str = mapping.make_type_string(mapping.flatten_type_string(type)) attr_type = mapping.make_argument_type(type) superclass = mapping.simple_type_parent(class_name) or "IfcUtil::IfcBaseType" simpletype_impl_is = ( templates.simpletype_impl_is_with_supertype if superclass else templates.simpletype_impl_is_without_supertype ) constructor = templates.constructor_single_initlist # if superclass else templates.constructor simpletype_impl_cast = ( templates.simpletype_impl_cast_templated if mapping.is_templated_list(type) else templates.simpletype_impl_cast ) simpletype_impl_constructor = ( templates.simpletype_impl_constructor_templated if mapping.is_templated_list(type) else templates.simpletype_impl_constructor ) def compose(params, schema_name=schema_name, schema_name_upper=schema_name_upper): class_name, attr_type, superclass, superclass_init, name, tmpl, return_type, args, body = params index_in_schema = self.names.index(str(class_name)) underlying_type = mapping.list_instance_type(type) arguments = ",".join(args) body = body % locals() return tmpl % locals() simple_type_impl.append(templates.simpletype_impl_comment % {"name": class_name}) simple_type_impl.extend( map( compose, map( lambda x: (class_name, attr_type, superclass) + x, ( ( "", "Class", templates.function, "const IfcParse::type_declaration&", (), templates.simpletype_impl_class, ), ( "", "declaration", templates.const_function, "const IfcParse::type_declaration&", (), templates.simpletype_impl_declaration, ), ( "std::move(e)", "", constructor, "", ("IfcEntityInstanceData&& e",), "", ), ( ( "", "", constructor, "", ("%s v" % type_str,), ( "set_attribute_value(0, v%s);" % ("->generalize()" if mapping.is_templated_list(type) else "") ), ) if mapping.simple_type_parent(class_name) is None else ("v", "", constructor, "", ("%s v" % type_str,), "") ), ("", "", templates.cast_function, type_str, (), simpletype_impl_cast), ), ), ) ) simple_type_impl.append("") """ external_definitions = ( [("extern entity* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.entities.keys()] + [ ("extern type_declaration* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.simpletypes.keys() ] + [ ("extern enumeration_type* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.enumerations.keys() ] + [ ("extern select_type* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.selects.keys() ] ) """ external_definitions = ["extern declaration* %s_types[%d];" % (schema_name_upper, len(self.names))] self.str = templates.implementation % { "schema_name_upper": schema_name_upper, "schema_name": schema_name, "max_id": max_id, "enumeration_functions": cat(enumeration_functions), "schema_entity_statements": catnl(schema_entity_statements), "type_name_strings": type_name_strings, "string_map_statements": catnl(string_map_statements), "simple_type_statement": simple_type_statements, "parent_type_statements": parent_type_statements, "entity_implementations": catnl(entity_implementations), "simple_type_impl": catnl(simple_type_impl), "external_definitions": catnl(external_definitions), } self.schema_name = mapping.schema.name.capitalize() self.file_name = "%s.cpp" % self.schema_name def __repr__(self): return self.str Generator = Implementation