Evan Altair
Evan Altair

Reputation: 1

Using SWIG to wrap C++ pure virtual class as director to Java

Brief

I have a C++ library that provides sort of Sender and Listener functionality.

The Listener class is pure virtual class that is intended to be extended by the client.

The Sender class accepts shared pointer of Listener instance instead of raw pointer to avoid dangling pointers.

I try to convert it into Java wrapper with SWIG 4.3, using shared_ptr feature to wrap shared pointer type and director feature to allow extension of Listener class in Java.

The conversion succeeded. But when I use it in Java program, I get a compile error saying that there is no default constructor of Listener class but an explicit constructor with parameters.

How could I avoid this explicit constructor in SWIG conversion? Or how should I extend the virtual class in Java if explicit constructor is unavoidable?


Details

The C++ definition looks like this:

class MyListener
{
public:
    inline virtual ~MyListener() = default;
    virtual void notify(const std::string &event) = 0;
    virtual void disposed(bool expected) = 0;
};

Class MySender
{
public:
    void addListener(std::shared_ptr<MyListener> listener);
    void removeListener(std::shared_ptr<MyListener> listener);
};

In SWIG interface definition, I use shared_ptr feature to automatically wrap C++ shared_ptr type to Object in Java. And I use director feature to support extending MyListener class in Java side.

The SWIG interface file looks like this:

%include <std_shared_ptr.i>

%module(directors="1") MyWrapper

%{
#include "MyClasses.h"
%}

%shared_ptr(MyListener);
%feature("director") MyListener;

%include "MyClasses.h"

The intended usage in Java program would look like:

public class Main {

    static {
        System.loadLibrary("MyWrapper");
    }
    
    public class DerivedListener extends MyListener{
        @Override
        public void notify(String event) {
            // do something
        }
    
        @Override
        public void disposed(boolean expected) {
            // do something
        }
    }

    public static void main(String[] args) {
        MySender sender = new MySender();
        DerivedListener listener = new DerivedListener();
        sender.addListener(listener);
        sender.removeListener(listener);
        return;
    }
}

However, I get the Java compile error on DerivedListener that says: Implicit super constructor MyListener() is undefined for default constructor. Must define an explicit constructor

When I look at the generated Java class of MyListener.java, I notice that there is indeed no default constructor, instead, there is only an explicit constructor:

protected MyListener(long cPtr, boolean cMemoryOwn) {
    swigCMemOwn = cMemoryOwn;
    swigCPtr = cPtr;
}

This error blocks the client from my intended usage of these 2 classes. How could I avoid the explicit constructor of MyListener and still passing MyListener as shared_ptr in function parameters?

If the explicit constructor is indeed unavoidable, what value shall I pass to these 2 parameters: long cPtr, boolean cMemoryOwn then?


Append

In the SWIG documentation I read that:

Default constructors are not generated for classes with pure virtual methods or for classes that inherit from an abstract class, but don't provide definitions for all of the pure methods. https://www.swig.org/Doc4.3/SWIGPlus.html#SWIGPlus_nn8

Even if I explicitly declare a default constructor in MyListener class: inline MyListener(){};, it would still not appear in generated Java class.

Then how should the client extend such virtual class with generated Java wrapper? The example in SWIG documentation does not take arguments for constructor: https://www.swig.org/Doc4.3/SWIGPlus.html#SWIGPlus_directors_for_function_pointers

Upvotes: 0

Views: 35

Answers (0)

Related Questions