What can I do about Covariant return types?

S

Stephen Walch

Error C2392 is hitting me hard!

I have a managed C++ library that implements a bunch of fixed interfaces.
For example, one interface is:


public abstract interface IDbCommand
{
public abstract new System.Data.IDbConnection Connection [ get, set ]
}

and I am forced to implement:

public __gc class MyCommand : public IDbCommand
{
__property IDbConnection* get_Connection();
__property void set_Connection(IDbConnection* value);
}

instead of what I really want, which is:

public __gc class MyCommand : public IDbCommand
{
__property MyConnection* get_Connection();
__property void set_Connection(MyConnection* value);
}

This has been an annoyance in that consumers of my library have to cast
things to the derived types in order to use them. But it is turning into a
MAJOR PROBLEM now that I am doing Visual Studio.NET integration. VS.NET
bases design time behavior (such as code generation) on the types of
components/properties and thier attributes. I have had several cases where
I can not get VS.NET to read my attributes because it thinks it has an
IDbConnection instead of a MyConnection.

Are there any workarounds at all to the covariant return type problem?
Thanks!

-Steve
 
J

Jay B. Harlow [MVP - Outlook]

Stephen,
Unfortunately I'm not sure how to in C++, I wanted to let you know how to in
C# or VB.NET, in case it offers some hints for you to pursue.

In C# you use "Explicit interface member implementations"

http://msdn.microsoft.com/library/d.../en-us/csspec/html/vclrfcsharpspec_13_4_1.asp

In VB.NET you can change the name of the implementing function and make it
private to get the same effect as C#.

I have not delved deep enough into C++ yet to know how here.

Hope this helps
Jay
 
K

Ken Alverson

Jay B. Harlow said:
Stephen,
Unfortunately I'm not sure how to in C++, I wanted to let you know how to in
C# or VB.NET, in case it offers some hints for you to pursue.

In C# you use "Explicit interface member implementations"

Managed C++ does not support explicit interface implementation, and the CLR
does not support covariant return. In C#, you can use explicit interface
implementation to simulate covariant return, but it is a dirty hack, and your
class will become unusable as a base class for Managed C++ users.

Unfortunately there is not a good solution to the OP's question while
remaining in Managed C++. Probably the least painful solution would be to
write the classes that use pseudo-covariant return in C#. Managed C++ should
still be able to consume those classes, even if you can't derive off of them.

Ken
 
J

Jay B. Harlow [MVP - Outlook]

Ken,
your class will become unusable as a base class for
Managed C++ users.

Will Managed C++ have the same issue with VB.NET classes that make the
implementing function private? I know at the IL level the C# & VB.NET
technique are slightly different, while having the same net effect.

Out of curiosity why is it that Managed C++ cannot derive from them? I
realize that the method of the interface itself is now hidden, I would not
expect that to preclude you from inheriting from it.

Thanks
Jay
 
S

Stephen Walch

Can you tell me more about this "dirty hack"? I do not minf making my
classes final; I just want managed applications that use my library to see
the more-derived returned types. Can you give me an example, perhaps based
on my original problem. Deriving from:

public abstract interface IDbCommand
{
public abstract new System.Data.IDbConnection Connection [ get, set ]
}

what I want clients to see:

public __gc class MyCommand : public IDbCommand
{
__property MyConnection* get_Connection();
__property void set_Connection(MyConnection* value);
}

Thanks again.
 
J

Jay B. Harlow [MVP - Outlook]

Stephen,
I believe the "dirty hack" that Ken was referring to is what C# does, which
is specific to C#.

C# hides the implementation of the IDbCommand.Connection method, then you
can define Connection property with the return type you want.

I want to say the closest thing in C++ would be ATL's tear-off interface,
although I never did really figure out how those work. Also I'm not sure you
can do a tear-off interface in Managed C++.

Hope this helps
Jay

Stephen Walch said:
Can you tell me more about this "dirty hack"? I do not minf making my
classes final; I just want managed applications that use my library to see
the more-derived returned types. Can you give me an example, perhaps based
on my original problem. Deriving from:

public abstract interface IDbCommand
{
public abstract new System.Data.IDbConnection Connection [ get, set ]
}

what I want clients to see:

public __gc class MyCommand : public IDbCommand
{
__property MyConnection* get_Connection();
__property void set_Connection(MyConnection* value);
}

Thanks again.

Ken Alverson said:
Managed C++ does not support explicit interface implementation, and the CLR
does not support covariant return. In C#, you can use explicit interface
implementation to simulate covariant return, but it is a dirty hack, and your
class will become unusable as a base class for Managed C++ users.

Unfortunately there is not a good solution to the OP's question while
remaining in Managed C++. Probably the least painful solution would be to
write the classes that use pseudo-covariant return in C#. Managed C++ should
still be able to consume those classes, even if you can't derive off of them.

Ken
 
K

Ken Alverson

Jay B. Harlow said:
Ken,

Will Managed C++ have the same issue with VB.NET classes that make the
implementing function private? I know at the IL level the C# & VB.NET
technique are slightly different, while having the same net effect.

I honestly don't know, but I would suspect the effect would be the same.
Out of curiosity why is it that Managed C++ cannot derive from them? I
realize that the method of the interface itself is now hidden, I would not
expect that to preclude you from inheriting from it.

Well, specifically, you can't override the pseudo-covariant function (which is
generally why you derived in the first place). I believe you could derive
from the pseudo-covariant class if you didn't try to override any
pseudo-covariant functions.

If you try to override the generic version of the function, you'll get this
error:

C2555: 'Derived::Clone': overriding virtual function return type differs and
is not covariant from 'Base::Clone'.

If you try to override the specific version of the function, you'll instead
get this error:

C2392: 'Base __gc *Derived::Clone(void)' : covariant returns types are not
supported in managed types.

I wouldn't be surprised if the next version of MC++ makes one or the other of
these scenarios work, but that's a ways off from now...

Ken
 
K

Ken Alverson

Stephen Walch said:
Can you tell me more about this "dirty hack"? I do not minf making my
classes final; I just want managed applications that use my library to see
the more-derived returned types. Can you give me an example, perhaps based
on my original problem. Deriving from:

public abstract interface IDbCommand
{
public abstract new System.Data.IDbConnection Connection [ get, set ]
}

what I want clients to see:

public __gc class MyCommand : public IDbCommand
{
__property MyConnection* get_Connection();
__property void set_Connection(MyConnection* value);
}

You'll have to write the MyCommand class in C#. Then you can do:

public class MyCommand : IDbCommand {
IDbConnection IDbCommand.Connection {
get {...}
set {...}
}
public MyConnection Connection {
get {...}
set {...}
}
}

This implements the IDbCommand's Connection property, while not exposing it to
users of your class (unless they cast you to your IDbCommand interface).
Then, with the IDbCommand version hidden, you are creating a new property with
your new signature that returns your MyConnection type.

Ken
 
K

Ken Alverson

Jay B. Harlow said:
I'm not sure you
can do a tear-off interface in Managed C++.

You could sort of simulate a tear-off interface in the CLR by defining an
implicit conversion from your class to the interface which will be torn-off,
and when that implicit conversion occurs, you could create an internal object
that implements the interface and return it.

The wrinkle in that plan is the torn-off interface cannot be casted back to
the original object. Whether or not that is sufficient for what you are
attempting to do is another issue.

Ken
 
J

Jay B. Harlow [MVP - Outlook]

Ken,
Well, specifically, you can't override the pseudo-covariant function (which is
generally why you derived in the first place). I believe you could derive
from the pseudo-covariant class if you didn't try to override any
pseudo-covariant functions.
O.K. so if I'm reading you correctly its the 'same' problem, you can not
make the derived functions type more specific then the base functions type.
In other words Covariant return types are just not supported in any of the
..NET languages. Not sure if IL itself supports it or not. I understand there
is a 'redirection' method that Eiffel uses to achieve it.

What I normally do is meet 'half way', the function in the abstract base
class has a more specific type then the interface, while all the functions
in the derived classes have the same return type as the abstract base class
function.

Thanks for the info
Jay
 
K

Ken Alverson

Jay B. Harlow said:
O.K. so if I'm reading you correctly its the 'same' problem, you can not
make the derived functions type more specific then the base functions type.
In other words Covariant return types are just not supported in any of the
.NET languages. Not sure if IL itself supports it or not. I understand there
is a 'redirection' method that Eiffel uses to achieve it.

Almost, but it's worse than that.

If your base class uses explicit interface implementation to simulate
covariant return, you *can not* override that function in Managed C++. I'm
not saying you can't override it covariantly, you can't override at all. If
you try to override it covariantly, you get a message saying covariant return
isn't supported on managed types. If you try to override it non-covariantly,
you get a message complaining that your return type was contravariant from the
return type of your pseudo-covariant parent.

It's a catch-22.

Ken
 
J

Jay B. Harlow [MVP - Outlook]

Ken,
If your base class uses explicit interface implementation to simulate
covariant return, you *can not* override that function in Managed C++.
I get it, it does sound like a bug in the way Managed C++ is interpreting
the base class from C#.

As I don't have a problem from C#, or in VB.NET when I use the VB.NET
method.

I wonder if Whidbey is going to offer anything for this.

Thanks for the info
Jay
 
R

Ronald Laeremans [MSFT]

Yes, C++ in Whidbey is going to support the CLR feature of explicit
interface implementations. Current plan is to offer the fully general
functionality of VB in that respect (but we think with nicer syntax).

Ronald Laeremans
Visual C++ team
 
S

Stephen Walch

It worked! I had to play around with it a bit to find the right C++
syntax. Here is what I ended up with:

private:
IDbConnection* IDbCommand::get_Connection();
void IDbCommand::set_Connection(IDbConnection* value);
public:
[ MyAttributes ]
__property MyConnection* get_Connection();
__property void set_Connection(MyConnection* value);

Note that I had to remove "__property" from the interface implementations. I
could compile with them there, by client C# code complained that they could
not access the property.

Thanks for the support. You saved me from having to implement a complete
set of C# wrapper classes as my public interface!

-Steve

----- Original Message -----
From: "Ken Alverson" <[email protected]>
Newsgroups: microsoft.public.dotnet.languages.vc
Sent: Thursday, October 23, 2003 4:54 PM
Subject: Re: What can I do about Covariant return types?

Stephen Walch said:
Can you tell me more about this "dirty hack"? I do not minf making my
classes final; I just want managed applications that use my library to see
the more-derived returned types. Can you give me an example, perhaps based
on my original problem. Deriving from:

public abstract interface IDbCommand
{
public abstract new System.Data.IDbConnection Connection [ get, set ]
}

what I want clients to see:

public __gc class MyCommand : public IDbCommand
{
__property MyConnection* get_Connection();
__property void set_Connection(MyConnection* value);
}

You'll have to write the MyCommand class in C#. Then you can do:

public class MyCommand : IDbCommand {
IDbConnection IDbCommand.Connection {
get {...}
set {...}
}
public MyConnection Connection {
get {...}
set {...}
}
}

This implements the IDbCommand's Connection property, while not exposing it to
users of your class (unless they cast you to your IDbCommand interface).
Then, with the IDbCommand version hidden, you are creating a new property with
your new signature that returns your MyConnection type.

Ken
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top