Skip to content

Commit 587f466

Browse files
committed
std::wstring support
- In IgnoreSystemDeclarationsPass: Explicitly renamed basic_string:: assign and data methods becasue otherwise some following implicit renamings make the code not working (this issue can be observer when a std::string and std::wstring is present in the C++ input classes) - Removed the resetting of method names in SpecializationMethodsWithDependentPointersPass becasue this reverts changes which are made in the pass IgnoreSystemDeclarationsPass - added wstring type map - added a hacky flag be able to run tests when the option MarshalCharAsManagedChar is set to false
1 parent c36145b commit 587f466

File tree

7 files changed

+141
-6
lines changed

7 files changed

+141
-6
lines changed

src/Generator/Passes/IgnoreSystemDeclarationsPass.cs

+25-1
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,36 @@ public override bool VisitClassDecl(Class @class)
4242
switch (@class.Name)
4343
{
4444
case "basic_string":
45+
@class.GenerationKind = GenerationKind.Generate;
46+
foreach (var specialization in from s in @class.Specializations
47+
let arg = s.Arguments[0].Type.Type.Desugar()
48+
where arg.IsPrimitiveType(PrimitiveType.Char) || arg.IsPrimitiveType(PrimitiveType.WideChar)
49+
select s)
50+
{
51+
specialization.GenerationKind = GenerationKind.Generate;
52+
foreach (var method in specialization.Methods)
53+
{
54+
if (method.OriginalName == "assign" || method.OriginalName == "data")
55+
{
56+
if (specialization.Arguments[0].Type.Type.Desugar().IsPrimitiveType(PrimitiveType.WideChar))
57+
method.Name = method.Name + "W";
58+
else if (specialization.Arguments[0].Type.Type.Desugar().IsPrimitiveType(PrimitiveType.Char))
59+
method.Name = method.Name + "A";
60+
61+
method.GenerationKind = GenerationKind.Generate;
62+
method.Namespace.GenerationKind = GenerationKind.Generate;
63+
method.InstantiatedFrom.GenerationKind = GenerationKind.Generate;
64+
method.InstantiatedFrom.Namespace.GenerationKind = GenerationKind.Generate;
65+
}
66+
}
67+
}
68+
break;
4569
case "allocator":
4670
case "char_traits":
4771
@class.GenerationKind = GenerationKind.Generate;
4872
foreach (var specialization in from s in @class.Specializations
4973
let arg = s.Arguments[0].Type.Type.Desugar()
50-
where arg.IsPrimitiveType(PrimitiveType.Char)
74+
where arg.IsPrimitiveType(PrimitiveType.Char) || arg.IsPrimitiveType(PrimitiveType.WideChar)
5175
select s)
5276
{
5377
specialization.GenerationKind = GenerationKind.Generate;

src/Generator/Passes/SpecializationMethodsWithDependentPointersPass.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,9 @@ private static Method GetExtensionMethodForDependentPointer(Method specializedMe
113113
}
114114
}
115115

116-
specializedMethod.Name = specializedMethod.OriginalName;
117-
extensionMethod.Name = extensionMethod.OriginalName;
116+
// If we change the Name in IgnoreSystemDeclarationsPass.cs we should not revert this change here. Otherwise we get Assign_1 instead of AssignW or AssignA for string / wstring
117+
//specializedMethod.Name = specializedMethod.OriginalName;
118+
//extensionMethod.Name = extensionMethod.OriginalName;
118119
extensionMethod.OriginalFunction = specializedMethod;
119120
extensionMethod.Kind = CXXMethodKind.Normal;
120121
extensionMethod.IsStatic = true;

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

+1
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ public partial class ConstChar32TPointer : ConstCharPointer
302302
}
303303

304304
[TypeMap("basic_string<char, char_traits<char>, allocator<char>>", GeneratorKind = GeneratorKind.CSharp)]
305+
[TypeMap("basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t>>", GeneratorKind = GeneratorKind.CSharp)]
305306
public partial class String : TypeMap
306307
{
307308
public override Type CSharpSignatureType(TypePrinterContext ctx)

tests/Common/Common.Gen.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public override void Setup(Driver driver)
6262

6363
public override void SetupPasses(Driver driver)
6464
{
65-
driver.Options.MarshalCharAsManagedChar = true;
65+
driver.Options.MarshalCharAsManagedChar = false; // c++ char is mapped to sbyte. c++ wchar_t is mapped to char
6666
driver.Options.GenerateDefaultValuesForArguments = true;
6767
}
6868

@@ -72,8 +72,11 @@ public override void Preprocess(Driver driver, ASTContext ctx)
7272
ctx.SetClassAsValueType("Bar2");
7373
ctx.IgnoreClassWithName("IgnoredType");
7474

75-
ctx.FindCompleteClass("Foo").Enums.First(
75+
if (ctx.FindCompleteClass("Foo") != null)
76+
{
77+
ctx.FindCompleteClass("Foo").Enums.First(
7678
e => string.IsNullOrEmpty(e.Name)).Name = "RenamedEmptyEnum";
79+
}
7780
}
7881

7982
public override void Postprocess(Driver driver, ASTContext ctx)

tests/Common/Common.Tests.cs

+73-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System;
1+
#define MarshalCharAsManagedSByte // Tests need to behave differently when char is mapped to sbyte
2+
3+
using System;
24
using System.Reflection;
35
using CommonTest;
46
using NUnit.Framework;
@@ -396,8 +398,13 @@ public void TestNestedAnonymousTypes()
396398
Assert.That(testNestedTypes.ToVerifyCorrectLayoutBefore, Is.EqualTo(5));
397399
testNestedTypes.I = 10;
398400
Assert.That(testNestedTypes.I, Is.EqualTo(10));
401+
#if MarshalCharAsManagedSByte
402+
testNestedTypes.C = Convert.ToSByte('D');
403+
Assert.That(Convert.ToChar(testNestedTypes.C), Is.EqualTo('D'));
404+
#else
399405
testNestedTypes.C = 'D';
400406
Assert.That(testNestedTypes.C, Is.EqualTo('D'));
407+
#endif
401408
testNestedTypes.ToVerifyCorrectLayoutAfter = 15;
402409
Assert.That(testNestedTypes.ToVerifyCorrectLayoutAfter, Is.EqualTo(15));
403410
}
@@ -459,9 +466,19 @@ public void TestCharMarshalling()
459466
{
460467
using (Foo2 foo2 = new Foo2())
461468
{
469+
#if MarshalCharAsManagedSByte
470+
for (Int32 c = sbyte.MinValue; c <= sbyte.MaxValue; c++)
471+
{
472+
sbyte cSbyte = Convert.ToSByte(c);
473+
sbyte charMarshalled = foo2.TestCharMarshalling(cSbyte);
474+
Int32 charBackConverted = Convert.ToInt32(charMarshalled);
475+
Assert.That(charBackConverted, Is.EqualTo(c));
476+
}
477+
#else
462478
for (char c = char.MinValue; c <= sbyte.MaxValue; c++)
463479
Assert.That(foo2.TestCharMarshalling(c), Is.EqualTo(c));
464480
Assert.Catch<OverflowException>(() => foo2.TestCharMarshalling('ж'));
481+
#endif
465482
}
466483
}
467484

@@ -511,7 +528,11 @@ public void TestOperators()
511528
{
512529
using (var @class = new ClassWithOverloadedOperators())
513530
{
531+
#if MarshalCharAsManagedSByte
532+
sbyte @char = @class;
533+
#else
514534
char @char = @class;
535+
#endif
515536
Assert.That(@char, Is.EqualTo(1));
516537
short @short = @class;
517538
Assert.That(@short, Is.EqualTo(3));
@@ -573,7 +594,12 @@ public void TestProperties()
573594
Assert.That(prop.StartWithVerb, Is.EqualTo(25));
574595
prop.StartWithVerb = 5;
575596

597+
#if MarshalCharAsManagedSByte
598+
sbyte cSByte = Convert.ToSByte('a');
599+
Assert.That(prop.Contains(cSByte), Is.EqualTo(prop.Contains("a")));
600+
#else
576601
Assert.That(prop.Contains('a'), Is.EqualTo(prop.Contains("a")));
602+
#endif
577603

578604
Assert.That(prop.conflict, Is.EqualTo(CommonTest.TestProperties.Conflict.Value1));
579605
prop.conflict = CommonTest.TestProperties.Conflict.Value2;
@@ -849,8 +875,14 @@ public void TestVirtualReturningClassWithCharField()
849875
hasProblematicFields.B = true;
850876
Assert.That(hasProblematicFields.B, Is.EqualTo(true));
851877
Assert.That(hasProblematicFields.C, Is.EqualTo(char.MinValue));
878+
#if MarshalCharAsManagedSByte
879+
hasProblematicFields.C = Convert.ToSByte('a');
880+
Assert.That(Convert.ToChar(hasProblematicFields.C), Is.EqualTo('a'));
881+
#else
852882
hasProblematicFields.C = 'a';
853883
Assert.That(hasProblematicFields.C, Is.EqualTo('a'));
884+
#endif
885+
854886
}
855887
}
856888

@@ -897,10 +929,17 @@ public void TestFixedCharArray()
897929
{
898930
using (var foo = new Foo())
899931
{
932+
#if MarshalCharAsManagedSByte
933+
foo.FixedCharArray = new sbyte[] { Convert.ToSByte('a'), Convert.ToSByte('b'), Convert.ToSByte('c') };
934+
Assert.That(foo.FixedCharArray[0], Is.EqualTo('a'));
935+
Assert.That(foo.FixedCharArray[1], Is.EqualTo('b'));
936+
Assert.That(foo.FixedCharArray[2], Is.EqualTo('c'));
937+
#else
900938
foo.FixedCharArray = new char[] { 'a', 'b', 'c' };
901939
Assert.That(foo.FixedCharArray[0], Is.EqualTo('a'));
902940
Assert.That(foo.FixedCharArray[1], Is.EqualTo('b'));
903941
Assert.That(foo.FixedCharArray[2], Is.EqualTo('c'));
942+
#endif
904943
}
905944
}
906945

@@ -964,6 +1003,39 @@ public void TestNullStdString()
9641003
}
9651004
}
9661005

1006+
[Test]
1007+
public void TestStdWString()
1008+
{
1009+
// when C++ memory is deleted, it's only marked as free but not immediadely freed
1010+
// this can hide memory bugs while marshalling
1011+
// so let's use a long string to increase the chance of a crash right away
1012+
const string t = @"This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string.
1013+
This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string.
1014+
This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string.
1015+
This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string.
1016+
This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string.
1017+
This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string. This is a very long string.";
1018+
const string unicodeString1 = "你好";
1019+
const string unicodeString2 = "Ÿ‰ϰ";
1020+
1021+
using (var hasStdWString = new HasStdWString())
1022+
{
1023+
Assert.That(hasStdWString.TestStdWString(t), Is.EqualTo(t + "_test"));
1024+
hasStdWString.S = t;
1025+
Assert.That(hasStdWString.S, Is.EqualTo(t));
1026+
Assert.That(hasStdWString.StdWString, Is.EqualTo(t));
1027+
Assert.That(hasStdWString.StdWString, Is.EqualTo(t)
1028+
Assert.That(hasStdWString.TestStdWString(unicodeString1), Is.EqualTo(unicodeString1 + "_test"));
1029+
hasStdWString.S = unicodeString1;
1030+
Assert.That(hasStdWString.S, Is.EqualTo(unicodeString1));
1031+
Assert.That(hasStdWString.StdWString, Is.EqualTo(unicodeString1)
1032+
Assert.That(hasStdWString.TestStdWString(unicodeString2), Is.EqualTo(unicodeString2 + "_test"));
1033+
hasStdWString.S = unicodeString2;
1034+
Assert.That(hasStdWString.S, Is.EqualTo(unicodeString2));
1035+
Assert.That(hasStdWString.StdWString, Is.EqualTo(unicodeString2));
1036+
}
1037+
}
1038+
9671039
[Test]
9681040
public void TestUTF8()
9691041
{

tests/Common/Common.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,29 @@ std::string& HasStdString::getStdString()
607607
return s;
608608
}
609609

610+
HasStdWString::HasStdWString()
611+
{
612+
}
613+
614+
HasStdWString::~HasStdWString()
615+
{
616+
}
617+
618+
std::wstring HasStdWString::testStdWString(const std::wstring& s)
619+
{
620+
return s + L"_test";
621+
}
622+
623+
std::wstring HasStdWString::testStdWStringPassedByValue(std::wstring s)
624+
{
625+
return s + L"_test";
626+
}
627+
628+
std::wstring& HasStdWString::getStdWString()
629+
{
630+
return s;
631+
}
632+
610633
SomeNamespace::AbstractClass::~AbstractClass()
611634
{
612635
}

tests/Common/Common.h

+11
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,17 @@ class DLL_API HasStdString
817817
std::string& getStdString();
818818
};
819819

820+
class DLL_API HasStdWString
821+
{
822+
public:
823+
HasStdWString();
824+
~HasStdWString();
825+
std::wstring testStdWString(const std::wstring& s);
826+
std::wstring testStdWStringPassedByValue(std::wstring s);
827+
std::wstring s;
828+
std::wstring& getStdWString();
829+
};
830+
820831
class DLL_API InternalCtorAmbiguity
821832
{
822833
public:

0 commit comments

Comments
 (0)