From dc417d9febdcc6c2f72a11a2e52b7990ecf987c5 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Wed, 27 Dec 2023 10:02:05 -0800 Subject: [PATCH] Adding a switch to minimally support pointers in generics --- .../CSharp/CSharpOutputBuilder.Visit.cs | 2 +- .../PInvokeGenerator.VisitStmt.cs | 10 +- .../PInvokeGenerator.cs | 97 ++++++++++++------- .../PInvokeGeneratorConfiguration.cs | 2 + .../PInvokeGeneratorConfigurationOptions.cs | 2 + sources/ClangSharpPInvokeGenerator/Program.cs | 7 ++ 6 files changed, 83 insertions(+), 37 deletions(-) diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs index 26554621..522d5c79 100644 --- a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs +++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.Visit.cs @@ -12,7 +12,7 @@ internal partial class CSharpOutputBuilder public void WriteCustomAttribute(string attribute, Action? callback = null) { - if (attribute.Equals("Flags", StringComparison.Ordinal) || attribute.Equals("Obsolete", StringComparison.Ordinal)) + if (attribute.Equals("Flags", StringComparison.Ordinal) || attribute.Equals("Obsolete", StringComparison.Ordinal) || attribute.StartsWith("Obsolete(", StringComparison.Ordinal)) { AddUsingDirective("System"); } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs index b465fb3f..dd9e744f 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs @@ -340,7 +340,7 @@ void VisitArgs(CallExpr callExpr, IReadOnlyList? args = null) if (subExpr is CXXThisExpr) { - var referenceTypeName = GetTypeName(callExpr, context: null, referenceType, ignoreTransparentStructsWhereRequired: true, out _); + var referenceTypeName = GetTypeName(callExpr, context: null, type: referenceType, ignoreTransparentStructsWhereRequired: true, isTemplate: false, nativeTypeName: out _); outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); outputBuilder.Write('('); @@ -443,6 +443,12 @@ private void VisitCharacterLiteral(CharacterLiteral characterLiteral) targetTypeName = GetRemappedTypeName(implicitCastExpr, context: null, targetType, out _, skipUsing: true); targetTypeNumBits = targetType.Handle.NumBits; } + else if (IsPrevContextStmt(out var binaryOperator, out _)) + { + var targetType = binaryOperator.Type; + targetTypeName = GetRemappedTypeName(implicitCastExpr, context: null, targetType, out _, skipUsing: true); + targetTypeNumBits = targetType.Handle.NumBits; + } else if (PreviousContext.Cursor is VarDecl varDecl) { var targetType = varDecl.Type; @@ -2106,7 +2112,7 @@ private void VisitReturnStmt(ReturnStmt returnStmt) if (subExpr is CXXThisExpr) { - var referenceTypeName = GetTypeName(returnStmt, context: null, referenceType, ignoreTransparentStructsWhereRequired: true, out _); + var referenceTypeName = GetTypeName(returnStmt, context: null, type: referenceType, ignoreTransparentStructsWhereRequired: true, isTemplate: false, nativeTypeName: out _); outputBuilder.AddUsingDirective("System.Runtime.CompilerServices"); outputBuilder.Write('('); diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index dadb32ab..08320b66 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -1652,6 +1652,16 @@ public void GenerateBindings(TranslationUnit translationUnit, string filePath, s var name = kvp.Key; var remappings = kvp.Value; + if (name.Contains('<', StringComparison.OrdinalIgnoreCase)) + { + var parts = name.Split('<'); + + if (parts.Length >= 2) + { + name = parts[0]; + } + } + if (!_config.RemappedNames.TryGetValue(name, out _)) { var addDiag = false; @@ -2716,7 +2726,7 @@ private string GetCursorName(NamedDecl namedDecl) { name = (typeDecl is TagDecl tagDecl) && tagDecl.Handle.IsAnonymous ? GetAnonymousName(tagDecl, tagDecl.TypeForDecl.KindSpelling) - : GetTypeName(namedDecl, context: null, typeDecl.TypeForDecl, ignoreTransparentStructsWhereRequired: false, out _); + : GetTypeName(namedDecl, context: null, type: typeDecl.TypeForDecl, ignoreTransparentStructsWhereRequired: false, isTemplate: false, nativeTypeName: out _); } else if (namedDecl is ParmVarDecl) { @@ -3158,9 +3168,9 @@ string AddUsingDirectiveIfNeeded(IOutputBuilder? outputBuilder, string remappedN } } - private string GetRemappedTypeName(Cursor? cursor, Cursor? context, Type type, out string nativeTypeName, bool skipUsing = false, bool ignoreTransparentStructsWhereRequired = false) + private string GetRemappedTypeName(Cursor? cursor, Cursor? context, Type type, out string nativeTypeName, bool skipUsing = false, bool ignoreTransparentStructsWhereRequired = false, bool isTemplate = false) { - var name = GetTypeName(cursor, context, type, ignoreTransparentStructsWhereRequired, out nativeTypeName); + var name = GetTypeName(cursor, context, type, ignoreTransparentStructsWhereRequired, isTemplate: isTemplate, nativeTypeName: out nativeTypeName); var nameToCheck = nativeTypeName; var remappedName = GetRemappedName(nameToCheck, cursor, tryRemapOperatorName: false, out var wasRemapped, skipUsing, skipUsingIfNotRemapped: true); @@ -3343,7 +3353,7 @@ private string GetTargetTypeName(Cursor cursor, out string nativeTypeName) return targetTypeName; } - private string GetTypeName(Cursor? cursor, Cursor? context, Type type, bool ignoreTransparentStructsWhereRequired, out string nativeTypeName) + private string GetTypeName(Cursor? cursor, Cursor? context, Type type, bool ignoreTransparentStructsWhereRequired, bool isTemplate, out string nativeTypeName) { if (_typeNames.TryGetValue((cursor, context, type), out var result)) { @@ -3366,11 +3376,11 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type type, bool igno } else { - return GetTypeName(cursor, context, type, type, ignoreTransparentStructsWhereRequired, out nativeTypeName); + return GetTypeName(cursor, context, type, type, ignoreTransparentStructsWhereRequired, isTemplate, out nativeTypeName); } } - private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type type, bool ignoreTransparentStructsWhereRequired, out string nativeTypeName) + private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type type, bool ignoreTransparentStructsWhereRequired, bool isTemplate, out string nativeTypeName) { if (!_typeNames.TryGetValue((cursor, context, type), out var result)) { @@ -3395,7 +3405,7 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type } else if (type is AttributedType attributedType) { - result.typeName = GetTypeName(cursor, context, rootType, attributedType.ModifiedType, ignoreTransparentStructsWhereRequired, out _); + result.typeName = GetTypeName(cursor, context, rootType, attributedType.ModifiedType, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else if (type is BuiltinType) { @@ -3538,17 +3548,17 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type } else if (type is DecltypeType decltypeType) { - result.typeName = GetTypeName(cursor, context, rootType, decltypeType.UnderlyingType, ignoreTransparentStructsWhereRequired, out _); + result.typeName = GetTypeName(cursor, context, rootType, decltypeType.UnderlyingType, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else if (type is DeducedType deducedType) { - result.typeName = GetTypeName(cursor, context, rootType, deducedType.GetDeducedType, ignoreTransparentStructsWhereRequired, out _); + result.typeName = GetTypeName(cursor, context, rootType, deducedType.GetDeducedType, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else if (type is DependentNameType dependentNameType) { if (dependentNameType.IsSugared) { - result.typeName = GetTypeName(cursor, context, rootType, dependentNameType.Desugar, ignoreTransparentStructsWhereRequired, out _); + result.typeName = GetTypeName(cursor, context, rootType, dependentNameType.Desugar, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else { @@ -3557,7 +3567,7 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type } else if (type is ElaboratedType elaboratedType) { - result.typeName = GetTypeName(cursor, context, rootType, elaboratedType.NamedType, ignoreTransparentStructsWhereRequired, out var nativeNamedTypeName); + result.typeName = GetTypeName(cursor, context, rootType, elaboratedType.NamedType, ignoreTransparentStructsWhereRequired, isTemplate, out var nativeNamedTypeName); if (!string.IsNullOrWhiteSpace(nativeNamedTypeName) && !result.nativeTypeName.StartsWith("const ", StringComparison.Ordinal) && @@ -3570,19 +3580,19 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type } else if (type is FunctionType functionType) { - result.typeName = GetTypeNameForPointeeType(cursor, context, rootType, functionType, ignoreTransparentStructsWhereRequired, out _, out _); + result.typeName = GetTypeNameForPointeeType(cursor, context, rootType, functionType, ignoreTransparentStructsWhereRequired, isTemplate, out _, out _); } else if (type is InjectedClassNameType injectedClassNameType) { - result.typeName = GetTypeName(cursor, context, rootType, injectedClassNameType.InjectedTST, ignoreTransparentStructsWhereRequired, out _); + result.typeName = GetTypeName(cursor, context, rootType, injectedClassNameType.InjectedTST, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else if (type is PackExpansionType packExpansionType) { - result.typeName = GetTypeName(cursor, context, rootType, packExpansionType.Pattern, ignoreTransparentStructsWhereRequired, out _); + result.typeName = GetTypeName(cursor, context, rootType, packExpansionType.Pattern, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else if (type is PointerType pointerType) { - result.typeName = GetTypeNameForPointeeType(cursor, context, rootType, pointerType.PointeeType, ignoreTransparentStructsWhereRequired, out var nativePointeeTypeName, out var isAdjusted); + result.typeName = GetTypeNameForPointeeType(cursor, context, rootType, pointerType.PointeeType, ignoreTransparentStructsWhereRequired, isTemplate, out var nativePointeeTypeName, out var isAdjusted); if (isAdjusted) { @@ -3591,7 +3601,7 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type } else if (type is ReferenceType referenceType) { - result.typeName = GetTypeNameForPointeeType(cursor, context, rootType, referenceType.PointeeType, ignoreTransparentStructsWhereRequired, out var nativePointeeTypeName, out var isAdjusted); + result.typeName = GetTypeNameForPointeeType(cursor, context, rootType, referenceType.PointeeType, ignoreTransparentStructsWhereRequired, isTemplate, out var nativePointeeTypeName, out var isAdjusted); if (isAdjusted) { @@ -3600,7 +3610,7 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type } else if (type is SubstTemplateTypeParmType substTemplateTypeParmType) { - result.typeName = GetTypeName(cursor, context, rootType, substTemplateTypeParmType.ReplacementType, ignoreTransparentStructsWhereRequired, out _); + result.typeName = GetTypeName(cursor, context, rootType, substTemplateTypeParmType.ReplacementType, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else if (type is TagType tagType) { @@ -3615,7 +3625,7 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type } else if (tagType.Handle.IsConstQualified) { - result.typeName = GetTypeName(cursor, context, rootType, tagType.Decl.TypeForDecl, ignoreTransparentStructsWhereRequired, out _); + result.typeName = GetTypeName(cursor, context, rootType, tagType.Decl.TypeForDecl, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else { @@ -3683,7 +3693,7 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type { case CXTemplateArgumentKind_Type: { - typeName = GetRemappedTypeName(cursor, context: null, arg.AsType, out var nativeAsTypeName, skipUsing: true); + typeName = GetRemappedTypeName(cursor, context: null, arg.AsType, out var nativeAsTypeName, skipUsing: true, isTemplate: true); break; } @@ -3715,6 +3725,11 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type if (typeName.EndsWith('*') || typeName.Contains("delegate*", StringComparison.Ordinal)) { + if (Config.GenerateGenericPointerWrapper) + { + AddDiagnostic(DiagnosticLevel.Warning, $"Unhandled pointer in template: '{typeName}'. Falling back 'IntPtr'.", cursor); + } + // Pointers are not yet supported as generic arguments; remap to IntPtr typeName = "IntPtr"; _outputBuilder?.EmitSystemSupport(); @@ -3736,7 +3751,7 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type { if (templateTypeParmType.IsSugared) { - result.typeName = GetTypeName(cursor, context, rootType, templateTypeParmType.Desugar, ignoreTransparentStructsWhereRequired, out _); + result.typeName = GetTypeName(cursor, context, rootType, templateTypeParmType.Desugar, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else { @@ -3750,11 +3765,11 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type // platform size, based on whatever parameters were passed into clang. var remappedName = GetRemappedName(result.typeName, cursor, tryRemapOperatorName: false, out var wasRemapped, skipUsing: true); - result.typeName = wasRemapped ? remappedName : GetTypeName(cursor, context, rootType, typedefType.Decl.UnderlyingType, ignoreTransparentStructsWhereRequired, out _); + result.typeName = wasRemapped ? remappedName : GetTypeName(cursor, context, rootType, typedefType.Decl.UnderlyingType, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else if (type is UsingType usingType) { - result.typeName = GetTypeName(cursor, context, rootType, usingType.Desugar, ignoreTransparentStructsWhereRequired, out _); + result.typeName = GetTypeName(cursor, context, rootType, usingType.Desugar, ignoreTransparentStructsWhereRequired, isTemplate, out _); } else { @@ -3776,7 +3791,7 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type return result.typeName; } - private string GetTypeNameForPointeeType(Cursor? cursor, Cursor? context, Type rootType, Type pointeeType, bool ignoreTransparentStructsWhereRequired, out string nativePointeeTypeName, out bool isAdjusted) + private string GetTypeNameForPointeeType(Cursor? cursor, Cursor? context, Type rootType, Type pointeeType, bool ignoreTransparentStructsWhereRequired, bool isTemplate, out string nativePointeeTypeName, out bool isAdjusted) { var name = pointeeType.AsString; @@ -3788,11 +3803,11 @@ private string GetTypeNameForPointeeType(Cursor? cursor, Cursor? context, Type r if (pointeeType is AttributedType attributedType) { - name = GetTypeNameForPointeeType(cursor, context, rootType, attributedType.ModifiedType, ignoreTransparentStructsWhereRequired, out var nativeModifiedTypeName, out isAdjusted); + name = GetTypeNameForPointeeType(cursor, context, rootType, attributedType.ModifiedType, ignoreTransparentStructsWhereRequired, isTemplate, out var nativeModifiedTypeName, out isAdjusted); } else if (pointeeType is ElaboratedType elaboratedType) { - name = GetTypeNameForPointeeType(cursor, context, rootType, elaboratedType.NamedType, ignoreTransparentStructsWhereRequired, out var nativeNamedTypeName, out isAdjusted); + name = GetTypeNameForPointeeType(cursor, context, rootType, elaboratedType.NamedType, ignoreTransparentStructsWhereRequired, isTemplate, out var nativeNamedTypeName, out isAdjusted); if (!string.IsNullOrWhiteSpace(nativeNamedTypeName) && !nativePointeeTypeName.StartsWith("const ", StringComparison.Ordinal) && @@ -3932,19 +3947,33 @@ private string GetTypeNameForPointeeType(Cursor? cursor, Cursor? context, Type r if (wasRemapped) { - name = remappedName; - name += '*'; + if (isTemplate && Config.GenerateGenericPointerWrapper) + { + name = $"Pointer<{remappedName}>"; + } + else + { + name = $"{remappedName}*"; + } } else { - name = GetTypeNameForPointeeType(cursor, context, rootType, typedefType.Decl.UnderlyingType, ignoreTransparentStructsWhereRequired, out var nativeUnderlyingTypeName, out isAdjusted); + name = GetTypeNameForPointeeType(cursor, context, rootType, typedefType.Decl.UnderlyingType, ignoreTransparentStructsWhereRequired, isTemplate, out var nativeUnderlyingTypeName, out isAdjusted); } } else { // Otherwise fields that point at anonymous structs get the wrong name - name = GetRemappedTypeName(cursor, context, pointeeType, out nativePointeeTypeName, skipUsing: true); - name += '*'; + var remappedName = GetRemappedTypeName(cursor, context, pointeeType, out nativePointeeTypeName, skipUsing: true); + + if (isTemplate && Config.GenerateGenericPointerWrapper) + { + name = $"Pointer<{remappedName}>"; + } + else + { + name = $"{remappedName}*"; + } } return name; @@ -4342,7 +4371,7 @@ private void GetTypeSize(Cursor cursor, Type type, ref long alignment32, ref lon // can be treated correctly. Otherwise, they will resolve to a particular // platform size, based on whatever parameters were passed into clang. - var name = GetTypeName(cursor, context: null, type, ignoreTransparentStructsWhereRequired: false, out _); + var name = GetTypeName(cursor, context: null, type: type, ignoreTransparentStructsWhereRequired: false, isTemplate: false, nativeTypeName: out _); var remappedName = GetRemappedTypeName(cursor, context: null, type, out _, skipUsing: true, ignoreTransparentStructsWhereRequired: false); if ((remappedName == name) && _config.WithTransparentStructs.TryGetValue(remappedName, out var transparentStruct) && (transparentStruct.Name.Equals("long", StringComparison.Ordinal) || transparentStruct.Name.Equals("ulong", StringComparison.Ordinal))) @@ -4838,7 +4867,7 @@ bool IsComProxy(FunctionDecl functionDecl, string name) if ((parmVarDecl is not null) && IsType(parmVarDecl, out var pointerType)) { - var typeName = GetTypeName(parmVarDecl, context: null, pointerType.PointeeType, ignoreTransparentStructsWhereRequired: false, out var nativeTypeName); + var typeName = GetTypeName(parmVarDecl, context: null, type: pointerType.PointeeType, ignoreTransparentStructsWhereRequired: false, isTemplate: false, nativeTypeName: out var nativeTypeName); return name.StartsWith($"{nativeTypeName}_", StringComparison.Ordinal) || name.StartsWith($"{typeName}_", StringComparison.Ordinal) || typeName.Equals("IRpcStubBuffer", StringComparison.Ordinal); } return false; @@ -5089,7 +5118,7 @@ private bool IsFixedSize(Cursor cursor, Type type) } else if (type is TypedefType typedefType) { - var name = GetTypeName(cursor, context: null, type, ignoreTransparentStructsWhereRequired: false, out _); + var name = GetTypeName(cursor, context: null, type: type, ignoreTransparentStructsWhereRequired: false, isTemplate: false, nativeTypeName: out _); var remappedName = GetRemappedTypeName(cursor, context: null, type, out _, skipUsing: true, ignoreTransparentStructsWhereRequired: false); return !remappedName.Equals("IntPtr", StringComparison.Ordinal) @@ -5775,7 +5804,7 @@ private bool IsUnchecked(string targetTypeName, Stmt stmt) return true; } - var sourceTypeName = GetTypeName(stmt, context: null, unaryOperator.SubExpr.Type, ignoreTransparentStructsWhereRequired: false, out _); + var sourceTypeName = GetTypeName(stmt, context: null, type: unaryOperator.SubExpr.Type, ignoreTransparentStructsWhereRequired: false, isTemplate: false, nativeTypeName: out _); switch (unaryOperator.Opcode) { diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs index 2ef121d1..3dfb786b 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfiguration.cs @@ -227,6 +227,8 @@ public IReadOnlyCollection ExcludedNames public bool GenerateFileScopedNamespaces => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateFileScopedNamespaces); + public bool GenerateGenericPointerWrapper => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateGenericPointerWrapper); + public bool GenerateGuidMember => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateGuidMember); public bool GenerateHelperTypes => _options.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateHelperTypes); diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs index 60343e0b..db91ccd4 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGeneratorConfigurationOptions.cs @@ -84,4 +84,6 @@ public enum PInvokeGeneratorConfigurationOptions : long GenerateDisableRuntimeMarshalling = 1L << 36, GenerateCallConvMemberFunction = 1L << 37, + + GenerateGenericPointerWrapper = 1L << 38, } diff --git a/sources/ClangSharpPInvokeGenerator/Program.cs b/sources/ClangSharpPInvokeGenerator/Program.cs index c5d522a9..ab53111e 100644 --- a/sources/ClangSharpPInvokeGenerator/Program.cs +++ b/sources/ClangSharpPInvokeGenerator/Program.cs @@ -171,6 +171,7 @@ public static class Program new TwoColumnHelpRow("generate-marker-interfaces", "Bindings for marker interfaces representing native inheritance hierarchies should be generated."), new TwoColumnHelpRow("generate-native-bitfield-attribute", "[NativeBitfield(\"\", offset: #, length: #)] attribute should be generated to document the encountered bitfield layout."), new TwoColumnHelpRow("generate-native-inheritance-attribute", "[NativeInheritance(\"\")] attribute should be generated to document the encountered C++ base type."), + new TwoColumnHelpRow("generate-generic-pointer-wrapper", "Pointer should be used for limited generic type support."), new TwoColumnHelpRow("generate-setslastsystemerror-attribute", "[SetsLastSystemError] attribute should be generated rather than using SetLastError = true."), new TwoColumnHelpRow("generate-template-bindings", "Bindings for template-definitions should be generated. This is currently experimental."), new TwoColumnHelpRow("generate-unmanaged-constants", "Unmanaged constants should be generated using static ref readonly properties. This is currently experimental."), @@ -566,6 +567,12 @@ public static void Run(InvocationContext context) break; } + case "generate-generic-pointer-wrapper": + { + configOptions |= PInvokeGeneratorConfigurationOptions.GenerateGenericPointerWrapper; + break; + } + case "generate-setslastsystemerror-attribute": { configOptions |= PInvokeGeneratorConfigurationOptions.GenerateSetsLastSystemErrorAttribute;