Reputation: 439
I have the following problem which I don't know how to solve. I want to create a Java wrapper using SWIG for these two classes which are in the same file:
utilities.h:
template<class T>
class EncoderInterface
{
public:
virtual ~EncoderInterface()
{
}
virtual const cdap_rib::SerializedObject* encode(const T &object) = 0;
virtual T* decode(
const cdap_rib::SerializedObject &serialized_object) const = 0;
};
class IntEncoder : public rib::EncoderInterface<int>
{
public:
const cdap_rib::SerializedObject* encode(const int &object);
int* decode(const cdap_rib::SerializedObject &serialized_object) const;
};
Then I do the usual swig stuff in .i:
%{
#include "utilities.h"
%}
%include "utilities.h"
And it says:
Warning 401: Nothing known about base class 'EncoderInterface< int >'. Ignored.
Warning 401: Maybe you forgot to instantiate 'EncoderInterface< int >' using %template.
If I try to use the %template thing like this:
%template(IntEncoder) EncoderInterface<int>;
Warning 302: Identifier 'IntEncoder' redefined (ignored) (Renamed from 'EncoderInterface< int >'),
utilities.h:302: Warning 302: previous definition of 'IntEncoder'.
IntEncoder
has code in utilities.cc and I want to let the user of the utilities create new template instantiations or use the given one if he wants. I don't really want to change the name of the IntEncoder
so any user of the library (coming from C++ or from Java) will use the same names.
I have read something about splitting files (keeping EncoderInterface
template in one file and the instantiation in another one) is this the only solution to this problem? I don't want to create new files if I can avoid it.
Upvotes: 4
Views: 869
Reputation: 4052
Warning 401: Nothing known about base class 'EncoderInterface< int >'. Ignored. Warning 401: Maybe you forgot to instantiate 'EncoderInterface< int >' using %template.
EncoderInterface<int>
is the base class of IntEncoder
. SWIG is trying to wrap up your IntEncoder
class for Java, but it hasn't got a wrapper for this base class, because it only wraps instantiations of templates.
Until SWIG is given a name for the template base class, it doesn't know how to make the Java wrapper for IntEncoder
derive from a wrapper around EncoderInterface<int>
, which is why it's warning.
If I try to use the %template thing like this:
%template(IntEncoder) EncoderInterface<int>; Warning 302: Identifier 'IntEncoder' redefined (ignored) (Renamed from 'EncoderInterface< int >'), utilities.h:302: Warning 302: previous definition of 'IntEncoder'.
This is the right idea, but you've now told SWIG to call the wrapper around EncoderInterface<int>
the same thing as your other class, IntEncoder
.
You need to tell SWIG to call the wrapper around EncoderInterface<int>
something else, e.g. IntEncoderInterface
:
%template(IntEncoderInterface) EncoderInterface<int>;
You can call it what you like, so long as it's a name you're happy to have as the name of a class in your Java API.
You can only wrap-up instantiations of C++ templates with SWIG, you cannot, for example, wrap them up as Java generic classes.
If you need instantiations of EncoderInterface<T>
for different types T
as well, you will have to add a %template
declaration for each of those types, telling SWIG to wrap-up each of them up with a different class name in Java.
If an automatically generated Java wrapper for EncoderInterface
that uses Java generics was what you were really after, then you're out of luck. If you need your Java API to be consistent with your C++ API you will have to explore other API styles (I can see that this is some kind of serialization interface, but not knowing the details of the kind of types you want to serialize with your API, and what functionality to support that is in the base class, I can't suggest any specific alternative strategies though).
I have read something about splitting files (keeping
EncoderInterface
template in one file and the instantiation in another one) is this the only solution to this problem? I don't want to create new files if I can avoid it.
What you read may have been suggesting that in order to hide the template from SWIG, so it wouldn't see it or try to wrap it. That is an option, if you don't want to wrap the base class at all.
You don't need to put it in another file though, you can hide it using the preprocessor (note that IntEncoder
's inheritance from EncoderInterface<int>
is hidden from SWIG here, in addition to EncoderInterface
itself):
#ifndef SWIG
template<class T>
class EncoderInterface
{
public:
virtual ~EncoderInterface()
{
}
virtual const cdap_rib::SerializedObject* encode(const T &object) = 0;
virtual T* decode(
const cdap_rib::SerializedObject &serialized_object) const = 0;
};
#endif
class IntEncoder
#ifndef SWIG
: public EncoderInterface<int>
#endif
{
public:
const cdap_rib::SerializedObject* encode(const int &object);
int* decode(const cdap_rib::SerializedObject &serialized_object) const;
};
Note also that as-is, the int*
return type from your decode
method will get wrapped as the unfriendly SWIGTYPE_p_int
, which may not be what you want. Perhaps decode
can return by value (int
) instead, which will get wrapped straightforwardly as int
in Java.
Upvotes: 3