Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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);
}
like image 245
zezba9000 Avatar asked Jan 15 '19 07:01

zezba9000


2 Answers

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

See

  • Tutorialspoint
  • Is it possible to have different return types for a overloaded method?
  • Function overloading by return type?

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);
}
like image 93
fstam Avatar answered Oct 18 '22 20:10

fstam


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.

like image 23
Victor Padureanu Avatar answered Oct 18 '22 19:10

Victor Padureanu