Bernat
Bernat

Reputation: 439

Instantiated templates and SWIG

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

Answers (1)

softwariness
softwariness

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

Related Questions