Why does sip complain about unexpected type 'str' when using a char *?

325 views Asked by At

I am trying to use sip to create python bindings from c++ to python 3.8. I found a simple example here and updated it to get it working with sip version 5.4 which I installed with pip. Details can be found here

I changed the names from word to basicword because I rewrote and tested the word example with strings. To do that I had to write a bunch of sip specific code to get the import of the string library working and thought there must be an easier way.

I was under the assumption, that using a char * (like in the original tutorial) would be 'easier' for sip, what am I missing?

My sip file basicword.sip:

// Define the SIP wrapper to the basicword library.

%Module(name=basicword, language="C++")

class Basicword {

%TypeHeaderCode
#include <basicword.h>
%End

public:
    Basicword(const char *w);

    char *reverse() const;
};

My pyproject.toml file:

# Specify sip v5 as the build system for the package.
[build-system]
requires = ["sip >=5, <6"]
build-backend = "sipbuild.api"

# Specify the PEP 566 metadata for the project.
[tool.sip.metadata]
name = "basicword"

# Configure the building of the basicword bindings.
[tool.sip.bindings.basicword]
headers = ["basicword.h"]
include-dirs = ["."]
libraries = ["basicword"]
library-dirs = ["."]

My basicword.h file:

#ifndef BASICWORD_H
#define BASICWORD_H


// Define the interface to the basicword library.

class Basicword {

private:
    const char *the_word;

public:
    Basicword(const char *w);

    char *reverse() const;
};


#endif //BASICWORD_H

My basicword.cpp file:

#include "basicword.h"

#include <cstring>

Basicword::Basicword(const char *w) {
    the_word = w;
}

char* Basicword::reverse() const {
    int len = strlen(the_word);
    char *str = new char[len+1];
    for(int i = len-1;i >= 0 ;i--) {
        str[len-1-i] = the_word[i];
    }
    str[len+1]='\0';
    return str;
}

My file test.py:

from basicword import Basicword

w = Basicword("reverse me") // -> error thrown here


if __name__ == '__main__':
    print(w.reverse())

The error message:

Traceback (most recent call last):
  File "<path to testfile>/test.py", line 3, in <module>
    w = Basicword("reverse me")
TypeError: arguments did not match any overloaded call:
  Basicword(str): argument 1 has unexpected type 'str'
  Basicword(Basicword): argument 1 has unexpected type 'str'

Thank you for your answer!

Bye Johnny

1

There are 1 answers

0
JohnnyWaker On BEST ANSWER

In short Python2 uses str-type by default and python3 byte-type so I had to change default encoding for python3.

I found the relevant detail here

Encoding

This string annotation specifies that the corresponding argument (which should be either char, const char, char * or const char *)

refers to an encoded character or '\0' terminated encoded string with the specified encoding. The encoding can be either "ASCII", "Latin-1", "UTF-8" or "None". An encoding of "None" means that the corresponding argument refers to an unencoded character or string.

The default encoding is specified by the %DefaultEncoding directive. If the directive is not specified then None is used.

Python v3 will use the bytes type to represent the argument if the encoding is "None" and the str type otherwise.

Python v2 will use the str type to represent the argument if the encoding is "None" and the unicode type otherwise.

With the following simple addition in basicword.sip ...

// Define the SIP wrapper to the basicword library.

%Module(name=basicword, language="C++")

%DefaultEncoding "UTF-8" // Missing Encoding!

class Basicword {

%TypeHeaderCode
#include <basicword.h>
%End

public:
    Basicword(const char *w);

    char *reverse() const;
};

everything works now.