diff --git a/src/AST/Class.cs b/src/AST/Class.cs index 11459697..15753fdd 100644 --- a/src/AST/Class.cs +++ b/src/AST/Class.cs @@ -270,6 +270,11 @@ namespace CppSharp.AST } } + public bool IsTemplate + { + get { return IsDependent && TemplateParameters.Count > 0; } + } + public override IEnumerable FindOperator(CXXOperatorKind kind) { return Methods.Where(m => m.OperatorKind == kind); diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index 791cc038..8da934c8 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -271,7 +271,16 @@ namespace CppSharp.Generators.CSharp GenerateClassInternals(specialization); foreach (var group in generated.SelectMany(s => s.Classes).Where( - c => !c.IsIncomplete).GroupBy(c => c.Name)) + // HACK: the distinction is needed to eliminate duplicate nested types + // Since all template specialisations are incomplete by default, + // so are classes nested in them. + // When such classes are also forwarded, there are two incomplete declarations + // with the same name and in the same scope. Our parser searches by name and + // completion and it can therefore not make a difference between the two. + // Consequently, it always returns the first type it finds even if it isn't the right one. + // When clang::Sema later completes the specialisations, it completes the nested + // types too which leads to two identical complete types in the same scope. + c => !c.IsIncomplete).Distinct().GroupBy(c => c.Name)) GenerateNestedInternals(group.Key, group); WriteCloseBraceIndent(); @@ -329,7 +338,7 @@ namespace CppSharp.Generators.CSharp foreach (var nestedTemplate in @class.Classes.Where(c => !c.IsIncomplete && c.IsDependent)) GenerateClassTemplateSpecializationInternal(nestedTemplate); - if (@class.IsDependent) + if (@class.IsTemplate) { if (!(@class.Namespace is Class)) GenerateClassTemplateSpecializationInternal(@class); diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index 919b0665..c7967922 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -570,7 +570,7 @@ namespace CppSharp.Generators.CSharp Helpers.InternalStruct}{Helpers.GetSuffixForInternal(@class)}"; var printed = VisitDeclaration(@class).Type; - if (!@class.IsDependent) + if (!@class.IsTemplate) return printed; return $@"{printed}<{string.Join(", ", @class.TemplateParameters.Select(p => p.Name))}>"; @@ -638,9 +638,7 @@ namespace CppSharp.Generators.CSharp while (!(ctx is TranslationUnit)) { - var isInlineNamespace = ctx is Namespace && ((Namespace)ctx).IsInline; - if (!string.IsNullOrWhiteSpace(ctx.Name) && !isInlineNamespace) - names.Push(ctx.Name); + AddContextName(names, ctx); ctx = ctx.Namespace; } @@ -802,6 +800,38 @@ namespace CppSharp.Generators.CSharp return templateParam == null || templateParam.Parameter != null; } + private void AddContextName(Stack names, Declaration ctx) + { + var isInlineNamespace = ctx is Namespace && ((Namespace) ctx).IsInline; + if (string.IsNullOrWhiteSpace(ctx.Name) || isInlineNamespace) + return; + + if (ContextKind != TypePrinterContextKind.Managed) + { + names.Push(ctx.Name); + return; + } + + string name = null; + var @class = ctx as Class; + if (@class != null) + { + if (@class.IsTemplate) + name = $@"{ctx.Name}<{string.Join(", ", + @class.TemplateParameters.Select(p => p.Name))}>"; + else + { + var specialization = @class as ClassTemplateSpecialization; + if (specialization != null) + { + name = $@"{ctx.Name}<{string.Join(", ", + specialization.Arguments.Select(VisitTemplateArgument))}>"; + } + } + } + names.Push(name ?? ctx.Name); + } + private CSharpExpressionPrinter expressionPrinter => new CSharpExpressionPrinter(this); } } diff --git a/src/Generator/Passes/CheckIgnoredDecls.cs b/src/Generator/Passes/CheckIgnoredDecls.cs index f8440608..61ec8086 100644 --- a/src/Generator/Passes/CheckIgnoredDecls.cs +++ b/src/Generator/Passes/CheckIgnoredDecls.cs @@ -36,7 +36,7 @@ namespace CppSharp.Passes if (@class.IsInjected) injectedClasses.Add(@class); - if (!@class.IsDependent) + if (!@class.IsTemplate) return true; if (Options.GenerateClassTemplates) diff --git a/src/Generator/Passes/TrimSpecializationsPass.cs b/src/Generator/Passes/TrimSpecializationsPass.cs index 1569f47f..3c4dc7ec 100644 --- a/src/Generator/Passes/TrimSpecializationsPass.cs +++ b/src/Generator/Passes/TrimSpecializationsPass.cs @@ -37,7 +37,7 @@ namespace CppSharp.Passes if (!base.VisitClassDecl(@class)) return false; - if (@class.IsDependent) + if (@class.IsTemplate) { templates.Add(@class); foreach (var specialization in @class.Specializations.Where( diff --git a/tests/CSharp/CSharp.cs b/tests/CSharp/CSharp.cs index fdcb4e47..57dfa50d 100644 --- a/tests/CSharp/CSharp.cs +++ b/tests/CSharp/CSharp.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using CppSharp.AST; diff --git a/tests/CSharp/CSharpTemplates.h b/tests/CSharp/CSharpTemplates.h index 2cfbed03..9031370d 100644 --- a/tests/CSharp/CSharpTemplates.h +++ b/tests/CSharp/CSharpTemplates.h @@ -136,6 +136,7 @@ public: DependentValueFields operator+(const DependentValueFields& other); T getDependentValue(); void setDependentValue(const T& value); + IndependentFields returnNestedInTemplate(); private: T field{}; union { @@ -165,6 +166,12 @@ void DependentValueFields::setDependentValue(const T& value) field = value; } +template +IndependentFields::Nested> DependentValueFields::returnNestedInTemplate() +{ + return DependentValueFields::Nested(); +} + template DependentValueFields& DependentValueFields::returnInjectedClass() { @@ -532,6 +539,7 @@ template class DLL_API IndependentFields; template class DLL_API IndependentFields; template class DLL_API Base; template class DLL_API DependentValueFields; +template DLL_API IndependentFields::Nested> DependentValueFields::returnNestedInTemplate(); template class DLL_API VirtualTemplate; template class DLL_API VirtualTemplate; template class DLL_API HasDefaultTemplateArgument;