diff --git a/src/CppAst.Tests/TestFunctions.cs b/src/CppAst.Tests/TestFunctions.cs index 3932d81..c4b01af 100644 --- a/src/CppAst.Tests/TestFunctions.cs +++ b/src/CppAst.Tests/TestFunctions.cs @@ -256,5 +256,32 @@ public void TestFunctionVariadic() ); } + + + [Test] + public void TestFunctionTemplate() + { + ParseAssert(@" +template +void function0(T t); +", + compilation => + { + Assert.False(compilation.HasErrors); + + Assert.AreEqual(1, compilation.Functions.Count); + + { + var cppFunction = compilation.Functions[0]; + Assert.AreEqual(1, cppFunction.Parameters.Count); + Assert.AreEqual("void", cppFunction.ReturnType.ToString()); + Assert.AreEqual(cppFunction.IsFunctionTemplate, true); + Assert.AreEqual(cppFunction.TemplateParameters.Count, 1); + } + + } + ); + } + } } \ No newline at end of file diff --git a/src/CppAst.Tests/TestNamespaces.cs b/src/CppAst.Tests/TestNamespaces.cs index c986fa8..0ce8125 100644 --- a/src/CppAst.Tests/TestNamespaces.cs +++ b/src/CppAst.Tests/TestNamespaces.cs @@ -107,5 +107,48 @@ struct MyStruct; } ); } + + [Test] + public void TestInlineNamespace() + { + var text = @" +namespace A +{ + +inline namespace __1 +{ + // Test using Template + template + struct MyStruct; + + using MyStructInt = MyStruct; +} + +} + +"; + + ParseAssert(text, + compilation => + { + Assert.False(compilation.HasErrors); + + Assert.AreEqual(1, compilation.Namespaces.Count); + + var inlineNs = compilation.Namespaces[0].Namespaces[0]; + Assert.AreEqual(inlineNs.Name, "__1"); + Assert.AreEqual(true, inlineNs.IsInlineNamespace); + + var cppStruct = compilation.FindByFullName("A::MyStruct"); + Assert.AreEqual(inlineNs.Classes[0], cppStruct); + Assert.AreEqual(cppStruct.FullName, "A::MyStruct"); + + var cppTypedef = compilation.FindByFullName("A::MyStructInt"); + var cppStructInt = cppTypedef.ElementType as CppClass; + //So now we can use this full name in exporter convenience. + Assert.AreEqual(cppStructInt.FullName, "A::MyStruct"); + } + ); + } } } \ No newline at end of file diff --git a/src/CppAst/CppBaseType.cs b/src/CppAst/CppBaseType.cs index a854315..a5eccc9 100644 --- a/src/CppAst/CppBaseType.cs +++ b/src/CppAst/CppBaseType.cs @@ -51,6 +51,31 @@ public override string ToString() } builder.Append(Type.GetDisplayName()); + + var cls = Type as CppClass; + if(cls != null && cls.TemplateKind != CppTemplateKind.NormalClass) + { + builder.Append("<"); + + if (cls.TemplateKind == CppTemplateKind.TemplateSpecializedClass) + { + for (var i = 0; i < cls.TemplateSpecializedArguments.Count; i++) + { + if (i > 0) builder.Append(", "); + builder.Append(cls.TemplateSpecializedArguments[i].ToString()); + } + } + else if (cls.TemplateKind == CppTemplateKind.TemplateClass) + { + for (var i = 0; i < cls.TemplateParameters.Count; i++) + { + if (i > 0) builder.Append(", "); + builder.Append(cls.TemplateParameters[i].ToString()); + } + } + + builder.Append(">"); + } return builder.ToString(); } } diff --git a/src/CppAst/CppClass.cs b/src/CppAst/CppClass.cs index 91472cb..df3e569 100644 --- a/src/CppAst/CppClass.cs +++ b/src/CppAst/CppClass.cs @@ -41,20 +41,57 @@ public CppClass(string name) : base(CppTypeKind.StructOrClass) /// public string Name { get; set; } - public string FullName - { - get + public override string FullName + { + get { + StringBuilder sb = new StringBuilder(); string fullparent = FullParentName; - if(string.IsNullOrEmpty(fullparent)) + if (string.IsNullOrEmpty(fullparent)) { - return Name; + sb.Append(Name); } else { - return $"{fullparent}{Name}"; + sb.Append($"{fullparent}::{Name}"); + } + + if (TemplateKind == CppTemplateKind.TemplateClass + || TemplateKind == CppTemplateKind.PartialTemplateClass) + { + sb.Append('<'); + for (int i = 0; i < TemplateParameters.Count; i++) + { + var tp = TemplateParameters[i]; + if (i != 0) + { + sb.Append(", "); + } + sb.Append(tp.ToString()); + } + sb.Append('>'); } - } + else if (TemplateKind == CppTemplateKind.TemplateSpecializedClass) + { + sb.Append('<'); + for (int i = 0; i < TemplateSpecializedArguments.Count; i++) + { + var ta = TemplateSpecializedArguments[i]; + if (i != 0) + { + sb.Append(", "); + } + sb.Append(ta.ArgString); + } + sb.Append('>'); + } + //else if(TemplateKind == CppTemplateKind.PartialTemplateClass) + //{ + // sb.Append('<'); + // sb.Append('>'); + //} + return sb.ToString(); + } } /// diff --git a/src/CppAst/CppElement.cs b/src/CppAst/CppElement.cs index 944f377..7e801da 100644 --- a/src/CppAst/CppElement.cs +++ b/src/CppAst/CppElement.cs @@ -25,35 +25,46 @@ public string FullParentName { get { - string tmpname = ""; - var p = Parent; - while (p != null) - { - if (p is CppClass) - { - var cpp = p as CppClass; - tmpname = $"{cpp.Name}::{tmpname}"; - p = cpp.Parent; - } - else if (p is CppNamespace) - { - var ns = p as CppNamespace; - tmpname = $"{ns.Name}::{tmpname}"; - p = ns.Parent; - } - else if (p is CppCompilation) - { - // root namespace here, just ignore~ - p = null; - } - else - { - throw new NotImplementedException("Can not be here, not support type here!"); - } - } + string tmpname = ""; + var p = Parent; + while (p != null) + { + if (p is CppClass) + { + var cpp = p as CppClass; + tmpname = $"{cpp.Name}::{tmpname}"; + p = cpp.Parent; + } + else if (p is CppNamespace) + { + var ns = p as CppNamespace; - return tmpname; - } + //Just ignore inline namespace + if (!ns.IsInlineNamespace) + { + tmpname = $"{ns.Name}::{tmpname}"; + } + p = ns.Parent; + } + else if (p is CppCompilation) + { + // root namespace here, just ignore~ + p = null; + } + else + { + throw new NotImplementedException("Can not be here, not support type here!"); + } + } + + //Try to remove not need `::` in string tails. + if (tmpname.EndsWith("::")) + { + tmpname = tmpname.Substring(0, tmpname.Length - 2); + } + + return tmpname; + } } /// diff --git a/src/CppAst/CppEnum.cs b/src/CppAst/CppEnum.cs index dc4dbcf..43f6273 100644 --- a/src/CppAst/CppEnum.cs +++ b/src/CppAst/CppEnum.cs @@ -30,22 +30,21 @@ public CppEnum(string name) : base(CppTypeKind.Enum) /// public string Name { get; set; } - public string FullName - { - get - { - string fullparent = FullParentName; - if (string.IsNullOrEmpty(fullparent)) - { - return Name; - } - else - { - return $"{fullparent}{Name}"; - } - } - } - + public override string FullName + { + get + { + string fullparent = FullParentName; + if (string.IsNullOrEmpty(fullparent)) + { + return Name; + } + else + { + return $"{fullparent}::{Name}"; + } + } + } /// /// Gets or sets a boolean indicating if this enum is scoped. diff --git a/src/CppAst/CppFunction.cs b/src/CppAst/CppFunction.cs index 99addaa..a1ca7a3 100644 --- a/src/CppAst/CppFunction.cs +++ b/src/CppAst/CppFunction.cs @@ -97,6 +97,7 @@ public int DefaultParamCount public bool IsConst => ((int)Flags & (int)CppFunctionFlags.Const) != 0; + public bool IsFunctionTemplate => ((int)Flags & (int)CppFunctionFlags.FunctionTemplate) != 0; /// public List TemplateParameters { get; } diff --git a/src/CppAst/CppFunctionFlags.cs b/src/CppAst/CppFunctionFlags.cs index 4f2ce47..96e6937 100644 --- a/src/CppAst/CppFunctionFlags.cs +++ b/src/CppAst/CppFunctionFlags.cs @@ -61,5 +61,10 @@ public enum CppFunctionFlags /// This is a variadic function (has `...` parameter) /// Variadic = 1 << 8, + + /// + /// This is a function template (has template params in function) + /// + FunctionTemplate = 1 << 9, } } \ No newline at end of file diff --git a/src/CppAst/CppGlobalDeclarationContainer.cs b/src/CppAst/CppGlobalDeclarationContainer.cs index 90f3a29..29fdd10 100644 --- a/src/CppAst/CppGlobalDeclarationContainer.cs +++ b/src/CppAst/CppGlobalDeclarationContainer.cs @@ -109,6 +109,21 @@ private CppElement SearchForChild(CppElement parent, string child_name) if (t != null) return t; } + //Not found, try to find in inline namespace. + if(parent is CppNamespace) + { + var ns = parent as CppNamespace; + foreach(var sn in ns.Namespaces) + { + if (sn.IsInlineNamespace) + { + var findElem = SearchForChild(sn, child_name); + //Find it in inline namespace, just return. + if(findElem != null) return findElem; + } + } + } + return null; } @@ -117,7 +132,7 @@ private CppElement SearchForChild(CppElement parent, string child_name) /// /// Name of the element to find /// The CppElement found or null if not found - public CppElement FindByFullName(string name) + public CppElement FindByFullName(string name) { var arr = name.Split(new string[] { "::" }, StringSplitOptions.RemoveEmptyEntries); if(arr.Length == 0) return null; diff --git a/src/CppAst/CppModelBuilder.cs b/src/CppAst/CppModelBuilder.cs index 43fe513..f5aa55e 100644 --- a/src/CppAst/CppModelBuilder.cs +++ b/src/CppAst/CppModelBuilder.cs @@ -51,16 +51,15 @@ public CXChildVisitResult VisitTranslationUnit(CXCursor cursor, CXCursor parent, return VisitMember(cursor, parent, data); } - private void TryToCreateTemplateParameters(CppClass parentclass, CXCursor cursor, void* data) + private CppType TryToCreateTemplateParameters(CXCursor cursor, void* data) { switch (cursor.Kind) { case CXCursorKind.CXCursor_TemplateTypeParameter: { var parameterTypeName = new CppTemplateParameterType(GetCursorSpelling(cursor)); - parentclass.TemplateParameters.Add(parameterTypeName); + return parameterTypeName; } - break; case CXCursorKind.CXCursor_NonTypeTemplateParameter: { //Just use low level ClangSharp object to do the logic @@ -69,22 +68,20 @@ private void TryToCreateTemplateParameters(CppClass parentclass, CXCursor cursor var tmpname = cursor.Spelling.ToString(); var noneTypeParam = new CppTemplateParameterNonType(tmpname, tmpcpptype); - parentclass.TemplateParameters.Add(noneTypeParam); + return noneTypeParam; } - break; case CXCursorKind.CXCursor_TemplateTemplateParameter: { //ToDo: add template template parameter support here~~ Debug.WriteLine("[Warning] template template parameter maybe not handle right here!"); var tmplparam = new CppTemplateParameterType(GetCursorSpelling(cursor)); - parentclass.TemplateParameters.Add(tmplparam); + return tmplparam; } - break; } + return null; } - private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, void* data) { var typeAsCString = clang.getCursorUSR(cursor).CString.ToString(); @@ -117,6 +114,7 @@ private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, voi Debug.Assert(parentGlobalDeclarationContainer != null); var ns = new CppNamespace(GetCursorSpelling(cursor)); symbol = ns; + ns.IsInlineNamespace = cursor.IsInlineNamespace; defaultContainerVisibility = CppVisibility.Default; parentGlobalDeclarationContainer.Namespaces.Add(ns); break; @@ -132,6 +130,7 @@ private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, voi break; case CXCursorKind.CXCursor_ClassTemplate: + case CXCursorKind.CXCursor_ClassTemplatePartialSpecialization: case CXCursorKind.CXCursor_ClassDecl: case CXCursorKind.CXCursor_StructDecl: case CXCursorKind.CXCursor_UnionDecl: @@ -160,33 +159,46 @@ private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, voi cppClass.TemplateKind = CppTemplateKind.TemplateClass; cursor.VisitChildren((childCursor, classCursor, clientData) => { - TryToCreateTemplateParameters(cppClass, childCursor, clientData); - + var tmplParam = TryToCreateTemplateParameters(childCursor, clientData); + if (tmplParam != null) + { + cppClass.TemplateParameters.Add(tmplParam); + } return CXChildVisitResult.CXChildVisit_Continue; }, new CXClientData((IntPtr)data)); } - else if (cursor.DeclKind == CX_DeclKind.CX_DeclKind_ClassTemplateSpecialization) + else if (cursor.DeclKind == CX_DeclKind.CX_DeclKind_ClassTemplateSpecialization + || cursor.DeclKind == CX_DeclKind.CX_DeclKind_ClassTemplatePartialSpecialization) { //Try to generate template class first cppClass.SpecializedTemplate = (CppClass)GetOrCreateDeclarationContainer(cursor.SpecializedCursorTemplate, data).Container; - cppClass.TemplateKind = CppTemplateKind.TemplateSpecializedClass; + if (cursor.DeclKind == CX_DeclKind.CX_DeclKind_ClassTemplatePartialSpecialization) + { + cppClass.TemplateKind = CppTemplateKind.PartialTemplateClass; + } + else + { + cppClass.TemplateKind = CppTemplateKind.TemplateSpecializedClass; + } + //Just use low level api to call ClangSharp - var temp_params = cppClass.SpecializedTemplate.TemplateParameters; + var tempArgsCount = cursor.NumTemplateArguments; + var tempParams = cppClass.SpecializedTemplate.TemplateParameters; - var temp_args_count = cursor.NumTemplateArguments; - //Just use template class template params here - foreach (var param in temp_params) + foreach (var param in tempParams) { cppClass.TemplateParameters.Add(param); } - ////var temp_decl = specilize_decl.SpecializedTemplate; - - Debug.Assert(cppClass.SpecializedTemplate.TemplateParameters.Count == temp_args_count); + if(cppClass.TemplateKind == CppTemplateKind.TemplateSpecializedClass) + { + Debug.Assert(cppClass.SpecializedTemplate.TemplateParameters.Count == tempArgsCount); + } + - for (uint i = 0; i < temp_args_count; i++) + for (uint i = 0; i < tempArgsCount; i++) { var arg = cursor.GetTemplateArgument(i); switch (arg.kind) @@ -194,32 +206,25 @@ private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, voi case CXTemplateArgumentKind.CXTemplateArgumentKind_Type: { var argh = arg.AsType; - var arg_type = GetCppType(argh.Declaration, argh, cursor, data); - cppClass.TemplateSpecializedArguments.Add(new CppTemplateArgument(temp_params[(int)i], arg_type)); + var argType = GetCppType(argh.Declaration, argh, cursor, data); + cppClass.TemplateSpecializedArguments.Add(new CppTemplateArgument(tempParams[(int)i], argType)); } break; case CXTemplateArgumentKind.CXTemplateArgumentKind_Integral: { - cppClass.TemplateSpecializedArguments.Add(new CppTemplateArgument(temp_params[(int)i], arg.AsIntegral)); + cppClass.TemplateSpecializedArguments.Add(new CppTemplateArgument(tempParams[(int)i], arg.AsIntegral)); } break; default: { Debug.WriteLine($"[Warning]template argument in class:{cppClass.FullName} with type: {arg.kind.ToString()} do not handle right now!"); - cppClass.TemplateSpecializedArguments.Add(new CppTemplateArgument(temp_params[(int)i], arg.ToString())); + cppClass.TemplateSpecializedArguments.Add(new CppTemplateArgument(tempParams[(int)i], arg.ToString())); } break; } } } - else if (cursor.DeclKind == CX_DeclKind.CX_DeclKind_ClassTemplatePartialSpecialization) - { - Debug.WriteLine("[Warning]Maybe can not run to here in CppAst!"); - //Try to generate template class first - cppClass.SpecializedTemplate = (CppClass)GetOrCreateDeclarationContainer(cursor.SpecializedCursorTemplate, data).Container; - cppClass.TemplateKind = CppTemplateKind.PartialTemplateClass; - } else { cppClass.TemplateKind = CppTemplateKind.NormalClass; @@ -1229,6 +1234,15 @@ private CppFunction VisitFunctionDecl(CXCursor cursor, CXCursor parent, void* da } var functionName = GetCursorSpelling(cursor); + + //We need ignore the function define out in the class definition here(Otherwise it will has two same functions here~)! + var semKind = cursor.SemanticParent.Kind; + if ((semKind == CXCursorKind.CXCursor_StructDecl || semKind == CXCursorKind.CXCursor_ClassDecl) + && cursor.LexicalParent != cursor.SemanticParent) + { + return null; + } + var cppFunction = new CppFunction(functionName) { Visibility = contextContainer.CurrentVisibility, @@ -1247,6 +1261,21 @@ private CppFunction VisitFunctionDecl(CXCursor cursor, CXCursor parent, void* da container.Functions.Add(cppFunction); } + if (cursor.kind == CXCursorKind.CXCursor_FunctionTemplate) + { + cppFunction.Flags |= CppFunctionFlags.FunctionTemplate; + //Handle template argument here~ + cursor.VisitChildren((childCursor, funcCursor, clientData) => + { + var tmplParam = TryToCreateTemplateParameters(childCursor, clientData); + if (tmplParam != null) + { + cppFunction.TemplateParameters.Add(tmplParam); + } + return CXChildVisitResult.CXChildVisit_Continue; + }, new CXClientData((IntPtr)data)); + } + if (cursor.Kind == CXCursorKind.CXCursor_CXXMethod) { cppFunction.Flags |= CppFunctionFlags.Method; @@ -1697,9 +1726,17 @@ private CppType VisitTypeAliasDecl(CXCursor cursor, void* data) } var contextContainer = GetOrCreateDeclarationContainer(cursor.SemanticParent, data); - var underlyingTypeDefType = GetCppType(cursor.TypedefDeclUnderlyingType.Declaration, cursor.TypedefDeclUnderlyingType, cursor, data); - var typedefName = GetCursorSpelling(cursor); + var kind = cursor.Kind; + + CXCursor usedCursor = cursor; + if(kind == CXCursorKind.CXCursor_TypeAliasTemplateDecl) + { + usedCursor = cursor.TemplatedDecl; + } + + var underlyingTypeDefType = GetCppType(usedCursor.TypedefDeclUnderlyingType.Declaration, usedCursor.TypedefDeclUnderlyingType, usedCursor, data); + var typedefName = GetCursorSpelling(usedCursor); if (AutoSquashTypedef && underlyingTypeDefType is ICppMember cppMember && (string.IsNullOrEmpty(cppMember.Name) || typedefName == cppMember.Name)) { diff --git a/src/CppAst/CppNamespace.cs b/src/CppAst/CppNamespace.cs index d673c2a..4a42b17 100644 --- a/src/CppAst/CppNamespace.cs +++ b/src/CppAst/CppNamespace.cs @@ -33,6 +33,11 @@ public CppNamespace(string name) /// public string Name { get; set; } + /// + /// Is the namespace inline or not(such as std::__1::vector). + /// + public bool IsInlineNamespace { get; set; } + /// public CppContainerList Fields { get; } diff --git a/src/CppAst/CppTemplateArgument.cs b/src/CppAst/CppTemplateArgument.cs index 64cfc08..2f97827 100644 --- a/src/CppAst/CppTemplateArgument.cs +++ b/src/CppAst/CppTemplateArgument.cs @@ -10,30 +10,30 @@ namespace CppAst /// /// For c++ specialized template argument /// - public class CppTemplateArgument : CppElement + public class CppTemplateArgument : CppType { - public CppTemplateArgument(CppType sourceParam, CppType typeArg) + public CppTemplateArgument(CppType sourceParam, CppType typeArg) : base(CppTypeKind.TemplateArgumentType) { SourceParam = sourceParam ?? throw new ArgumentNullException(nameof(sourceParam)); ArgAsType = typeArg ?? throw new ArgumentNullException(nameof(typeArg)); ArgKind = CppTemplateArgumentKind.AsType; } - public CppTemplateArgument(CppType sourceParam, long intArg) - { + public CppTemplateArgument(CppType sourceParam, long intArg) : base(CppTypeKind.TemplateArgumentType) + { SourceParam = sourceParam ?? throw new ArgumentNullException(nameof(sourceParam)); ArgAsInteger = intArg; - ArgKind = CppTemplateArgumentKind.AsInteger; - } + ArgKind = CppTemplateArgumentKind.AsInteger; + } - public CppTemplateArgument(CppType sourceParam, string unknownStr) + public CppTemplateArgument(CppType sourceParam, string unknownStr) : base(CppTypeKind.TemplateArgumentType) { SourceParam = sourceParam ?? throw new ArgumentNullException(nameof(sourceParam)); ArgAsUnknown = unknownStr; - ArgKind = CppTemplateArgumentKind.Unknown; - } + ArgKind = CppTemplateArgumentKind.Unknown; + } - public CppTemplateArgumentKind ArgKind { get; } + public CppTemplateArgumentKind ArgKind { get; } public CppType ArgAsType { get; } @@ -45,17 +45,17 @@ public string ArgString { get { - switch(ArgKind) + switch (ArgKind) { case CppTemplateArgumentKind.AsType: - return ArgAsType.ToString(); + return ArgAsType.FullName; case CppTemplateArgumentKind.AsInteger: return ArgAsInteger.ToString(); - case CppTemplateArgumentKind.Unknown: + case CppTemplateArgumentKind.Unknown: return ArgAsUnknown; default: return "?"; - } + } } } @@ -65,6 +65,20 @@ public string ArgString /// public CppType SourceParam { get; } + + /// + public override int SizeOf + { + get => 0; + set => throw new InvalidOperationException("This type does not support SizeOf"); + } + + /// + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is CppTemplateArgument other && Equals(other); + } + /// public override int GetHashCode() { @@ -75,6 +89,12 @@ public override int GetHashCode() } + /// + public override CppType GetCanonicalType() => this; + + /// + + /// public override string ToString() => $"{SourceParam} = {ArgString}"; } diff --git a/src/CppAst/CppType.cs b/src/CppAst/CppType.cs index 683f31e..0d2f01e 100644 --- a/src/CppAst/CppType.cs +++ b/src/CppAst/CppType.cs @@ -48,5 +48,16 @@ public override int GetHashCode() /// /// A canonical type of this type instance public abstract CppType GetCanonicalType(); + + /// + /// We can use this name in exporter to use this type. + /// + public virtual string FullName + { + get + { + return ToString(); + } + } } } \ No newline at end of file diff --git a/src/CppAst/CppTypeKind.cs b/src/CppAst/CppTypeKind.cs index 21e329c..a40f928 100644 --- a/src/CppAst/CppTypeKind.cs +++ b/src/CppAst/CppTypeKind.cs @@ -54,6 +54,10 @@ public enum CppTypeKind /// TemplateParameterNonType, /// + /// A template specialized argument type. + /// + TemplateArgumentType, + /// /// An unexposed type. /// Unexposed, diff --git a/src/CppAst/CppTypedef.cs b/src/CppAst/CppTypedef.cs index 4404677..4aade4c 100644 --- a/src/CppAst/CppTypedef.cs +++ b/src/CppAst/CppTypedef.cs @@ -34,23 +34,23 @@ public CppTypedef(string name, CppType type) : base(CppTypeKind.Typedef) /// public string Name { get; set; } - public string FullName - { - get - { - string fullparent = FullParentName; - if (string.IsNullOrEmpty(fullparent)) - { - return Name; - } - else - { - return $"{fullparent}{Name}"; - } - } - } + public override string FullName + { + get + { + string fullparent = FullParentName; + if (string.IsNullOrEmpty(fullparent)) + { + return Name; + } + else + { + return $"{fullparent}::{Name}"; + } + } + } - private bool Equals(CppTypedef other) + private bool Equals(CppTypedef other) { return base.Equals(other) && string.Equals(Name, other.Name); }