public void RegisterCandidates(TypeMapper mapper, List <CppFunction> functions) { string[] prefixes = { "Is", "Has", "Get" }; foreach (var f in functions) { var pi = new PropertyInfo(mapper); string name = mapper.RenameForApi(f.Name, true); string prefix = prefixes.FirstOrDefault(p => name.StartsWith(p)); if (prefix != null) { if (f.Parameters.Count == 0 && !f.IsConstructor) { pi.GetterFunction = f; pi.GettetApiName = name; pi.PropertyName = name.Remove(0, prefix.Length); // looking for setter: CppFunction setter = functions.FirstOrDefault(sf => sf.Parameters.Count == 1 && mapper.RenameForApi(sf.Name, true).StartsWith("Set")); if (setter != null && f.IsStatic() == setter.IsStatic() && !setter.IsConstructor) { pi.SetterFunction = setter; pi.SetterApiName = mapper.RenameForApi(setter.Name, true); } } } _candidates.Add(pi); } }
/// <param name="dllImportPath">will be used as the first argument in [DllImport]. Can be a path to some constant</param> public static void Generate(TypeMapper mapper, TemplateManager templateManager, string @namespace, string dllImportPath, string outCFile, string outCsFile) { var csFileSb = new StringBuilder(); var cFileSb = new StringBuilder(); foreach (CppClassContainer cppClass in mapper.GetAllClasses()) { // Header for C types: cFileSb.AppendLine(); cFileSb.AppendLine(); cFileSb.Append("/************* "); cFileSb.Append(cppClass.IsGlobal ? "Global functions:" : cppClass.Class.GetFullTypeName()); cFileSb.Append(" *************/"); cFileSb.AppendLine(); cFileSb.AppendLine(); var csDllImportsSb = new StringBuilder(); var csApiSb = new StringBuilder(); var allFunctions = new List <CppFunction>(); // filter out functions we are not going to bind: foreach (var function in cppClass.Functions) { if (function.Visibility == CppVisibility.Private) { continue; } if (mapper.IsMethodMarkedAsUnsupported(function) || !mapper.IsSupported(function.ReturnType.GetDisplayName()) || !function.Parameters.All(p => mapper.IsSupported(p.Type.GetDisplayName())) || function.IsOperator() || function.IsCopyConstructor()) { cFileSb.AppendLine($"//NOT_BOUND:".PadRight(32) + function); continue; } allFunctions.Add(function); } var propertyGenerator = new PropertyGenerator(); propertyGenerator.RegisterCandidates(mapper, allFunctions); foreach (CppFunction function in allFunctions) { // Type_MethodName_argsMask string flatFunctionName = $"{cppClass.Name}_{function.Name}_{function.ParametersMask()}"; var cfunctionWriter = new FunctionWriter(); var dllImportWriter = new FunctionWriter(); var apiFunctionWriter = new FunctionWriter(); dllImportWriter .Attribute($"[DllImport({dllImportPath}, CallingConvention = CallingConvention.Cdecl)]") .Private() .Static() .Extern(); apiFunctionWriter .SummaryComment(function.Comment?.ChildrenToString()); var propertyInfo = propertyGenerator.AsPropertyCandidate(function); if (propertyInfo == null) { apiFunctionWriter.Public(); } else { // TODO: convert to properties //apiFunctionWriter.Private(); //csApiSb.AppendLine(propertyInfo.GenerateProperty().Tabify(2)); apiFunctionWriter.Public(); } if (function.IsStatic() || cppClass.IsGlobal) { apiFunctionWriter.Static(); } if (function.IsConstructor) { cfunctionWriter.ReturnType(cppClass.Class.GetFullTypeName() + "*", "EXPORTS", 32); dllImportWriter.ReturnType(nameof(IntPtr)); } else { cfunctionWriter.ReturnType(function.ReturnType.GetFullTypeName(), "EXPORTS", 32); dllImportWriter.ReturnType(mapper.NativeToPinvokeType(function.ReturnType)); apiFunctionWriter.ReturnType(mapper.MapToManagedApiType(function.ReturnType)); } cfunctionWriter.MethodName(flatFunctionName); dllImportWriter.MethodName(flatFunctionName); apiFunctionWriter.MethodName(mapper.RenameForApi(function.Name, isMethod: !function.IsConstructor)); if (!function.IsConstructor && !function.IsStatic() && !cppClass.IsGlobal) { // all instance methods will have "this" as the first argument cfunctionWriter.Parameter(cppClass.Class.GetFullTypeName() + "*", "target"); dllImportWriter.Parameter(nameof(IntPtr), "target"); } foreach (var parameter in function.Parameters) { cfunctionWriter.Parameter( parameter.Type.GetDisplayName(), parameter.Name); dllImportWriter.Parameter( mapper.NativeToPinvokeType(parameter.Type), mapper.EscapeVariableName(parameter.Name)); apiFunctionWriter.Parameter( mapper.MapToManagedApiType(parameter.Type), mapper.EscapeVariableName(parameter.Name)); } // append "return" if needed cfunctionWriter.BodyStart(); if (cppClass.IsGlobal) { // GlobalMethod cfunctionWriter.BodyCallMethod(function.Name); } else if (!function.IsConstructor && !function.IsStatic()) { // target->InstanceMethod cfunctionWriter.BodyCallMethod($"target->{function.Name}"); } else if (function.IsStatic()) { // Class1::StaticMethod cfunctionWriter.BodyCallMethod(cppClass.Class.GetFullTypeName() + "::" + function.Name); } else { // new Class1 cfunctionWriter.BodyCallMethod($"new {cppClass.Class.GetFullTypeName()}"); } if (function.IsConstructor) { apiFunctionWriter .BaseCtor("ownsHandle: true") .StartExpressionBody() .BodyCallMethod("SetHandle"); } else { apiFunctionWriter.StartExpressionBody(); } if (mapper.IsKnownNativeType(function.ReturnType)) { // call "ctor(IntPtr, bool)" apiFunctionWriter.BodyCallMethod("new " + mapper.MapToManagedApiType(function.ReturnType)); } // some API functions need special casts, e.g. IntPtr/*size_t*/ (nint) to long if (mapper.NeedsCastForApi(function.ReturnType, out string returnTypeApiCast)) { apiFunctionWriter.BodyCallMethod(returnTypeApiCast); } apiFunctionWriter.BodyCallMethod(flatFunctionName); // all API functions pass "Handle" property as the first argument to DllImport methods if (!function.IsConstructor && !cppClass.IsGlobal && !function.IsStatic()) { apiFunctionWriter.PassParameter("Handle"); } foreach (var parameter in function.Parameters) { cfunctionWriter.PassParameter(parameter.Name); string dllImportType = mapper.NativeToPinvokeType(parameter.Type); string escapedName = mapper.EscapeVariableName(parameter.Name); if (parameter.Type.IsBool()) // bool to byte { escapedName = $"(Byte)({escapedName} ? 1 : 0)"; } // cast to DllImport's type if needed (TODO: wrap with checked {}) else if (dllImportType.Contains("/*")) { escapedName = $"({dllImportType.DropComments()}){escapedName}"; } // if the parameter is a C# class-wrapper - pass its Handle else if (mapper.IsKnownNativeType(parameter.Type)) { escapedName = $"({escapedName} == null ? IntPtr.Zero : {escapedName}.Handle)"; } apiFunctionWriter.PassParameter(escapedName); } if (function.ReturnType.IsBool()) { csApiSb.AppendLine(apiFunctionWriter.Build(" > 0").Tabify(2)); // byte to bool } else if (mapper.IsKnownNativeType(function.ReturnType)) { // pass "false" to "ownsHandle" argument apiFunctionWriter.EndLastCall(true).PassParameter("false"); csApiSb.AppendLine(apiFunctionWriter.Build().Tabify(2)); } else { csApiSb.AppendLine(apiFunctionWriter.Build().Tabify(2)); } csApiSb.AppendLine(); csDllImportsSb.AppendLine(dllImportWriter.BuildWithoutBody().Tabify(2)); csDllImportsSb.AppendLine(); cFileSb.AppendLine(cfunctionWriter.Build()); } if (cppClass.IsGlobal) { csFileSb.Append(templateManager.CSharpGlobalClass(csDllImportsSb.ToString(), csApiSb.ToString(), dllImportPath)); } else if (!cppClass.IsGlobal) { csFileSb.Append(templateManager.CSharpClass(mapper.RenameForApi(cppClass.Name, false), cppClass.Name, csDllImportsSb.ToString(), csApiSb.ToString(), dllImportPath)); // Append "delete" method: // EXPORTS(void) %class%_delete(%class%* target) { if (target) delete target; } var cfunctionWriter = new FunctionWriter(); cfunctionWriter.ReturnType("void", "EXPORTS", 32) .MethodName(cppClass.Name + "__delete") .Parameter(cppClass.Class.GetFullTypeName() + "*", "target") .BodyStart() .Body("delete target"); cFileSb .AppendLine(cfunctionWriter.Build()); } } File.WriteAllText(outCFile, templateManager.CHeader() + cFileSb); File.WriteAllText(outCsFile, templateManager.CSharpHeader(@namespace, csFileSb.ToString())); }