Very strange error in managed code
Since we don't have a managed C++ forum I thought I would put this here:
Take the following code that is in one DLL:
Code:
#pragma once
using namespace System;
#include "NativeClass.h"
namespace Managed
{
public interface class ITest
{
public:
virtual void Foo();
virtual void SetNativeClass(NativeClass *pClass);
};
public ref class Test abstract : public ITest
{
public:
Test() : m_pNativeClass(0) { }
virtual void Foo() { }
virtual void SetNativeClass(NativeClass *pClass) = 0;
public:
NativeClass *m_pNativeClass;
};
}
Code:
#pragma once
class NativeClass
{
public:
NativeClass() { }
};
And the following code in another DLL:
Code:
// ManagedObject.h
#pragma once
using namespace System;
#include "NativeClass.h"
namespace Managed
{
public ref class ManagedObject : public Test
{
public:
ManagedObject() { }
virtual void SetNativeClass(NativeClass *pClass)
{
m_pNativeClass = pClass;
}
};
}
And the following errors from compiling the second DLL:
Quote:
1>ManagedObject.cpp
1>e:\dev\managedtest\managedobject\ManagedObject.h (11) : warning C4570: 'Managed::ManagedObject' : is not explicitly declared as abstract but has abstract functions
1> 'void Managed::Test::SetNativeClass(NativeClass *)' : is abstract
1> e:\dev\managedtest\managedtest\debug\managedtest.d ll : see declaration of 'Managed::Test::SetNativeClass'
1>e:\dev\managedtest\managedobject\ManagedObject.h (18) : error C2248: 'Managed::Test::m_pNativeClass' : cannot access private member declared in class 'Managed::Test'
1> e:\dev\managedtest\managedobject\ManagedObject.h(1 2) : see declaration of 'Managed::Test::m_pNativeClass'
1> e:\dev\managedtest\managedtest\debug\managedtest.d ll : see declaration of 'Managed::Test'
Let's take the errors one by one here:
Quote:
1>e:\dev\managedtest\managedobject\ManagedObject.h (11) : warning C4570: 'Managed::ManagedObject' : is not explicitly declared as abstract but has abstract functions
This makes no sense at all. Clearly I have implemented the abstract function in ManagedObject. This means that ManagedObject is NOT abstract. However the class it derives from...namely Test IS abstract b/c it has at least one pure virtual function. This error is bogus from what I can see.
Quote:
1> 'void Managed::Test::SetNativeClass(NativeClass *)' : is abstract
1> e:\dev\managedtest\managedtest\debug\managedtest.d ll : see declaration of 'Managed::Test::SetNativeClass'
First of all SetNativeClass() is abstract in Test() but not abstract in ManagedObject. When you look at the declaration of SetNativeClass() in Test you will see this. Another bogus error.
Quote:
1>e:\dev\managedtest\managedobject\ManagedObject.h (18) : error C2248: 'Managed::Test::m_pNativeClass' : cannot access private member declared in class 'Managed::Test'
1> e:\dev\managedtest\managedobject\ManagedObject.h(1 2) : see declaration of 'Managed::Test::m_pNativeClass'
1> e:\dev\managedtest\managedtest\debug\managedtest.d ll : see declaration of 'Managed::Test'
I beg your pardon? Since when is m_pNativeClass inaccessible to a class that derives from a base that gives protected access to it? It's not the old problem of passing in an object of type Test * and then trying to set m_pNativeClass from that pointer. That is clearly wrong since protected applies to your own instance and not the instance of another class of the same type. But even if I set m_pNativeClass to public it will still say in the second DLL that it is private to Test even though I'm deriving from Test. Another bogus error.
This occurs in MSVC 2005 and MSVC 2008. What is going on here?
My .NET book specifically states that protected access means that members of the class can access the member or data and derived classes in other assemblies can also access the member or the data.
However this does not seem to be true according to this error. The really really bad part about this is if you change the data type of m_pNativeClass to a POD type it works like a gem.
Now if you don't implement SetNativeClass() in ManagedObject the compiler will say that you must implement the function Managed::ITest::SetNativeClass(NativeClass *). But when I have clearly implemented it, it acts as if I have not. In fact it can see Managed::Test b/c the inheritance works fine.
If you remove the offending pointer and get everything compiling and put some assignment code in Test::SetNativeClass() and ManagedObject::SetNativeClass() so you can set breakpoints more odd things happen. So let's say you put int x= 5; in Test::SetNativeClass() and int x = 5; int ManagedObject::SetNativeClass(). The following code will surprise you.
Code:
ManagedObject ^object = gcnew ManagedObject();
object.SetNativeClass(0);
What is weird here is that the constructor for ManagedObject does get called but SetNativeClass() will call the BASE version instead of the derived version. How in the world can you construct a derived object, verify it constructed via breakpoints in the derived constructor, and yet when the call is made hit the breakpoint in the Base version of SetNativeClass()? Very odd. Whatever happened to polymorphism?