CppSharp
CppSharp copied to clipboard
C# code generation fails with System.NullReferenceException
Brief Description
C# code generation for header file my_header.h
#ifndef MY_HEADER_H
#define MY_HEADER_H
#include <cstdint>
#include <variant>
class my_variant_wrapper
{
public:;
my_variant_wrapper(float result) :
m_Result(result)
{
}
my_variant_wrapper(uint8_t errorCode) :
m_Result(errorCode)
{
}
private:
std::variant<float, uint8_t> m_Result;
};
#endif
fails with System.NullReferenceException.
Console output is:
Parsing libraries...
Parsed 'my_library.dll'
Parsing code...
Parsed 'my_header.h'
Processing code...
Pass 'CppSharp.Passes.ResolveIncompleteDeclsPass'
Pass 'CppSharp.Passes.IgnoreSystemDeclarationsPass'
Pass 'CppSharp.Passes.MatchParamNamesWithInstantiatedFromPass'
Pass 'CppSharp.Passes.EqualiseAccessOfOverrideAndBasePass'
Pass 'CppSharp.Passes.FlattenAnonymousTypesToFields'
Pass 'CppSharp.Passes.CheckIgnoredDeclsPass'
Decl 'm_Result' was ignored due to invalid access
Field 'my_variant_wrapper::m_Result' was ignored due to ignored type '::std::variant<>'
Field '::_Tail' was ignored due to ignored type '::std::_Variant_storage_<1>'
Field '_Variant_storage_::' was ignored due to ignored type '::std::_Variant_storage_<1>::'
Field '::_Tail' was ignored due to ignored type '::std::_Variant_storage_<1>'
Field '_Variant_storage_::' was ignored due to ignored type '::std::_Variant_storage_<1>::'
Pass 'CppSharp.Passes.MarkUsedClassInternalsPass'
Pass 'CppSharp.Passes.TrimSpecializationsPass'
Pass 'CppSharp.Passes.CheckAmbiguousFunctions'
Found ambiguous overload: my_variant_wrapper::operator=
Pass 'CppSharp.Passes.GenerateSymbolsPass'
Pass 'CppSharp.Passes.CheckIgnoredDeclsPass'
Field 'my_variant_wrapper::m_Result' was ignored due to internal type '::std::variant<>'
Function 'operator=' was ignored due to ignored param
Field '::_Tail' was ignored due to ignored type '::std::_Variant_storage_<1>'
Field '_Variant_storage_::' was ignored due to internal type '::std::_Variant_storage_<1>::'
Field '::_Tail' was ignored due to ignored type '::std::_Variant_storage_<1>'
Field '_Variant_storage_::' was ignored due to ignored type '::std::_Variant_storage_<1>::'
Pass 'CppSharp.Passes.MoveFunctionToClassPass'
Pass 'CppSharp.Passes.ValidateOperatorsPass'
Invalid operator overload my_variant_wrapper::Equal
Pass 'CppSharp.Passes.FindSymbolsPass'
Pass 'CppSharp.Passes.CheckMacroPass'
Pass 'CppSharp.Passes.CheckStaticClass'
Pass 'CppSharp.Passes.CheckAmbiguousFunctions'
Pass 'CppSharp.Passes.ConstructorToConversionOperatorPass'
Pass 'CppSharp.Passes.MarshalPrimitivePointersAsRefTypePass'
Pass 'CppSharp.Passes.CheckOperatorsOverloadsPass'
Pass 'CppSharp.Passes.CheckVirtualOverrideReturnCovariance'
Pass 'CppSharp.Passes.CleanCommentsPass'
Pass 'CppSharp.Passes.CheckAbiParameters'
Pass 'CppSharp.Passes.CleanInvalidDeclNamesPass'
Pass 'CppSharp.FastDelegateToDelegatesPass'
Pass 'CppSharp.Passes.FieldToPropertyPass'
Property created from field: std::_Variant_storage_<bool, >::_0::___Head
Property created from field: std::_Variant_storage_<bool, >::_0::___Tail
Pass 'CppSharp.Passes.CheckIgnoredDeclsPass'
Field 'my_variant_wrapper::m_Result' was ignored due to internal type '::std::variant<>'
Function 'operator=' was ignored due to ignored param
Function 'operator=' was ignored due to ignored param
Field '_0::___Tail' was ignored due to ignored type '::std::_Variant_storage_<1>'
Property '_Head' was ignored due to ignored type
Property '_Tail' was ignored due to ignored type
Field '_Variant_storage_::_0' was ignored due to internal type '::std::_Variant_storage_<1>::'
Field '_0::_Tail' was ignored due to ignored type '::std::_Variant_storage_<1>'
Field '_Variant_storage_::_0' was ignored due to ignored type '::std::_Variant_storage_<1>::'
Pass 'CppSharp.Passes.CheckFlagEnumsPass'
Pass 'CppSharp.Passes.MakeProtectedNestedTypesPublicPass'
Pass 'CppSharp.Passes.GenerateAbstractImplementationsPass'
Pass 'CppSharp.Passes.MultipleInheritancePass'
Pass 'CppSharp.Passes.DelegatesPass'
Pass 'CppSharp.Passes.GetterSetterToPropertyPass'
Pass 'CppSharp.Passes.StripUnusedSystemTypesPass'
Pass 'CppSharp.Passes.SpecializationMethodsWithDependentPointersPass'
Pass 'CppSharp.Passes.ParamTypeToInterfacePass'
Pass 'CppSharp.Passes.CheckDuplicatedNamesPass'
Pass 'CppSharp.Passes.CaseRenamePass'
Pass 'CppSharp.Passes.CheckKeywordNamesPass'
Pass 'CppSharp.Passes.HandleVariableInitializerPass'
Pass 'CppSharp.MarkEventsWithUniqueIdPass'
Generating code...
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
at CppSharp.AST.ClassExtensions.HasDependentValueFieldInLayout(Class class, IEnumerable`1 specializations)
at CppSharp.Generators.Helpers.GetSuffixForInternal(Class class)
at CppSharp.Generators.CSharp.CSharpTypePrinter.VisitClassDecl(Class class)
at CppSharp.AST.Class.Visit[T](IDeclVisitor`1 visitor)
at CppSharp.Generators.TypePrinter.VisitTagType(TagType tag, TypeQualifiers quals)
at CppSharp.Generators.CSharp.CSharpTypePrinter.VisitTagType(TagType tag, TypeQualifiers quals)
at CppSharp.AST.TagType.Visit[T](ITypeVisitor`1 visitor, TypeQualifiers quals)
at CppSharp.Generators.TypePrinter.VisitQualifiedType(QualifiedType type)
at CppSharp.AST.QualifiedType.Visit[T](ITypeVisitor`1 visitor)
at CppSharp.Generators.CSharp.CSharpTypePrinter.VisitFieldDecl(Field field)
at CppSharp.Generators.CSharp.CSharpSources.GenerateClassInternalsFields(Class class, Boolean sequentialLayout)
at CppSharp.Generators.CSharp.CSharpSources.GenerateClassInternals(Class class)
at CppSharp.Generators.CSharp.CSharpSources.GenerateClassTemplateSpecializationsInternals(Class template, IList`1 specializations)
at CppSharp.Generators.CSharp.CSharpSources.GenerateClassTemplateSpecializationInternal(Class classTemplate)
at CppSharp.Generators.CSharp.CSharpSources.VisitClassDecl(Class class)
at CppSharp.AST.Class.Visit[T](IDeclVisitor`1 visitor)
at CppSharp.Generators.CSharp.CSharpSources.VisitDeclContext(DeclarationContext context)
at CppSharp.Generators.CodeGenerator.VisitNamespace(Namespace namespace)
at CppSharp.Generators.CSharp.CSharpSources.VisitNamespace(Namespace namespace)
at CppSharp.AST.Namespace.Visit[T](IDeclVisitor`1 visitor)
at CppSharp.Generators.CSharp.CSharpSources.VisitDeclContext(DeclarationContext context)
at CppSharp.Generators.CodeGenerator.VisitNamespace(Namespace namespace)
at CppSharp.Generators.CSharp.CSharpSources.VisitNamespace(Namespace namespace)
at CppSharp.Generators.CodeGenerator.VisitTranslationUnit(TranslationUnit unit)
at CppSharp.AST.TranslationUnit.Visit[T](IDeclVisitor`1 visitor)
at CppSharp.Generators.CSharp.CSharpSources.Process()
at CppSharp.Generators.Generator.GenerateModule(Module module)
at CppSharp.Generators.Generator.Generate()
at CppSharp.Driver.GenerateCode()
at CppSharp.ConsoleDriver.Run(ILibrary library)
at Program.<Main>$(String[] args)
OS: Windows
If I modify my header file to define CS_IGNORE and I apply it on a class level generation still fails.
#ifndef MY_HEADER_H
#define MY_HEADER_H
#define CS_IGNORE
#include <cstdint>
#include <variant>
class CS_IGNORE my_variant_wrapper
{
public:;
my_variant_wrapper(float result) :
m_Result(result)
{
}
my_variant_wrapper(uint8_t errorCode) :
m_Result(errorCode)
{
}
private:
std::variant<float, uint8_t> m_Result;
};
#endif
I also encountered the same error, also about std::variant
Is there any feedback for this?
Running into this one now, so yeah... Guess I'll fix this one too haha.
Minimal repro:
template<typename X>
class SpecializedTemplateReferencingChildStruct;
template<typename R>
class SpecializedTemplateReferencingChildStruct<R*> {
struct Bar {
R xyzzy;
};
Bar baz;
};
class C {
SpecializedTemplateReferencingChildStruct<int*> a;
};
Root cause of the error is here.
The code tries to look for Bar in the template, but because Bar is dependent on R the declaration is instead in the specialization itself.
This code is in general also not very resilient: it only checks the direct parent for specializations.
If I modify the template a bit to look like this instead:
template<typename X>
class SpecializedTemplateReferencingDeepChildStruct;
template<typename R>
class SpecializedTemplateReferencingDeepChildStruct<R*> {
struct Goo {
struct Bar {
bool xyzzy;
};
};
typename Goo::Bar baz;
};
class D {
SpecializedTemplateReferencingDeepChildStruct<int*> a;
};
Then the GetSuffixForInternal function will immediately quit through the specialization == null shortcut, because the specialization is actually 2 levels up. It should walk the whole list of parents instead.
Ok, this issue seems quite a bit more complex than that other one, so I'm going to post a small update here:
Ideally, I wanted to solve this issue by calling getTemplateInstantiationPattern in Clang during the initial parse and storing the result on the Class node, so that the actual correct original template as known by Clang can be referenced directly. However, I had the native code set up and then realized I have no idea how to regenerate the parser bindings. Tried running Cpp.Parser.Bootstrap.exe in the bin directory but that crashes when it can't find llvm/Support/TrailingObjects.h.
So at the moment I'm using a hack that does "solve" the issue:
public static string GetSuffixForInternal(Class @class)
{
var specialization = @class.GetParentSpecialization();
if (specialization == null)
return string.Empty;
Class template = @class == specialization
? specialization.TemplatedDecl.TemplatedClass
: specialization.FindNestedClass(@class.Name) ?? specialization.TemplatedDecl.TemplatedClass.FindNestedClass(@class.Name);
if (template.HasDependentValueFieldInLayout())
{
if (specialization.Arguments.All(
a => a.Type.Type?.IsAddress() == true))
return "_Ptr";
return GetSuffixFor(specialization);
}
return string.Empty;
}
but is obviously really fragile as well. If the search back down into the AST tree finds another class with the same name first it will incorrectly take that one.
However, even though it doesn't crash anymore now, the code it generates is still faulty:
namespace test
{
namespace SpecializedTemplateReferencingChildStruct
{
[StructLayout(LayoutKind.Sequential, Size = 4)]
public unsafe partial struct __Internal
{
internal global::test.SpecializedTemplateReferencingChildStruct.Bar.__Internal baz;
}
}
// implementation of C...
}
It doesn't generate anything for Bar at all, so it seems like CppSharp in general has trouble with this kind of construct?
To re-generate the parser bindings you should use https://github.com/mono/CppSharp/blob/main/src/CppParser/ParserGen/ParserGen.cs.
https://github.com/mono/CppSharp/releases/download/CppSharp/headers.zip is also necessary to generate bindings for all platforms.
It doesn't generate anything for
Barat all, so it seems like CppSharp in general has trouble with this kind of construct?
Yep, template support in general still has some issues.