C++ multiple interfaces that only differ in return type?

Viewed 1231

Doing an experiment of translating .NET IL to C++ in a human readable fashion.

Here is the issue: C# allows you to resolve multiple interfaces with the same method name that only differ in return type. C++ doesn't seem to support this however making resolving two interfaces impossible using the vTable (or am i wrong?).

I've found a way to replicate the C# approach in C++ using templates but am wondering if there is a way that doesn't require templates that solves the same issue? Templates are verbose and I'd prefer not using them for every interface type if possible.

Here is the C++ version.

template<typename T>
class IMyInterface
{
    public: short (T::*Foo_IMyInterface)() = 0;
};

template<typename T>
class IMyInterface2
{
    public: int (T::*Foo_IMyInterface2)() = 0;
};

class MyClass : public IMyInterface<MyClass>, public IMyInterface2<MyClass>
{
    public: MyClass()
    {
        Foo_IMyInterface = &MyClass::Foo;
        Foo_IMyInterface2 = &MyClass::IMyInterface2_Foo;
    }

    public: virtual short Foo()
    {
        return 1;
    }

    private: int IMyInterface2_Foo()
    {
        return 1;
    }
};

class MyClass2 : public MyClass
{
    public: virtual short Foo() override
    {
        return 2;
    }
};

void InvokeFoo(IMyInterface<MyClass>* k)
{
    (((MyClass*)k)->*k->Foo_IMyInterface)();
}

void main()
{
    auto a = new MyClass2();
    InvokeFoo(a);
}

Here is the C# reference source the C++ one is based on.

interface IMyInterface
{
    short Foo();
}

interface IMyInterface2
{
    int Foo();
}

class MyClass : IMyInterface, IMyInterface2
{
    public virtual short Foo()
    {
        return 1;
    }

    int IMyInterface2.Foo()
    {
        return 1;
    }
}

class MyClass2 : MyClass
{
    public override short Foo()
    {
        return 2;
    }
}

namespace CSTest
{
    class Program
    {
        static void InvokeFoo(IMyInterface k)
        {
            k.Foo();
        }

        static void Main(string[] args)
        {
            var a = new MyClass2();
            InvokeFoo(a);
        }
    }
}

This C++ method doesn't work below but wish it did (its more what I'm going for).

class IMyInterface
{
    public: virtual short Foo() = 0;
};

class IMyInterface2
{
    public: virtual int Foo() = 0;
};

class MyClass : public IMyInterface, public IMyInterface2
{
    public: virtual short Foo()
    {
        return 1;
    }

    private: int IMyInterface2::Foo()// compiler error
    {
        return 1;
    }
};

class MyClass2 : public MyClass
{
    public: virtual short Foo() override
    {
        return 2;
    }
};

void InvokeFoo(IMyInterface* k)
{
    k->Foo();
}

void main()
{
    auto a = new MyClass2();
    InvokeFoo(a);
}
4 Answers

The problem is that you can't overload based on return type alone.

See

The last stackoverflow thread points out overloading is possible using operators.

struct func {
    operator string() { return "1"; }
    operator int() { return 2; }
};

int main() {
    int x = func(); // calls int version
    string y = func(); // calls string version
    double d = func(); // calls int version
    cout << func() << endl; // calls int version
    func(); // calls neither
}

You can't name them though, this would quickly turn into a mess to work with.

The argument list has to change. Victor Padureau suggested to use void return types and pass the type of value as reference to be set to a value in the method, that will work. You can also change the method name for the different types.

class my_interface
{
public: 
    virtual short foo_short() = 0;
};

class my_interface2
{
public: 
    virtual int foo_int() = 0;
};

class my_class : public my_interface, public my_interface2
{
public: 
    short foo_short() override
    {
        return 1;
    }

    int foo_int() override
    {
        return 1;
    }
};

class my_class2 : public my_class
{
public: 
    virtual short foo_short() override
    {
        return 2;
    }
};

void InvokeFoo(my_interface* k)
{
    short result = k->foo_short();
    std::cout << result << std::endl;
}

void main()
{
    auto a = new my_class2();
    InvokeFoo(a);
}

I have a solution that might work. It's not perfect, but it is a way to workaround the issue if you are porting.

Instead of calling int foo() you can call void foo(int& out) that way you are putting the return type in the calling part of the function.

Best is: not having the same function names in both interfaces, making return values output parameters certainly is an interesting alternative. For completeness: let's assume we cannot change the base classes' interfaces, perhaps because of coming from different libraries – and we still need to override.

Well, first: Inheriting both interfaces at all might be a questionable design, it is very likely that you'll be violating the single responsibility principle.

Now let's assume we need to anyway. A possible workaround then (it doesn't match my own definition of 'elegance' – but at least...) are intermediate classes:

class BaseA { virtual ~BaseA(); virtual void foo(); };
class BaseB { virtual ~BaseB(); virtual int foo(); };

class DerivedA : public BaseA { void foo() override; };
class DerivedB : public BaseB { int foo() override; };

class Derived : public DerivedA, public DerivedB { };

Now Derived can serve as both BaseA and BaseB and still has overridden both (in this case) variants of foo, albeit indirectly.

If you intend to further inherit from Derived, still allowing to override either variant of foo function, then each override in DerivedA and DerivedB would itself call a (new) virtual function fooA and fooB respectively:

class DerivedA : public BaseA
{
public:
    void foo() final // better than override:
                     // prevents breaking the pattern again
    { fooA() };
protected:
    virtual void fooA() = 0;
};
class DerivedB : public BaseB
{
public:
    int foo() final { return fooB() };
protected:
    virtual int fooB() = 0;
};

This same trick allows the overrides to use members of both bases and is certainly the cleanest approach to get the matter done, however, it comes with some tradeoff as virtual function calls don't come for free (vtable lookup, then actual function call) – so if performance is more important (think well if it really is!), you might just implement the overrides in DerivedA and DerivedB directly.

If you want C++ code that would essentially be similar to your C# example, I would recommend you to essentially add an extra intermediate class MyClass_IMyInterface2 with the sole purpose to rename methods that are implemented explicitly in C# code.

Thus you would have something like:

class IMyInterface2
{
public: 
    virtual int Foo() = 0;
};

class MyClass_IMyInterface2 : public IMyInterface2
{
protected:
    int Foo() final { return IMyInterface2_Foo(); }
    virtual int IMyInterface2_Foo() = 0; 
};

class MyClass : public IMyInterface, public MyClass_IMyInterface2...

By the way, you only need to add the extra class if you have conflicts and only need to do the renaming if so.

Assuming that the purpose would be to mimic C# code, I would probably do it each time an interface method is explicitly implemented.

You could also adjust the visibility of the members as desired.

Related