Search code examples
pythonc++swig

How to automatically remove the namespace attached to a friend function defined within a C++ class, when using SWIG?


I am trying to wrap a C++ class designed by a colleague of mine, using SWIG, for scripting programming in Python. These classes are defined as a bunch of source files which I have, but do not want to modify.

All the classes are defined within a given namespace and include friend functions, for global treatment on its objects without having to use methods on any of the objects.

SWIG complains about the fact that the automatic identifier generated for such functions cannot be wrapped automatically (Warning 503: Can't wrap 'ns::inf' unless renamed to a valid identifier.. See below code). And, when I use the %rename keyword in the SWIG interface file (%rename(inf) ns::inf;. See below), the Python library file is correctly generated, but not the C++ .cxx intermediate file. The new error is then:

mwe_wrap.cxx: In function ‘PyObject* _wrap_inf(PyObject*, PyObject*)’:
mwe_wrap.cxx:3369:16: error: ‘inf’ is not a member of ‘ns’
 3369 |   result = ns::inf((ns::mwe const &)*arg1,(ns::mwe const &)*arg2);
      |                ^~~

I have reduced my problem to a minimal working example (with which I got the preceding error messages), available in the following files extracts.

mwe.cpp

#include "mwe.h"

namespace ns
{   mwe::mwe(void) {
        a = 1;
        b = -1;
    }

    mwe::mwe(long par1, long par2) {
        a = par1;
        b = par2;
    }

    mwe inf(const mwe &obj1, const mwe &obj2)
    {
        mwe objtemp(obj1.a, obj1.b);
        if (obj2.a > obj1.a) objtemp.a = obj2.a;
        if (obj2.b < obj1.b) objtemp.b = obj2.b;

        return(objtemp);
    }
}

mwe.h

#ifndef __MWE_H__
#define __MWE_H__

namespace ns
{
    class mwe {
    protected:
        long a;
        long b;

    public:
        mwe(void);
        mwe(long, long);

        friend mwe inf(const mwe &, const mwe &);
    };
}
#endif

mwe.i

// Module name
%module mwe

// Header
%rename(newinf) ns::inf;

// The following block is compulsory, because of the use of the `ns` namespace. See https://stackoverflow.com/a/3762478/7009806
%{
    #include "mwe.h"
%}

%include "mwe.h"

My compilation commands are the following ones (getting advantage of python-config):

swig -c++ -python -Wall mwe.i
g++ -fPIC -c -Wall mwe_wrap.cxx $(python-config --cflags)

How can I manage to automatically remove the ns:: namespace from the generated inf function in mwe_wrap.cxx (at line 3369 on my system)?

As a comparison, this function is correctly generated with the specified new name in mwe.py:

def newinf(arg1, arg2):
    return _mwe.newinf(arg1, arg2)

I really thought the .Py and .cxx would be consistent on such a matter. In facts, if I remove that ns:: manually and keep on compiling the shared object library and finally load it into Python, everything works as a charm (using newinf). Could it be some bug of some sort?


Solution

  • The warning is indeed not really warranted. Make an issue on github and I will look into it. The friend is the problem.

    However there is no other problem in SWIG besides this warning.

    Your C++ declaration is not really complete. A friend is not a declaration in itself. You should declare this function. Your header should be:

    #ifndef __MWE_H__
    #define __MWE_H__
    
    namespace ns {
    
    class mwe {
    protected:
      long a;
      long b;
    
    public:
      mwe(void);
      mwe(long, long);
    
      friend mwe inf(const mwe &, const mwe &);
    };
    
    mwe inf(const mwe &, const mwe &);
    } // namespace ns
    #endif
    

    If you do this, you will still get the warning, but the code will be correct and ns::inf will be automatically renamed to inf.