Limited Support for Multiple Inheritance in SWIG/Ruby

All,

We'd like to be able to provide support for a kind of "limited" multiple
inheritance in SWIG's Ruby module. It is limited in the sense that, since
Ruby doesn't support MI, we'll obviously never have the case that class "D"
is a direct subclass of more than one base class. However, it might be
useful to provide support for this kind of C++ class structure:

    class Base1 {
    public:
        void base1();
    };

    class Base2 {
    public:
        void base2();
    };

    class Base3 {
    public:
        void base3();
    };

    class Derived : public Base1, public Base2, public Base3 {
    public:
    };

With the limited MI I'm thinking about, all three C++ classes would get
wrapped by SWIG as Ruby classes, with "Derived" being a subclass of "Base1"
only. The bonus, however, would be that you'd also be able to call any of
the methods declared for "Base2" or "Base3" (and their ancestors) on an
instance of "Derived".

The simplest solution so far seems to be to override Derived's
#method_missing method, something like this (pseudocode):

    class Derived
      def method_missing(mth, *args)
        for each base other than Base1
          see if this base can handle it
        end
        super # call Object#method_missing
      end
    end

but I haven't tried to implement this yet.

Can someone else point out the flaws in this, and/or suggest other
possibilities?

Thanks in advance,

Lyle

We'd like to be able to provide support for a kind of "limited" multiple
inheritance in SWIG's Ruby module.

(snip)

The simplest solution so far seems to be to override Derived's
#method_missing method, something like this (pseudocode):

    class Derived
      def method_missing(mth, *args)
        for each base other than Base1
          see if this base can handle it
        end
        super # call Object#method_missing
      end
    end

but I haven't tried to implement this yet.

Can someone else point out the flaws in this, and/or suggest other
possibilities?

Lyle,
         you surely thought of it already---I'm just wondering what the reason was for discarding the possibility. How about mixins?

Bye,
         Luigi

···

At 03:41 PM 3/10/03 +0000, lyle@knology.net wrote:

How about this:

For every C++ class (Base1, Base2, Base3) we create a pair of Ruby objects:

module Base1_internals

Here come all instance methods of Base1

end

class Base1
include Base1_internals

Other Base1 stuff

end

Same for Base2

Same for Base3

Then for Derrived we may have:

module Derrived_internals

Here come all instance methods of Derrived

end

class Derrived
include Derrived_internals
include Base1_internals
include Base2_internals
include Base3_internals

Other Derrived stuff

end

Hope this helps,
Gennady.

···

----- Original Message -----
From: lyle@knology.net
To: “ruby-talk ML” ruby-talk@ruby-lang.org; swig-dev@cs.uchicago.edu
Cc: ruby-talk@ruby-lang.org
Sent: Monday, March 10, 2003 7:41 AM
Subject: Limited Support for Multiple Inheritance in SWIG/Ruby

All,

We’d like to be able to provide support for a kind of “limited” multiple
inheritance in SWIG’s Ruby module. It is limited in the sense that, since
Ruby doesn’t support MI, we’ll obviously never have the case that class
“D”
is a direct subclass of more than one base class. However, it might be
useful to provide support for this kind of C++ class structure:

class Base1 {
public:
    void base1();
};

class Base2 {
public:
    void base2();
};

class Base3 {
public:
    void base3();
};

class Derived : public Base1, public Base2, public Base3 {
public:
};

With the limited MI I’m thinking about, all three C++ classes would get
wrapped by SWIG as Ruby classes, with “Derived” being a subclass of
“Base1”
only. The bonus, however, would be that you’d also be able to call any of
the methods declared for “Base2” or “Base3” (and their ancestors) on an
instance of “Derived”.

The simplest solution so far seems to be to override Derived’s
#method_missing method, something like this (pseudocode):

class Derived
  def method_missing(mth, *args)
    for each base other than Base1
      see if this base can handle it
    end
    super # call Object#method_missing
  end
end

but I haven’t tried to implement this yet.

Can someone else point out the flaws in this, and/or suggest other
possibilities?

Thanks in advance,

Lyle

Mixins are probably the right way to go. As you’ve noticed, you can’t
include a Class, so the solution is to have every Class include a Module
that has the implementation. This gives you the option of using
inheritance to simulate C++ single inheritance and mixins to simulate
C++ multiple inheritance. The biggest disadvantage is that you lose the
"is-a?" relationship between classes, but you do retain duck typing.

There are some issues you should be aware of, though. For one,
diamond-inheritance doesn’t necessarily work the way you might expect:

class Base
module Impl
def foo
puts "Base#foo"
end
end

include Impl
end

class Left
module Impl
include Base::Impl

def foo
  puts "Left#foo"
end

end

include Impl
end

class Right
module Impl
include Base::Impl

def foo
  puts "Right#foo"
end

end

include Impl
end

class Derived
module Impl
include Left::Impl
include Right::Impl

# no foo() here

end

include Impl
end

d = Derived.new
d.foo() #=> Right#foo

In C++, a program like this is ambiguous (do you call Left::foo() or
Right::foo()?). In Ruby, you can never have true diamond inheritance
(it’s all linear, and Left#foo is inaccessible from Derived). Swig
should probably produce a compile-time error if there is ever a function
it needs to wrap that would result in an ambiguous call.

Another issue is with non-virtual inheritance. Virtual inheritance is
easy, because there is only one instance of each base class. Without
thinking about the problem some more, I don’t know where I would begin
wrapping a class that non-virtually inherits multiple times from a
single base class. It probably isn’t something you should run into
often, so it’s not a big loss if swig produces an error here, too.

Paul

lyle@knology.net wrote in message
news:20030310154111.23290.qmail@webmail1.knology.net

All,

We’d like to be able to provide support for a kind of “limited” multiple
inheritance in SWIG’s Ruby module. It is limited in the sense that, since
Ruby doesn’t support MI, we’ll obviously never have the case that class
“D”
is a direct subclass of more than one base class. However, it might be
useful to provide support for this kind of C++ class structure:

[snip]

The simplest solution so far seems to be to override Derived’s
#method_missing method, something like this (pseudocode):

class Derived
  def method_missing(mth, *args)
    for each base other than Base1
      see if this base can handle it
    end
    super # call Object#method_missing
  end
end

but I haven’t tried to implement this yet.

Can someone else point out the flaws in this, and/or suggest other
possibilities?

I realize I am way behind on this discussion (no cc’s to swig, just swig-dev
of which I’m not a member) but I don’t see a definite decision yet. The
news server at work just ceased feeding comp.lang.ruby (grr) so I only catch
up here at home until they get it fixed.

I just want to indicate that I already use method_missing in my SWIG wrapped
classes for other reasons.
If this is the way you go, I suppose I could alias the old method_missing to
something, then have my new one call the old one when my code falls through.
This just seems like it would be a gotcha though, i.e. you redefine
method_missing and your MI stops working.

That is still a possibility; the only potential drawback is that (I think)
you’d have to wrap the non-primary base classes twice, once as a regular
Ruby Class and once as a Module. Using the previous example, we’d wrap
Base1, Base2, Base3 and Derived as classes, and Base2 and Base3 as modules,
hen mix the Base2 and Base3 modules into Derived.

This is assuming that the developer wants to be able to instantiate Base2
and Base3 objects directly, use them as bases for other classes, and
anything else that you’d want to do with a class. If that were not the
case, I guess we could introduce a special directive that says “Wrap this
class as a Ruby module only, and not as a class”.

What do you think?

I hope I’m not missing portions of this thread… I view it through
comp.lang.ruby and the threading is always screwy–(is that due to mail
clients through the ruby-talk mailing list?). In this case it may be that
half of this is on swig-dev and not here :slight_smile:

This is certainly better than my original braindead suggestion many months
ago which is to wrap the Base2 etc classes for each subclass who has it as a
secondary superclass. I’d certainly be happy with the above. I have a need
for both situations, i.e. when a class should only be wrapped as a module
and when it should be both.

Thanks for working on this one Lyle. Sorry if I’m behind the 8-ball here
but this is my pet wish for SWIG Ruby.

···


Brett Williams

Hi,

···

At Tue, 11 Mar 2003 01:26:58 +0900, Gennady wrote:

For every C++ class (Base1, Base2, Base3) we create a pair of Ruby objects:

module Base1_internals

Here come all instance methods of Base1

end

It might be better to be nested (ex. Base1::MixIn).


Nobu Nakada

Gennady wrote:

How about this:

Actually, I just recalled that a Class is-a Module, so I think Luigi’s
original suggestion was correct. We should be able to mix Base2 and
Base3 into Derived directly, e.g.

 class Base1
   def base1; end
 end

 class Base2
   def base2; end
 end

 class Base3
   def base3; end
 end

 class Derived < Base1
   include Base2
   include Base3
 end

and since Object#is_a? treats mixed-in modules as base classes (IIRC)
the follow relationships should also hold:

 Derived.new.is_a? Base1
 Derived.new.is_a? Base2
 Derived.new.is_a? Base3

Lyle