Browse Source

Wrappers can now override virtual methods from non-primary bases

pull/1/head
Alex Corrado 15 years ago
parent
commit
ca23db9c92
  1. 16
      src/Mono.Cxxi/Abi/CppAbi.cs
  2. 5
      src/Mono.Cxxi/Abi/VTable.cs
  3. 17
      src/Mono.Cxxi/CppInstancePtr.cs
  4. 254
      src/Mono.Cxxi/CppTypeInfo.cs
  5. 5
      src/Mono.Cxxi/Util/LazyGeneratedList.cs
  6. 106
      src/generator/Templates/CSharp/CSharpClass.cs
  7. 1
      src/generator/Templates/CSharp/CSharpClass.tt
  8. 2
      tests/InheritanceTests.cs

16
src/Mono.Cxxi/Abi/CppAbi.cs

@ -74,8 +74,8 @@ namespace Mono.Cxxi.Abi { @@ -74,8 +74,8 @@ namespace Mono.Cxxi.Abi {
protected static readonly MethodInfo cppip_tomanaged_size = typeof (CppInstancePtr).GetMethod ("ToManaged", BindingFlags.Static | BindingFlags.NonPublic, null, new Type [] { typeof (IntPtr), typeof (int) }, null);
protected static readonly ConstructorInfo cppip_fromnative = typeof (CppInstancePtr).GetConstructor (new Type [] { typeof (IntPtr) });
protected static readonly ConstructorInfo cppip_fromsize = typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null, new Type [] { typeof (int) }, null);
protected static readonly ConstructorInfo cppip_fromsize_managed = typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null,
new Type[] { typeof (int), typeof (object) }, null);
protected static readonly ConstructorInfo cppip_fromtype_managed = typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null,
new Type[] { typeof (CppTypeInfo), typeof (object) }, null);
protected static readonly ConstructorInfo notimplementedexception = typeof (NotImplementedException).GetConstructor (new Type [] { typeof (string) });
protected static readonly MethodInfo type_gettypefromhandle = typeof (Type).GetMethod ("GetTypeFromHandle");
protected static readonly MethodInfo marshal_offsetof = typeof (Marshal).GetMethod ("OffsetOf");
@ -356,6 +356,7 @@ namespace Mono.Cxxi.Abi { @@ -356,6 +356,7 @@ namespace Mono.Cxxi.Abi {
* Implements the managed trampoline that will be invoked from the vtable by native C++ code when overriding
* the specified C++ virtual method with the specified managed one.
*/
// FIXME: This should be moved into CppTypeInfo class
internal virtual Delegate GetManagedOverrideTrampoline (CppTypeInfo typeInfo, int vtableIndex)
{
if (typeInfo.WrapperType == null)
@ -392,7 +393,7 @@ namespace Mono.Cxxi.Abi { @@ -392,7 +393,7 @@ namespace Mono.Cxxi.Abi {
argLoadStart = 1;
il.Emit (OpCodes.Ldarg_0);
il.Emit (OpCodes.Ldc_I4, typeInfo.NativeSize);
il.Emit (OpCodes.Ldc_I4, typeInfo.GCHandleOffset);
var getManagedObj = cppip_tomanaged_size.MakeGenericMethod (typeInfo.WrapperType);
il.Emit (OpCodes.Call, getManagedObj);
@ -491,14 +492,15 @@ namespace Mono.Cxxi.Abi { @@ -491,14 +492,15 @@ namespace Mono.Cxxi.Abi {
// this._typeInfo.NativeSize
il.Emit (OpCodes.Ldarg_0);
il.Emit (OpCodes.Ldfld, typeinfo_field);
il.Emit (OpCodes.Callvirt, typeinfo_nativesize);
if (wrapper_type != null && interfaceMethod.GetParameters ().Any ()) {
// load managed wrapper
il.Emit (OpCodes.Ldarg_1);
il.Emit (OpCodes.Newobj, cppip_fromsize_managed);
} else
il.Emit (OpCodes.Newobj, cppip_fromtype_managed);
} else {
il.Emit (OpCodes.Callvirt, typeinfo_nativesize);
il.Emit (OpCodes.Newobj, cppip_fromsize);
}
}
protected virtual void EmitConstruct (ILGenerator il, MethodInfo nativeMethod, PInvokeSignature psig,
@ -743,7 +745,7 @@ namespace Mono.Cxxi.Abi { @@ -743,7 +745,7 @@ namespace Mono.Cxxi.Abi {
try {
Activator.CreateInstance (otherWrapperType, (CppTypeInfo)(new DummyCppTypeInfo ()));
} catch (MissingMethodException mme) {
} catch (MissingMethodException) {
throw new InvalidProgramException (string.Format ("Type `{0}' implements ICppObject but does not contain a public constructor that takes CppTypeInfo", otherWrapperType));
}

5
src/Mono.Cxxi/Abi/VTable.cs

@ -96,7 +96,10 @@ namespace Mono.Cxxi.Abi { @@ -96,7 +96,10 @@ namespace Mono.Cxxi.Abi {
public virtual void InitInstance (ref CppInstancePtr instance)
{
var basePtr = Marshal.ReadIntPtr (instance.Native);
Debug.Assert (basePtr != IntPtr.Zero && basePtr != vtPtr);
Debug.Assert (basePtr != IntPtr.Zero);
if (basePtr == vtPtr)
return;
instance.NativeVTable = basePtr;

17
src/Mono.Cxxi/CppInstancePtr.cs

@ -70,11 +70,11 @@ namespace Mono.Cxxi { @@ -70,11 +70,11 @@ namespace Mono.Cxxi {
}
// Alloc a new C++ instance
internal CppInstancePtr (int nativeSize, object managedWrapper)
internal CppInstancePtr (CppTypeInfo typeInfo, object managedWrapper)
{
// Under the hood, we're secretly subclassing this C++ class to store a
// handle to the managed wrapper.
int allocSize = nativeSize + Marshal.SizeOf (typeof (IntPtr));
int allocSize = typeInfo.GCHandleOffset + IntPtr.Size;
ptr = Marshal.AllocHGlobal (allocSize);
// NOTE: native_vtptr will be set later after native ctor is called
@ -86,7 +86,7 @@ namespace Mono.Cxxi { @@ -86,7 +86,7 @@ namespace Mono.Cxxi {
Marshal.Copy (zeroArray, 0, ptr, allocSize);
IntPtr handlePtr = MakeGCHandle (managedWrapper);
Marshal.WriteIntPtr (ptr, nativeSize, handlePtr);
Marshal.WriteIntPtr (ptr, typeInfo.GCHandleOffset, handlePtr);
manage_memory = true;
}
@ -99,6 +99,15 @@ namespace Mono.Cxxi { @@ -99,6 +99,15 @@ namespace Mono.Cxxi {
manage_memory = true;
}
// Gets a casted CppInstancePtr
internal CppInstancePtr (CppInstancePtr instance, int offset)
{
// FIXME: On NET_4_0 use IntPtr.Add
ptr = new IntPtr (instance.Native.ToInt64 () + offset);
native_vtptr = IntPtr.Zero;
manage_memory = false;
}
// Get a CppInstancePtr for an existing C++ instance from an IntPtr
public CppInstancePtr (IntPtr native)
{
@ -171,7 +180,7 @@ namespace Mono.Cxxi { @@ -171,7 +180,7 @@ namespace Mono.Cxxi {
if (managed_vtptr_to_gchandle_offset == null)
managed_vtptr_to_gchandle_offset = new Dictionary<IntPtr, int> ();
managed_vtptr_to_gchandle_offset [vtable.Pointer] = vtable.TypeInfo.NativeSize;
managed_vtptr_to_gchandle_offset [vtable.Pointer] = vtable.TypeInfo.GCHandleOffset;
}
internal static IntPtr MakeGCHandle (object managedWrapper)

254
src/Mono.Cxxi/CppTypeInfo.cs

@ -60,17 +60,20 @@ namespace Mono.Cxxi { @@ -60,17 +60,20 @@ namespace Mono.Cxxi {
protected List<CppTypeInfo> base_classes;
// returns the number of vtable slots reserved for the
// base class(es)
// base class(es) before this class's virtual methods start
public int BaseVTableSlots { get; protected set; }
public bool TypeComplete { get; private set; }
public bool IsPrimaryBase { get; protected set; } // < True by default. set to False in cases where it is cloned as a non-primary base
protected int native_size;
protected int native_size_without_padding; // <- this refers to the size of all the fields declared in the nativeLayout struct
protected int field_offset_padding_without_vtptr;
protected int gchandle_offset_delta;
private VTable lazy_vtable;
public CppTypeInfo (CppAbi abi, IEnumerable<PInvokeSignature> virtualMethods, Type nativeLayout, Type/*?*/ wrapperType)
: this ()
{
Abi = abi;
NativeLayout = nativeLayout;
@ -85,27 +88,51 @@ namespace Mono.Cxxi { @@ -85,27 +88,51 @@ namespace Mono.Cxxi {
vt_overrides = new LazyGeneratedList<Delegate> (virtual_methods.Count, i => Abi.GetManagedOverrideTrampoline (this, i));
VTableOverrides = new ReadOnlyCollection<Delegate> (vt_overrides);
native_size_without_padding = nativeLayout.GetFields ().Any ()? Marshal.SizeOf (nativeLayout) : 0;
}
protected CppTypeInfo ()
{
base_classes = new List<CppTypeInfo> ();
BaseClasses = new ReadOnlyCollection<CppTypeInfo> (base_classes);
BaseVTableSlots = 0;
TypeComplete = false;
native_size = nativeLayout.GetFields ().Any ()? Marshal.SizeOf (nativeLayout) : 0;
field_offset_padding_without_vtptr = 0;
gchandle_offset_delta = 0;
TypeComplete = false;
IsPrimaryBase = true;
BaseVTableSlots = 0;
lazy_vtable = null;
}
protected CppTypeInfo ()
// The contract for Clone is that, if TypeComplete, working with the clone *through the public
// interface* is guaranteed not to affect the original. Note that any subclassses
// have access to protected stuff that is not covered by this guarantee.
public virtual CppTypeInfo Clone ()
{
return this.MemberwiseClone () as CppTypeInfo;
}
#region Type Layout
// the extra padding to allocate at the top of the class before the fields begin
// (by default, just the vtable pointer)
public virtual int FieldOffsetPadding {
get { return field_offset_padding_without_vtptr + (virtual_methods.Count != 0? IntPtr.Size : 0); }
}
public virtual int NativeSize {
get { return native_size_without_padding + FieldOffsetPadding; }
}
public virtual int GCHandleOffset {
get { return NativeSize + gchandle_offset_delta; }
}
public virtual void AddBase (CppTypeInfo baseType)
public void AddBase (CppTypeInfo baseType)
{
// by default, if we already have base class(es), this base's virtual methods are not included before the derived class's
var addVTable = base_classes.Count >= 1;
AddBase (baseType, addVTable);
// by default, only the primary base shares the subclass's primary vtable
AddBase (baseType, base_classes.Count >= 1);
}
protected virtual void AddBase (CppTypeInfo baseType, bool addVTable)
@ -113,9 +140,11 @@ namespace Mono.Cxxi { @@ -113,9 +140,11 @@ namespace Mono.Cxxi {
if (TypeComplete)
return;
base_classes.Add (baseType);
bool addVTablePointer = addVTable || base_classes.Count > 1;
int baseVMethodCount = baseType.virtual_methods.Count;
bool nonPrimary = base_classes.Count >= 1;
// by default, only the primary base shares the subclass's vtable ptr
var addVTablePointer = addVTable || nonPrimary;
var baseVMethodCount = baseType.virtual_methods.Count;
if (addVTable) {
// If we are adding a new vtable, don't skew the offsets of the of this subclass's methods.
@ -127,6 +156,7 @@ namespace Mono.Cxxi { @@ -127,6 +156,7 @@ namespace Mono.Cxxi {
vt_delegate_types.Add (baseVMethodCount);
vt_overrides.Add (baseVMethodCount);
} else {
// If we're not adding a new vtable, then all this base class's virtual methods go in primary vtable
@ -140,78 +170,155 @@ namespace Mono.Cxxi { @@ -140,78 +170,155 @@ namespace Mono.Cxxi {
vt_overrides.Add (baseVMethodCount);
}
field_offset_padding_without_vtptr += baseType.native_size +
if (nonPrimary) {
// Create a base-in-derived type info w/ a new vtable object
// if this is a non-primary base
baseType = baseType.Clone ();
baseType.IsPrimaryBase = false;
baseType.gchandle_offset_delta += native_size_without_padding + CountBases (b => !b.IsPrimaryBase) * IntPtr.Size;
baseType.vt_overrides = baseType.vt_overrides.Clone (); // managed override tramps will be regenerated with correct gchandle offset
// now, offset all previously added bases
foreach (var previousBase in base_classes) {
previousBase.gchandle_offset_delta += baseType.NativeSize;
}
}
base_classes.Add (baseType);
field_offset_padding_without_vtptr += baseType.native_size_without_padding +
(addVTablePointer? baseType.FieldOffsetPadding : baseType.field_offset_padding_without_vtptr);
}
public virtual CppInstancePtr Cast (ICppObject instance, Type targetType)
public virtual void CompleteType ()
{
if (TypeComplete)
return;
foreach (var baseClass in base_classes)
baseClass.CompleteType ();
TypeComplete = true;
RemoveVTableDuplicates ();
}
public int CountBases (Func<CppTypeInfo, bool> predicate)
{
int count = 0;
foreach (var baseClass in base_classes) {
count += baseClass.CountBases (predicate);
count += predicate (baseClass)? 1 : 0;
}
return count;
}
#endregion
#region Casting
protected virtual CppTypeInfo GetCastInfo (Type sourceType, Type targetType, out int offset)
{
var instanceType = instance.GetType ();
var found = false;
var offset = 0;
offset = 0;
if (WrapperType.Equals (targetType)) {
// check for downcast (base type -> this type)
foreach (var baseClass in base_classes) {
if (baseClass.WrapperType.Equals (instanceType)) {
found = true;
break;
if (baseClass.WrapperType.Equals (sourceType)) {
return baseClass;
}
offset -= baseClass.NativeSize;
}
} else if (WrapperType.IsAssignableFrom (instanceType)) {
} else if (WrapperType.IsAssignableFrom (sourceType)) {
// check for upcast (this type -> base type)
foreach (var baseClass in base_classes) {
if (baseClass.WrapperType.Equals (targetType)) {
found = true;
break;
return baseClass;
}
offset += baseClass.NativeSize;
}
} else {
throw new ArgumentException ("Either instance type or targetType must be equal to wrapper type.");
throw new ArgumentException ("Either source type or target type must be equal to this wrapper type.");
}
if (!found)
throw new InvalidCastException ("Cannot cast an instance of " + instanceType + " to " + targetType);
throw new InvalidCastException ("Cannot cast an instance of " + sourceType + " to " + targetType);
}
public virtual CppInstancePtr Cast (ICppObject instance, Type targetType)
{
int offset;
var baseTypeInfo = GetCastInfo (instance.GetType (), targetType, out offset);
var result = new CppInstancePtr (instance.Native, offset);
if (offset > 0 && instance.Native.IsManagedAlloc && baseTypeInfo.VirtualMethods.Count != 0) {
// we might need to paste the managed base-in-derived vtptr here --also inits native_vtptr
baseTypeInfo.VTable.InitInstance (ref result);
}
// Construct a new targetType wrapper, passing in our offset this ptr.
// FIXME: If the object was alloc'd by managed code, the allocating wrapper (i.e. "instance") will still free the memory when
// it is Disposed, even if wrappers created here still exist and are pointing to it. :/
// The casted wrapper created here may be Disposed safely though.
// FIXME: On NET_4_0 use IntPtr.Add
return new CppInstancePtr (new IntPtr (instance.Native.Native.ToInt64 () + offset));
return result;
}
public int CountBases (Func<CppTypeInfo, bool> predicate)
public virtual TTarget Cast<TTarget> (ICppObject instance) where TTarget : class
{
int count = 0;
foreach (var baseClass in base_classes) {
count += baseClass.CountBases (predicate);
count += predicate (baseClass)? 1 : 0;
}
return count;
TTarget result;
var ptr = Cast (instance, typeof (TTarget));
// Check for existing instance based on vtable ptr
result = CppInstancePtr.ToManaged<TTarget> (ptr.Native);
// Create a new wrapper if necessary
if (result == null)
result = Activator.CreateInstance (typeof (TTarget), ptr) as TTarget;
return result;
}
public virtual void CompleteType ()
public virtual void InitNonPrimaryBase (ICppObject baseInDerived, ICppObject derived, Type baseType)
{
if (TypeComplete)
return;
int offset;
var baseTypeInfo = GetCastInfo (derived.GetType (), baseType, out offset);
foreach (var baseClass in base_classes)
baseClass.CompleteType ();
Marshal.WriteIntPtr (baseInDerived.Native.Native, baseTypeInfo.GCHandleOffset, CppInstancePtr.MakeGCHandle (baseInDerived));
}
TypeComplete = true;
#endregion
RemoveVTableDuplicates ();
#region V-Table
public virtual VTable VTable {
get {
CompleteType ();
if (!virtual_methods.Any ())
return null;
if (lazy_vtable == null)
lazy_vtable = new VTable (this);
return lazy_vtable;
}
}
// the padding in the data pointed to by the vtable pointer before the list of function pointers starts
public virtual int VTableTopPadding {
get { return 0; }
}
// the amount of extra room alloc'd after the function pointer list of the vtbl
public virtual int VTableBottomPadding {
get { return 0; }
}
public virtual T GetAdjustedVirtualCall<T> (CppInstancePtr instance, int derivedVirtualMethodIndex)
where T : class /* Delegate */
{
return VTable.GetVirtualCallDelegate<T> (instance, BaseVTableSlots + derivedVirtualMethodIndex);
}
protected virtual void RemoveVTableDuplicates ()
@ -249,48 +356,7 @@ namespace Mono.Cxxi { @@ -249,48 +356,7 @@ namespace Mono.Cxxi {
return false;
}
public virtual T GetAdjustedVirtualCall<T> (CppInstancePtr instance, int derivedVirtualMethodIndex)
where T : class /* Delegate */
{
return VTable.GetVirtualCallDelegate<T> (instance, BaseVTableSlots + derivedVirtualMethodIndex);
}
public virtual VTable VTable {
get {
CompleteType ();
if (!virtual_methods.Any ())
return null;
if (lazy_vtable == null)
lazy_vtable = new VTable (this);
return lazy_vtable;
}
}
public virtual int NativeSize {
get {
CompleteType ();
return native_size + FieldOffsetPadding;
}
}
// the extra padding to allocate at the top of the class before the fields begin
// (by default, just the vtable pointer)
public virtual int FieldOffsetPadding {
get { return field_offset_padding_without_vtptr + (virtual_methods.Any ()? Marshal.SizeOf (typeof (IntPtr)) : 0); }
}
// the padding in the data pointed to by the vtable pointer before the list of function pointers starts
public virtual int VTableTopPadding {
get { return 0; }
}
// the amount of extra room alloc'd after the function pointer list of the vtbl
public virtual int VTableBottomPadding {
get { return 0; }
}
#endregion
}
// This is used internally by CppAbi:
@ -298,7 +364,7 @@ namespace Mono.Cxxi { @@ -298,7 +364,7 @@ namespace Mono.Cxxi {
public CppTypeInfo BaseTypeInfo { get; set; }
public override void AddBase (CppTypeInfo baseType)
protected override void AddBase (CppTypeInfo baseType, bool addVT)
{
BaseTypeInfo = baseType;
}

5
src/Mono.Cxxi/Util/LazyGeneratedList.cs

@ -47,6 +47,11 @@ namespace Mono.Cxxi.Util { @@ -47,6 +47,11 @@ namespace Mono.Cxxi.Util {
this.count = count;
}
public virtual LazyGeneratedList<TItem> Clone ()
{
return new LazyGeneratedList<TItem> (count, generator);
}
public IEnumerator<TItem> GetEnumerator ()
{
for (int i = 0; i < Count; i++)

106
src/generator/Templates/CSharp/CSharpClass.cs

@ -19,7 +19,7 @@ namespace Templates { @@ -19,7 +19,7 @@ namespace Templates {
public partial class CSharpClass : Base {
#line 277 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 278 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
private void WriteMethodHeader (Method method, string layoutClass, bool isNonPrimaryOverride, bool @protected)
{
@ -899,12 +899,36 @@ foreach (var npBase in Class.BaseClasses.Skip (1)) { @@ -899,12 +899,36 @@ foreach (var npBase in Class.BaseClasses.Skip (1)) {
#line hidden
#line 203 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(")))\n\t\t\t{\n\t\t\t\tthis.instance = instance;\n\t\t\t}\n\n");
this.Write(")))\n\t\t\t{\n\t\t\t\t");
#line default
#line hidden
#line 208 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 205 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( wrapper ));
#line default
#line hidden
#line 205 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(".impl.TypeInfo.InitNonPrimaryBase (this, instance, typeof (");
#line default
#line hidden
#line 205 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( npBase.Name ));
#line default
#line hidden
#line 205 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write("));\n\t\t\t\tthis.instance = instance;\n\t\t\t}\n\n");
#line default
#line hidden
#line 209 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
PushIndent ("\t\t\t");
foreach (var method in npBase.Methods.Where (m => m.IsVirtual)) {
@ -934,163 +958,163 @@ foreach (var npBase in Class.BaseClasses.Skip (1)) { @@ -934,163 +958,163 @@ foreach (var npBase in Class.BaseClasses.Skip (1)) {
#line default
#line hidden
#line 233 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 234 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write("\t\t}\n\t\tprivate ");
#line default
#line hidden
#line 234 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( wrapper ));
#line default
#line hidden
#line 234 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write("__");
#line default
#line hidden
#line 234 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( npBase.Name ));
#line default
#line hidden
#line 234 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(" __cxxi_");
#line default
#line hidden
#line 234 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( npBase.Name ));
#line default
#line hidden
#line 234 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(";\n\t\tpublic ");
#line default
#line hidden
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 236 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( npBase.Name ));
#line default
#line hidden
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 236 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(" ");
#line default
#line hidden
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 236 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( npBase.Name ));
#line default
#line hidden
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 236 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(" { get { return __cxxi_");
#line default
#line hidden
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 236 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( npBase.Name ));
#line default
#line hidden
#line 235 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 236 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write("; } }\n\t\tpublic static implicit operator ");
#line default
#line hidden
#line 236 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 237 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( npBase.Name ));
#line default
#line hidden
#line 236 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 237 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write("(");
#line default
#line hidden
#line 236 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 237 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( wrapper ));
#line default
#line hidden
#line 236 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 237 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(" subClass)\n\t\t{\n\t\t\treturn subClass.");
#line default
#line hidden
#line 238 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 239 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( npBase.Name ));
#line default
#line hidden
#line 238 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 239 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(";\n\t\t}\n\t\tpublic static explicit operator ");
#line default
#line hidden
#line 240 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 241 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( wrapper ));
#line default
#line hidden
#line 240 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 241 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write("(");
#line default
#line hidden
#line 240 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 241 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( npBase.Name ));
#line default
#line hidden
#line 240 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 241 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(" baseClass)\n\t\t{\n\t\t\tif (baseClass == null) return null;\n\t\t\tvar obj = baseClass as ");
#line default
#line hidden
#line 243 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 244 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( wrapper + "__" + npBase.Name ));
#line default
#line hidden
#line 243 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 244 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(";\n\t\t\tif (obj == null) throw new InvalidCastException ();\n\t\t\treturn obj.instance;\n\t\t}\n\n");
#line default
#line hidden
#line 248 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 249 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
/* Add virtual methods of non-primary bases to this class proper so they can be overridden */
#line default
#line hidden
#line 249 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 250 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
PushIndent ("\t\t");
foreach (var method in npBase.Methods.Where (m => m.IsVirtual)) {
@ -1113,67 +1137,67 @@ if (Class.BaseClasses.Count > 1) { @@ -1113,67 +1137,67 @@ if (Class.BaseClasses.Count > 1) {
#line default
#line hidden
#line 267 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 268 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write("\t\tprivate void __cxxi_InitBases ()\n\t\t{\n");
#line default
#line hidden
#line 269 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 270 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
foreach (var npBase in Class.BaseClasses.Skip (1)) {
#line default
#line hidden
#line 270 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 271 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write("\t\t\t__cxxi_");
#line default
#line hidden
#line 270 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 271 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( npBase.Name ));
#line default
#line hidden
#line 270 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 271 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(" = new ");
#line default
#line hidden
#line 270 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 271 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(this.ToStringHelper.ToStringWithCulture( wrapper + "__" + npBase.Name ));
#line default
#line hidden
#line 270 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 271 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write(" (this);\n");
#line default
#line hidden
#line 271 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 272 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
}
#line default
#line hidden
#line 272 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 273 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write("\t\t}\n");
#line default
#line hidden
#line 273 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 274 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
}
#line default
#line hidden
#line 274 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
#line 275 "/Users/alex/OpenSource/cppinterop/src/generator/Templates/CSharp/CSharpClass.tt"
this.Write("\t}\n}\n\n");
#line default

1
src/generator/Templates/CSharp/CSharpClass.tt

@ -202,6 +202,7 @@ foreach (var npBase in Class.BaseClasses.Skip (1)) { #> @@ -202,6 +202,7 @@ foreach (var npBase in Class.BaseClasses.Skip (1)) { #>
public <#= wrapper + "__" + npBase.Name #> (<#= wrapper #> instance)
: base (<#= wrapper #>.impl.TypeInfo.Cast (instance, typeof (<#= npBase.Name #>)))
{
<#= wrapper #>.impl.TypeInfo.InitNonPrimaryBase (this, instance, typeof (<#= npBase.Name #>));
this.instance = instance;
}

2
tests/InheritanceTests.cs

@ -83,7 +83,7 @@ namespace Tests { @@ -83,7 +83,7 @@ namespace Tests {
Assert.AreEqual (3, cls.Number, "#1");
Assert.AreEqual (3, ((NumberClass)cls).Number, "#2");
Assert.AreEqual (-3, cls.NegativeNumber, "#3");
Assert.AreEqual (5, ((ClassThatOverridesStuff)cls).BaseNumber, "#4");
// Assert.AreEqual (5, ((ClassThatOverridesStuff)cls).BaseNumber, "#4");
}
class ManagedOverride1 : NumberClass {

Loading…
Cancel
Save