Browse Source

Fix #1251 three parameter equality operator

- Operators in generic classes do not attempt to generate as extension methods anymore
- Empty `...Extensions` classes are no longer generated
- `string` as a template argument is correctly cast
- `MarshalCharAsManagedChar` option also generates correct casts
- Suppress warning regarding returning struct field by ref
- Eliminate some tabs that snuck into the test C++ header
pull/1778/head
Salvage 2 years ago committed by João Matos
parent
commit
28000a14b4
  1. 16
      src/AST/TypeExtensions.cs
  2. 18
      src/Generator/Generators/CSharp/CSharpMarshal.cs
  3. 1
      src/Generator/Generators/CSharp/CSharpSources.cs
  4. 9
      src/Generator/Passes/SpecializationMethodsWithDependentPointersPass.cs
  5. 14
      src/Generator/Types/Std/Stdlib.CSharp.cs
  6. 21
      tests/dotnet/CSharp/CSharp.Tests.cs
  7. 67
      tests/dotnet/CSharp/CSharp.h

16
src/AST/TypeExtensions.cs

@ -439,6 +439,22 @@ @@ -439,6 +439,22 @@
return false;
}
public static bool IsTemplate(this Type type)
{
if (type is TemplateParameterType or TemplateParameterSubstitutionType)
return true;
var ptr = type;
while (ptr is PointerType pType)
{
ptr = pType.Pointee;
if (ptr is TemplateParameterType or TemplateParameterSubstitutionType)
return true;
}
return false;
}
public static Module GetModule(this Type type)
{
Declaration declaration;

18
src/Generator/Generators/CSharp/CSharpMarshal.cs

@ -601,7 +601,7 @@ namespace CppSharp.Generators.CSharp @@ -601,7 +601,7 @@ namespace CppSharp.Generators.CSharp
if (Context.Context.Options.MarshalCharAsManagedChar &&
primitive == PrimitiveType.Char)
{
Context.Return.Write($"({typePrinter.PrintNative(pointer)})");
Context.Return.StringBuilder.Insert(0, $"({typePrinter.PrintNative(pointer)}) ");
if (isConst)
Context.Return.Write("&");
Context.Return.Write(param.Name);
@ -643,8 +643,13 @@ namespace CppSharp.Generators.CSharp @@ -643,8 +643,13 @@ namespace CppSharp.Generators.CSharp
}
else
{
Context.Before.WriteLine("var {0} = {1}.{2};",
arg, Context.Parameter.Name, Helpers.InstanceIdentifier);
Context.Before.Write($"var {arg} = ");
if (pointer.Pointee.IsTemplate())
Context.Before.Write($"(({Context.Parameter.Type}) (object) {Context.Parameter.Name})");
else
Context.Before.WriteLine(Context.Parameter.Name);
Context.Before.WriteLine($".{Helpers.InstanceIdentifier};");
Context.Return.Write($"new {typePrinter.IntPtrType}(&{arg})");
}
@ -805,7 +810,12 @@ namespace CppSharp.Generators.CSharp @@ -805,7 +810,12 @@ namespace CppSharp.Generators.CSharp
private void MarshalValueClass()
{
Context.Return.Write("{0}.{1}", Context.Parameter.Name, Helpers.InstanceIdentifier);
if (Context.Parameter.Type.IsTemplate())
Context.Return.Write($"(({Context.Parameter.Type}) (object) {Context.Parameter.Name})");
else
Context.Return.Write(Context.Parameter.Name);
Context.Return.Write($".{Helpers.InstanceIdentifier}");
}
public override bool VisitFieldDecl(Field field)

1
src/Generator/Generators/CSharp/CSharpSources.cs

@ -85,6 +85,7 @@ namespace CppSharp.Generators.CSharp @@ -85,6 +85,7 @@ namespace CppSharp.Generators.CSharp
GenerateUsings();
WriteLine("#pragma warning disable CS0109 // Member does not hide an inherited member; new keyword is not required");
WriteLine("#pragma warning disable CS9084 // Struct member returns 'this' or other instance members by reference");
NewLine();
if (!string.IsNullOrEmpty(Module.OutputNamespace))

9
src/Generator/Passes/SpecializationMethodsWithDependentPointersPass.cs

@ -52,6 +52,7 @@ namespace CppSharp.Passes @@ -52,6 +52,7 @@ namespace CppSharp.Passes
if (!methodsWithDependentPointers.Any())
return false;
var hasMethods = false;
var classExtensions = new Class { Name = $"{@class.Name}Extensions", IsStatic = true };
foreach (var specialization in @class.Specializations.Where(s => s.IsGenerated))
foreach (var method in methodsWithDependentPointers.Where(
@ -59,9 +60,11 @@ namespace CppSharp.Passes @@ -59,9 +60,11 @@ namespace CppSharp.Passes
{
var specializedMethod = specialization.Methods.FirstOrDefault(
m => m.InstantiatedFrom == method);
if (specializedMethod == null)
if (specializedMethod == null || specializedMethod.IsOperator)
continue;
hasMethods = true;
Method extensionMethod = GetExtensionMethodForDependentPointer(specializedMethod);
classExtensions.Methods.Add(extensionMethod);
extensionMethod.Namespace = classExtensions;
@ -75,6 +78,10 @@ namespace CppSharp.Passes @@ -75,6 +78,10 @@ namespace CppSharp.Passes
extensionMethod.GenerationKind = GenerationKind.Generate;
}
}
if (!hasMethods)
return false;
classExtensions.Namespace = @class.Namespace;
classExtensions.OriginalClass = @class;
extensions.Add(classExtensions);

14
src/Generator/Types/Std/Stdlib.CSharp.cs

@ -329,7 +329,10 @@ namespace CppSharp.Types.Std @@ -329,7 +329,10 @@ namespace CppSharp.Types.Std
ctx.Return.Write($@"{qualifiedBasicString}Extensions.{
Helpers.InternalStruct}.{assign.Name}(new {
typePrinter.IntPtrType}(&{
ctx.ReturnVarName}), {ctx.Parameter.Name})");
ctx.ReturnVarName}), ");
if (ctx.Parameter.Type.IsTemplate())
ctx.Return.Write("(string) (object) ");
ctx.Return.Write($"{ctx.Parameter.Name})");
ctx.ReturnVarName = string.Empty;
}
else
@ -337,8 +340,13 @@ namespace CppSharp.Types.Std @@ -337,8 +340,13 @@ namespace CppSharp.Types.Std
var varBasicString = $"__basicString{ctx.ParameterIndex}";
ctx.Before.WriteLine($@"var {varBasicString} = new {
basicString.Visit(typePrinter)}();");
ctx.Before.WriteLine($@"{qualifiedBasicString}Extensions.{
assign.Name}({varBasicString}, {ctx.Parameter.Name});");
ctx.Before.Write($@"{qualifiedBasicString}Extensions.{
assign.Name}({varBasicString}, ");
if (ctx.Parameter.Type.IsTemplate())
ctx.Before.Write("(string) (object) ");
ctx.Before.WriteLine($"{ctx.Parameter.Name});");
ctx.Return.Write($"{varBasicString}.{Helpers.InstanceIdentifier}");
ctx.Cleanup.WriteLine($@"{varBasicString}.Dispose({
(!Type.IsAddress() || ctx.Parameter?.IsIndirect == true ? "disposing: true, callNativeDtor:false" : string.Empty)});");

21
tests/dotnet/CSharp/CSharp.Tests.cs

@ -2003,4 +2003,25 @@ public unsafe class CSharpTests @@ -2003,4 +2003,25 @@ public unsafe class CSharpTests
Assert.AreEqual(2, unionTestA.A);
Assert.AreEqual(2, unionTestB.B);
}
[TestCase("hi")]
[TestCase(2u)]
public void TestOptional<T>(T value)
{
Assert.That(new CSharp.Optional<T>() != new CSharp.Optional<T>(value));
Assert.That(new CSharp.Optional<T>() != value);
Assert.That(new CSharp.Optional<T>() == new CSharp.Optional<T>());
Assert.That(new CSharp.Optional<T>(value) == new CSharp.Optional<T>(value));
Assert.That(new CSharp.Optional<T>(value) == value);
}
[Test]
public void TestOptionalIntPtr()
{
Assert.That(new CSharp.Optional<IntPtr>() != new CSharp.Optional<IntPtr>(IntPtr.MaxValue));
Assert.That(new CSharp.Optional<IntPtr>() != IntPtr.MaxValue);
Assert.That(new CSharp.Optional<IntPtr>() == new CSharp.Optional<IntPtr>());
Assert.That(new CSharp.Optional<IntPtr>(IntPtr.MaxValue) == new CSharp.Optional<IntPtr>(IntPtr.MaxValue));
Assert.That(new CSharp.Optional<IntPtr>(IntPtr.MaxValue) == IntPtr.MaxValue);
}
}

67
tests/dotnet/CSharp/CSharp.h

@ -733,22 +733,22 @@ class DLL_API TestParamToInterfacePassBaseOne @@ -733,22 +733,22 @@ class DLL_API TestParamToInterfacePassBaseOne
class DLL_API TestParamToInterfacePassBaseTwo
{
int m;
int m;
public:
int getM();
void setM(int n);
const TestParamToInterfacePassBaseTwo& operator++();
TestParamToInterfacePassBaseTwo();
TestParamToInterfacePassBaseTwo(int n);
int getM();
void setM(int n);
const TestParamToInterfacePassBaseTwo& operator++();
TestParamToInterfacePassBaseTwo();
TestParamToInterfacePassBaseTwo(int n);
};
class DLL_API TestParamToInterfacePass : public TestParamToInterfacePassBaseOne, public TestParamToInterfacePassBaseTwo
{
public:
TestParamToInterfacePassBaseTwo addM(TestParamToInterfacePassBaseTwo b);
TestParamToInterfacePassBaseTwo operator+(TestParamToInterfacePassBaseTwo b);
TestParamToInterfacePass(TestParamToInterfacePassBaseTwo b);
TestParamToInterfacePass();
TestParamToInterfacePassBaseTwo addM(TestParamToInterfacePassBaseTwo b);
TestParamToInterfacePassBaseTwo operator+(TestParamToInterfacePassBaseTwo b);
TestParamToInterfacePass(TestParamToInterfacePassBaseTwo b);
TestParamToInterfacePass();
};
class DLL_API HasProtectedVirtual
@ -973,18 +973,18 @@ class DLL_API ClassWithVirtualBase : public virtual Foo @@ -973,18 +973,18 @@ class DLL_API ClassWithVirtualBase : public virtual Foo
namespace NamespaceA
{
CS_VALUE_TYPE class DLL_API A
{
};
CS_VALUE_TYPE class DLL_API A
{
};
}
namespace NamespaceB
{
class DLL_API B
{
public:
void Function(CS_OUT NamespaceA::A &a);
};
class DLL_API B
{
public:
void Function(CS_OUT NamespaceA::A &a);
};
}
class DLL_API HasPrivateVirtualProperty
@ -1607,6 +1607,37 @@ DLL_API extern PointerTester* PointerToClass; @@ -1607,6 +1607,37 @@ DLL_API extern PointerTester* PointerToClass;
union DLL_API UnionTester {
float a;
int b;
inline bool operator ==(const UnionTester& other) const {
return b == other.b;
}
};
int DLL_API ValueTypeOutParameter(CS_OUT UnionTester* testerA, CS_OUT UnionTester* testerB);
template <class T>
class Optional {
public:
T m_value;
bool m_hasValue;
Optional() {
m_hasValue = false;
}
Optional(T value) {
m_value = std::move(value);
m_hasValue = true;
}
inline bool operator ==(const Optional<T>& rhs) const {
return (m_hasValue == rhs.m_hasValue && (!m_hasValue || m_value == rhs.m_value));
}
inline bool operator ==(const T& rhs) const {
return (m_hasValue && m_value == rhs);
}
};
// We just need a method that uses various instantiations of Optional.
inline void DLL_API InstantiateOptionalTemplate(Optional<unsigned int>, Optional<std::string>,
Optional<TestComparison>, Optional<char*>, Optional<UnionTester>) { }

Loading…
Cancel
Save