/// <summary> /// Prepares a transformer from C++ to C# model. /// </summary> /// <typeparam name="TCppElement">The C++ type of data to process</typeparam> /// <param name="transform">The transform.</param> /// <param name="typeToProcess">The type to process.</param> private IEnumerable <TCsElement> PrepareTransform <TCppElement, TCsElement>(CppModule cppModule, ITransformPreparer <TCppElement, TCsElement> transform) where TCppElement : CppElement where TCsElement : CsBase { var csElements = new List <TCsElement>(); // Predefine all structs, typedefs and interfaces foreach (var cppInclude in cppModule.Includes) { foreach (var cppItem in cppInclude.Iterate <TCppElement>()) { Logger.RunInContext( cppItem.ToString(), () => { // If already mapped, it means that there is already a predefined mapping if (typeRegistry.FindBoundType(cppItem.Name) == null) { var csElement = transform.Prepare(cppItem); if (csElement != null) { csElements.Add(csElement); } } }); } } return(csElements); }
/// <summary> /// Maps the C++ struct to C# struct. /// </summary> /// <param name="csStruct">The c sharp struct.</param> public override void Process(CsStruct csStruct) { // TODO: this mapping must be robust. Current calculation for field offset is not always accurate for union. // TODO: need to handle align/packing correctly. // If a struct was already mapped, then return immediately // The method MapStruct can be called recursively if (csStruct.IsFullyMapped) { return; } // Set IsFullyMappy in order to avoid recursive mapping csStruct.IsFullyMapped = true; // Get the associated CppStruct and CSharpTag var cppStruct = (CppStruct)csStruct.CppElement; bool hasMarshalType = csStruct.HasMarshalType; // If this structure need to me moved to another container, move it now foreach (var keyValuePair in _mapMoveStructToInner) { if (keyValuePair.Key.Match(csStruct.CppElementName).Success) { string cppName = keyValuePair.Key.Replace(csStruct.CppElementName, keyValuePair.Value); var destSharpStruct = (CsStruct)typeRegistry.FindBoundType(cppName); // Remove the struct from his container csStruct.Parent.Remove(csStruct); // Add this struct to the new container struct destSharpStruct.Add(csStruct); } } // Current offset of a field int currentFieldAbsoluteOffset = 0; // Last field offset int previousFieldOffsetIndex = -1; // Size of the last field int previousFieldSize = 0; // int maxSizeOfField = 0; bool isNonSequential = false; int cumulatedBitOffset = 0; var inheritedStructs = new Stack <CppStruct>(); var currentStruct = cppStruct; while (currentStruct != null && currentStruct.Base != currentStruct.Name) { inheritedStructs.Push(currentStruct); currentStruct = typeRegistry.FindBoundType(currentStruct.Base)?.CppElement as CppStruct; } while (inheritedStructs.Count > 0) { currentStruct = inheritedStructs.Pop(); int fieldCount = currentStruct.IsEmpty ? 0 : currentStruct.Items.Count; // ------------------------------------------------------------------------------- // Iterate on all fields and perform mapping // ------------------------------------------------------------------------------- for (int fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { var cppField = (CppField)currentStruct.Items[fieldIndex]; Logger.RunInContext(cppField.ToString(), () => { var csField = factory.Create(cppField); csStruct.Add(csField); // Get name csField.Name = NamingRules.Rename(cppField); var fieldHasMarshalType = csField.PublicType != csField.MarshalType || (csField.PublicType is CsStruct fieldStruct && fieldStruct.HasMarshalType) || csField.IsArray; // BoolToInt doesn't generate native Marshaling although they have a different marshaller if (((!csField.IsBoolToInt || csField.IsArray) && fieldHasMarshalType) || csField.Relation != null) { hasMarshalType = true; } // If last field has same offset, then it's a union // CurrentOffset is not moved if (isNonSequential && previousFieldOffsetIndex != cppField.Offset) { previousFieldSize = maxSizeOfField; maxSizeOfField = 0; isNonSequential = false; } currentFieldAbsoluteOffset += previousFieldSize; var fieldAlignment = (csField.MarshalType ?? csField.PublicType).CalculateAlignment(); // If field alignment is < 0, then we have a pointer somewhere so we can't align if (fieldAlignment > 0) { // otherwise, align the field on the alignment requirement of the field int delta = (currentFieldAbsoluteOffset % fieldAlignment); if (delta != 0) { currentFieldAbsoluteOffset += fieldAlignment - delta; } } // Get correct offset (for handling union) csField.Offset = currentFieldAbsoluteOffset; // Handle bit fields : calculate BitOffset and BitMask for this field if (previousFieldOffsetIndex != cppField.Offset) { cumulatedBitOffset = 0; } if (cppField.IsBitField) { int lastCumulatedBitOffset = cumulatedBitOffset; cumulatedBitOffset += cppField.BitOffset; csField.BitMask = ((1 << cppField.BitOffset) - 1); csField.BitOffset = lastCumulatedBitOffset; } var nextFieldIndex = fieldIndex + 1; if ((previousFieldOffsetIndex == cppField.Offset) || (nextFieldIndex < fieldCount && ((CppField)currentStruct.Items[nextFieldIndex]).Offset == cppField.Offset)) { if (previousFieldOffsetIndex != cppField.Offset) { maxSizeOfField = 0; } maxSizeOfField = csField.Size > maxSizeOfField ? csField.Size : maxSizeOfField; isNonSequential = true; csStruct.ExplicitLayout = true; previousFieldSize = 0; } else { previousFieldSize = csField.Size; } previousFieldOffsetIndex = cppField.Offset; });
/// <summary> /// Gets the C# type from a C++ type. /// </summary> /// <typeparam name="T">The C# type to return</typeparam> /// <param name="marshallable">The marshallable element to create the C# type from.</param> /// <returns>An instantiated C# type</returns> private T CreateCore <T>(CppMarshallable marshallable) where T : CsMarshalBase, new() { CsTypeBase publicType = null; CsTypeBase marshalType = null; var csMarshallable = new T { CppElement = marshallable, IsArray = marshallable.IsArray, HasPointer = !string.IsNullOrEmpty(marshallable.Pointer) && (marshallable.Pointer.Contains("*") || marshallable.Pointer.Contains("&")), }; // TODO: handle multidimensional arrays // Calculate ArrayDimension int arrayDimensionValue = 0; if (marshallable.IsArray) { if (string.IsNullOrEmpty(marshallable.ArrayDimension)) { arrayDimensionValue = 0; } else if (!int.TryParse(marshallable.ArrayDimension, out arrayDimensionValue)) { arrayDimensionValue = 1; } } // If array Dimension is 0, then it is not an array if (arrayDimensionValue == 0) { marshallable.IsArray = false; csMarshallable.IsArray = false; } csMarshallable.ArrayDimensionValue = arrayDimensionValue; string publicTypeName = marshallable.GetTypeNameWithMapping(); switch (publicTypeName) { case "char": publicType = typeRegistry.ImportType(typeof(byte)); if (csMarshallable.HasPointer) { publicType = typeRegistry.ImportType(typeof(string)); } if (csMarshallable.IsArray) { publicType = typeRegistry.ImportType(typeof(string)); marshalType = typeRegistry.ImportType(typeof(byte)); } break; case "wchar_t": publicType = typeRegistry.ImportType(typeof(char)); csMarshallable.IsWideChar = true; if (csMarshallable.HasPointer) { publicType = typeRegistry.ImportType(typeof(string)); } if (csMarshallable.IsArray) { publicType = typeRegistry.ImportType(typeof(string)); marshalType = typeRegistry.ImportType(typeof(char)); } break; default: // If CppType is an array, try first to get the binding for this array if (marshallable.IsArray) { publicType = typeRegistry.FindBoundType(publicTypeName + "[" + marshallable.ArrayDimension + "]"); } // Else get the typeName if (publicType == null) { // Try to get a declared type // If it fails, then this type is unknown publicType = typeRegistry.FindBoundType(publicTypeName); if (publicType == null) { logger.Fatal("Unknown type found [{0}]", publicTypeName); } } else { csMarshallable.ArrayDimensionValue = 0; csMarshallable.IsArray = false; } // By default, use the underlying native type as the marshal type // if it differs from the public type. marshalType = typeRegistry.FindBoundType(marshallable.TypeName); if (publicType == marshalType) { marshalType = null; } if (marshalType == null) { // Otherwise, get the registered marshal type if one exists marshalType = typeRegistry.FindBoundMarshalType(publicTypeName); } if (publicType is CsStruct csStruct) { // If a structure was not already parsed, then parse it before going further if (!csStruct.IsFullyMapped) { RequestStructProcessing?.Invoke(csStruct); } if (!csStruct.IsFullyMapped) // No one tried to map the struct so we can't continue. { logger.Fatal($"No struct processor processed {csStruct.QualifiedName}. Cannot continue processing"); } // If referenced structure has a specialized marshalling, then use the structure's built-in marshalling if (csStruct.HasMarshalType && !csMarshallable.HasPointer) { marshalType = publicType; } } else if (publicType is CsEnum referenceEnum) { marshalType = null; // enums don't need a marshal type. They can always marshal as their underlying type. } break; } // Set bool to int conversion case csMarshallable.IsBoolToInt = marshalType is CsFundamentalType marshalFundamental && IsIntegerFundamentalType(marshalFundamental) && publicType is CsFundamentalType publicFundamental && publicFundamental.Type == typeof(bool); if (publicType.QualifiedName == globalNamespace.GetTypeName(WellKnownName.PointerSize)) { marshalType = typeRegistry.ImportType(typeof(IntPtr)); } // Present void* elements as IntPtr. Marshal strings as IntPtr if (csMarshallable.HasPointer) { if (publicTypeName == "void") { publicType = typeRegistry.ImportType(typeof(IntPtr)); marshalType = typeRegistry.ImportType(typeof(IntPtr)); } else if (publicType == typeRegistry.ImportType(typeof(string))) { marshalType = typeRegistry.ImportType(typeof(IntPtr)); } } csMarshallable.PublicType = publicType; csMarshallable.MarshalType = marshalType ?? publicType; csMarshallable.Relations = RelationParser.ParseRelation(marshallable.GetMappingRule().Relation, logger); return(csMarshallable); }