If I compile and run the following:
using namespace System;
ref class C1
{
public:
C1()
{
Console::WriteLine(L"Creating C1");
}
protected:
~C1()
{
Console::WriteLine(L"Destroying C1");
}
};
int main(array<System::String ^> ^args)
{
C1^ c1 = gcnew C1();
delete c1;
return 0;
}
...the code compiles without an error and runs giving me this:
Creating C1
Destroying C1
Press any key to continue . . .
If I do the same in C++ I get an error along the following lines:
1>ProtectedDestructor.cpp(45): error C2248: 'C1::~C1' : cannot access protected member declared in class 'C1'
1> ProtectedDestructor.cpp(35) : compiler has generated 'C1::~C1' here
1> ProtectedDestructor.cpp(23) : see declaration of 'C1'
...so why is it valid in CLI?
This is a leaky abstraction problem. C++/CLI has several of them, we already went through the const keyword problem. Much the same here, the runtime does not have any notion of a destructor, only the finalizer is real. So it has to be faked. It was pretty important to create that illusion, the RAII pattern in native C++ is holy.
It is faked by bolting the notion of a destructor on top of the IDisposable interface. The one that makes deterministic destruction work in .NET. Very common, the using keyword in the C# language invokes it for example. No such keyword in C++/CLI, you use the delete operator. Just like you would in native C++. And the compiler helps, automatically emitting the destructor calls when you use stack semantics. Just like a native C++ compiler does. Rescuing RAII.
Decent abstraction, but yes, it leaks. Problem is that an interface method is always public. It is technically possible to make it private with explicit interface implementation although it is but a stopgap:
public ref class Foo : IDisposable {
protected:
//~Foo() {}
virtual void Dispose() = IDisposable::Dispose {}
};
Produces a very impressive error list when you try this, the compiler fights back as hard as it can :). C2605 is the only relevant one: "'Dispose': this method is reserved within a managed class". It can't maintain the illusion when you do this.
Long story short, the IDisposable::Dispose() method implementation is always public, regardless of the accessibility of the destructor. The delete operator invokes it. No workaround for this.
In addition to Hans's detailed answer that delete on a C++/CLI object is actually activation of the IDisposable interface, and interface inheritance is always public1, it may be fruitful to ask
How does the protected destructor get called, then?
The compiler-generated Dispose methods call the user-defined destructor. Because this Dispose method is a member of the class, it has access to protected and private class members, such as the destructor.
(In native C++, the compiler isn't subject to accessibility rules, since it is the one enforcing them. In .NET, the IL verifier enforces them.)
1 Actually, his explanation centers on the fact that the compiler doesn't allow explicit implementation of IDisposable::Dispose(), in which case it could be a private member. But that's completely irrelevant. virtual members can be reached through the declaring type. And delete doesn't call object->Dispose(), it calls safe_cast<IDisposable^>(object)->Dispose().
Related
Is there a way to not have to repeatidly write this(parent class args) {super(parent class args);} when the arguments are exactly the same?
The code:
class Parent {
string name;
this(string name) {
this.name = name;
}
}
class Child : Parent {
}
unittest {
auto child = new Child("a name");
assert(child.name == "a name");
}
https://run.dlang.io/is/YnnezI
Gives me the compilation error:
Error: class onlineapp.Child cannot implicitly generate a default constructor when base class onlineapp.Parent is missing a default constructor
Java and C# don't inherit constructors either (unless that's changed in the last few years - I don't think C++ allowed it either until c++11), and D follows the same reasoning so you can read more by looking up things about them.
Basically though the reason is that subclasses must have their own unique state - at very least stuff like the vtable even if you don't declare any of your own variables - and thus a unique constructor is required. Otherwise you can have uninitialized members.
And if inheritance went the whole way, since Object has a this(), new AnyClass(); would compile and lead to a lot of invalid objects. (In regular D, if you declare any ctor with arguments, it disables the automatically-generated zero arg one.)
Now, D could in theory do what C++ does and auto-generate other args too... it just doesn't. Probably mostly because that is a relatively new idea in C++ and D's class system is primarily based on Java's older system.
But all that said, let me show you a trick:
this(Args...)(auto ref Args args) { super(args); }
stick that in your subclass and you basically inherit all the parent's constructors in one go. If the super doesn't compile for the given args, neither will this, so it doesn't add random things either. You can overload that with more specific versions if needed too, so it is a reasonable little substitute for a built-in language feature.
I've got a C++/CLI layer that I've been using successfully for a long time. But I just discovered something that makes me think I need to relearn some stuff.
When my C++/CLI functions receive an instance of any managed class, they use the "hat" operator ('^') and when they receive an instance of a managed struct, they do not. I thought this was how I was supposed to write it.
To illustrate as blandly as I can
using Point = System::Windows::Point;
public ref class CppCliClass
{
String^ ReturnText(String^ text) { return text; } // Hat operator for class
Point ReturnStruct(Point pt) { return pt; } // No hat operator for struct
};
I thought this was required. It certainly works. But just today I discovered that CancellationToken is a struct, not a class. My code accepts it with a hat. I thought it was a class when I wrote it. And this code works just fine. My cancellations are honored in the C++/CLI layer.
void DoSomethingWithCancellation(CancellationToken^ token)
{
// Code that uses the token. It works just fine
}
So apparently I can choose either method.
But then what is the difference between passing in a struct by value (as I've done with every other struct type I use, like Point) and by reference (as I'm doing with CancellationToken?). Is there a difference?
^ for reference types and without for value types matches C#, but C++/CLI does give you more flexibility:
Reference type without ^ is called "stack semantics" and automatically tries to call IDisposable::Dispose on the object at the end of the variable's lifetime. It's like a C# using block, except more user-friendly. In particular:
The syntax can be used whether the type implements IDisposable or not. In C#, you can only write a using block if the type can be proved, at compile time, to implement IDisposable. C++/CLI scoped resource management works fine in generic and polymorphic cases, where some of the objects do and some do not implement IDisposable.
The syntax can be used for class members, and automatically implements IDisposable on the containing class. C# using blocks only work on local scopes.
Value types used with ^ are boxed, but with the exact type tracked statically. You'll get errors if a boxed value of a different type is passed in.
After reading this post I think I mostly understand LSP and most of the examples, but I can’t say I’m 100% certain from my experience of many examples of inheritance, as it seems that many examples do violate LSP and it seems difficult not to when overriding behaviour.
For instance, consider the following simple demonstration of inheritance, taken from Head First Object Oriented Analysis & Design. Aren't they violating LSP with the Jet child class?
public class Airplane {
private int speed;
public void setSpeed(int speed) {
this.speed = speed;
}
public int getSpeed() {
return speed;
}
}
public class Jet extends Airplane {
private static final int MULTIPLIER=2;
/**
* The subclass can change behaviour of its superclass, as well as call the
* superclass's methods. This is called overriding the superclass's behaviour
*/
public void set setSpeed(int speed) {
super.setSpeed(speed * MULTIPLIER);
}
public void accelerate() {
super.setSpeed(getSpeed() * 2);
}
}
A client using a reference to an instance of base class Airplane might be surprised, after setting the speed, to find it is twice as fast as expected after being passed an instance of a Jet object. Isn't Jet changing the post-conditions for the setSpeed() method and thus violating LSP?
E.g.
void takeAirplane(Airplane airplane) {
airplane.setSpeed(10);
assert airplane.getSpeed()==10;
}
This will clearly fail if takeAirplane is passed a reference to a Jet object.
It seems to me that it will be difficult not to violate LSP when “overriding a superclass’s behaviour”, yet this is one of the main/desirable features of inheritance!
Can someone explain or help clarify this? Am I missing something?
According to Wikipedia
[The Liskov substitution principle] states that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.).
In the case of the Jet, it's speed being twice as fast is a violation of the LSP - it fails the postcondition that setSpeed(getSpeed(x))==x
The Liskov Substitution Principle says that it's ok to modify the behaviour of in a derived class, provided the correctness of the program does not change. It imposes restrictions on the changes you make in the derived classes.
It is certainly not for good OOP design - as the need for common behavior of all instances of a derived class is quite valid conceptually. Moreover, it would make for so much cleaner code if one could just say Data.parse(file), have the common parse() code in the base class and let overriding do its magic than having to implement mostly similar code in all data subtypes and be careful to call DataSybtype.parse(file) - ugly ugly ugly
So there must be a reason - like Performance ?
As a bonus - are there OOP languages that do allow this ?
Java-specific arguments are welcome as that's what I am used to - but I believe the answer is language agnostic.
EDIT : one could ideally :
<T> void method(Iface<? extends T> ifaceImpl){
T.staticMeth(); // here the right override would be called
}
This will also fail due to erasure (in java at least) - if erasure is at work one needs (would need) to actually pass the class :
<T, K extends T> void method(Iface<K> ifaceImpl, Class<K> cls){
cls.staticMeth(); // compile error
}
Does it make sense ? Are there languages doing this already ? Is there a workaround apart from reflection ?
Speaking to C++
class Foo {
public:
static void staticFn(int i);
virtual void virtFn(int i);
};
The virtual function is a member function - that is, it is called with a this pointer from which to look up the vtable and find the correct function to call.
The static function, explicitly, does not operate on a member, so there is no this object from which to look up the vtable.
When you invoke a static member function as above, you are explicitly providing a fixed, static, function pointer.
foo->virtFn(1);
expands out to something vaguely like
foo->_vtable[0](foo, 1);
while
foo->staticFn(1);
expands to a simple function call
Foo##staticFn(1);
The whole point of "static" is that it is object-independent. Thus it would be impossible to virtualize.
I have two c++/cli dlls (i.e. compiled with /clr) where A.dll references B.dll. In assembly B, I have a method, GetMgdClassB, I'd like to call from assembly A. Here is the code in assembly B (B.cpp):
namespace B
{
public class NativeClassB
{
public:
NativeClassB();
// ...
};
public ref class MgdClassB
{
public:
static MgdClassB ^ GetMgdClassB(const std::vector<NativeClassB *> & vecNativeBs)
{
// ...
vecNativeBs;
return gcnew MgdClassB();
}
};
}
Notice that the method GetMgdClassB takes a std::vector. In assembly A, I attempt to call this method with the following code (A.cpp):
namespace B
{
class NativeClassB;
}
#pragma make_public(std::vector<B::NativeClassB *>)
namespace A
{
void Foo()
{
std::vector<B::NativeClassB *> vecNativeBs;
B::MgdClassB::GetMgdClassB(vecNativeBs);
}
}
When I compile A.cpp, I get the following error:
error C2158: 'std::vector<_Ty>' : #pragma make_public directive is currently supported for native non-template types only
the reason I wanted to add this pragma is because native types are private to the assembly by default. If I remove the pragma I get the following error (as expected):
error C3767: 'B::MgdClassB::GetMgdClassB': candidate function(s) not accessible
since the template instantiation type std::vector<B::NativeClassB *> is private to the assembly.
Attempted Solutions
1. Use void *, break type safety:
Change the method, GetMgdClassB to take a void * and pass the address of the std::vector<NativeClassB *> to the method. In GetMgdClassB. I can then static_cast the passed in void * to std::vector<NativeClassB *> *. This, of course, works, but breaks type safety.
2. Create a Managed wrapper for NativeClassB, pass a managed container
Create a managed class, say ref class NativeClassBWrapper who's sole purpose is to hang on to a reference to the native NativeClassB. Change GetMgdClassB to take a managed container of NativeClassBWrappers (e.g. List<NativeClassBWrapper ^> ^). This has the downside of having to create and populate a new managed container prior to calling GetMgdClassB, and then within managed class B, I have to repackage it into the the native container std::vector<NativeClassB *> (since the code in B deals with this type.
Currently, I'm leaning toward going with Solution #1, since (a) it doesn't introduce any performance concerns and (b) I'll only be doing this in a few cases. I don't like losing the type safety, but it seems justifiable given the current deficiency in the compiler's ability to make native template instantiation types visible.
Question:
Are there better work arounds?
Related Question:
C++ CLI error C3767: candidate function(s) not accessible
I'm not aware of any way to export that type. If you have to have that function signature, I would lean in the direction of using a mix of managed and native exports (managed functions using native types can't be consumed by other languages anyway), and maybe use delay loading when calling the native exports so you have a chance to trap errors finding the assembly in the usual .NET way.
But your particular function may be problematic since it uses both managed types and complex native types in the signature.
In general, the best practice is to not pass native C++ classes across DLL boundaries at all, since this sets you up for One Definition Rule violations.
For this particular situation, my suggestion is to make an wrapper that implements ICollection. That cures the problem just like your solution #2, without ever having to actually copy all the elements into a new data structure.
I received a solution from Mike Danes on another forum:
http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/b43cca63-b0bf-451e-b8fe-74e9c618b8c4/
Basically, the solution is to create a native wrapper (call it VectorOfNativeB) in assembly B that holds on to a pointer or reference to the std::vector. Export VectorOfNativeB and make it publicly visible. Change method GetMgdClassB to take a pointer or reference VectorOfNativeB.
[posted this here for future reference and to see if anyone here has any comments about this solution].