Cosmo
Cosmo

Reputation: 148

Generic method with generic class type constraint

I've got an generic interface which has a method with another generic type. The constraint of the method should only allow types which are derived from the generic type of the class. I tried the following code and some other possibilities but none worked so far:

generic <class ObjectType>
public interface class IFoo
{
public:
    generic <class ObjectTypeInherited> where ObjectTypeInherited : ObjectType
    ObjectTypeInherited Test();
};

public ref class Derived : IFoo<System::Object^>
{
public:
generic <class ObjectTypeInherited> where ObjectTypeInherited :     System::Object^
    virtual ObjectTypeInherited Test()
    {
        return ObjectTypeInherited();
    }
};

int main(array<System::String ^> ^args)
{
    auto derived = gcnew Derived();
    auto nullString = derived->Test<System::String^>();
    if (nullString == nullptr)
            System::Console::WriteLine("Got a null string!");
    return 0;
}

I receive the following error with my code:

error C3766: 'Derived' must provide an implementation for the interface 

method 'ObjectTypeInherited IFoo::Test(void)'

If I do change it to:

generic <class ObjectTypeInherited> where ObjectTypeInherited :     System::Object^

I receive a different error:

error C3284: the constraints for generic parameter 'ObjectTypeInherited' of function 'ObjectTypeInherited Derived::Test(void)' must match the constraints for generic parameter 'ObjectTypeInherited' of function 'ObjectTypeInherited IFoo<System::Object ^>::Test(void)'

Does anyone know how to implement that correctly?

edit: The System::Object^ and System::String^ classes are just used as an example here. Normally I do use 2 own ref classes like MyObject and MyDerivedObject : MyObject

Upvotes: 1

Views: 103

Answers (1)

Zverev Evgeniy
Zverev Evgeniy

Reputation: 3719

First of all generic <class ObjectTypeInherited> where ObjectTypeInherited : System::Object^. should be generic <class ObjectTypeInherited> where ObjectTypeInherited : System::Object.

No ^ sign here.

And then the fun begins...

Somehow the C++\CLI compiler does not wish to understand that a class does not have to be generic in order to implement the IFoo<ObjectType>::Test. So I created a workaround for this strange problem.

You can use System::Object instead of MyObjectType. I used the MyObjectType just for versatility.

public ref class MyObjectType
{
};

generic <class ObjectType>
public interface class IFoo
{
public:
    generic <class ObjectTypeInherited> where ObjectTypeInherited : ObjectType
    ObjectTypeInherited Test();
};

//All the magic is here. Somehow the C++\CLI compiler does not wish to
//understand that the DerivedBase class does not have to be generic in order
//to implement the IFoo<ObjectType>::Test. As you can see the Whatever type
//name is never used. Although without it the compiler gives the C3284
//error.
generic <class Whatever>
public ref class DerivedBase : public IFoo<MyObjectType^>
{
public:
    //Why not make use of explicit syntax just because we can.
    generic <class ObjectTypeInherited> where ObjectTypeInherited : MyObjectType
    virtual ObjectTypeInherited Test() = IFoo<MyObjectType^>::Test
    {
        return ObjectTypeInherited();
    }
};

//In order to overcome the fake genericness of DerivedBase I provide
//another successor.
public ref class Derived : public DerivedBase<Object^>
{
};

I do not have any in-depth knowledge of either the Roslyn compiler or C++\CLI but to my mind this seems to be a bug.

If I do the same with the C# managed code, it should look as simple as this. And this is where I would stop myself. Even without any strange compiler behavior managed declarations are easier to maintain on the C# side than C++\CLI.

public class MyObjectType
{
}

public interface IFoo<ObjectType>
{
    ObjectTypeInherited Test<ObjectTypeInherited>() where ObjectTypeInherited : ObjectType;
}

public class Derived : IFoo<MyObjectType>
{
    //Explicit
    ObjectTypeInherited IFoo<MyObjectType>.Test<ObjectTypeInherited>()
    {
        return null;
    }

    //Implicit
    public ObjectTypeInherited Test<ObjectTypeInherited>() where ObjectTypeInherited : MyObjectType
    {
        return null;
    }
}

Upvotes: 1

Related Questions