Using SWIG to Wrap C++ Function With Default Values - c++

I have the following C++ function in say.hpp:
#include <iostream>
void say(const char* text, const uint32_t x = 16, const uint32_t y = 24, const int32_t z = -1) {
std::cout << text << std::endl;
}
Here is my say.i:
%module say
%{
#include "say.hpp"
%}
%include "say.hpp"
Then, I built the shared library:
$ swig -python -c++ -I/usr/include say.i
$ g++ -fPIC -c say_wrap.cxx -I/opt/rh/rh-python38/root/usr/include/python3.8
$ g++ -shared say_wrap.o -o _say.so
Then, I tried to call it:
>>> import say
>>> say.say("hello")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/hc/test/cpp/say.py", line 66, in say
return _say.say(text, x, y, z)
TypeError: Wrong number or type of arguments for overloaded function 'say'.
Possible C/C++ prototypes are:
say(char const *,uint32_t const,uint32_t const,int32_t const)
say(char const *,uint32_t const,uint32_t const)
say(char const *,uint32_t const)
say(char const *)
>>>
It seems something is wrong with having default values for the function parameters as once I remove them, it works.
Any idea?

Use the following say.i file. SWIG has prewritten code for the standard integer types and needs it included to understand them. Without them, the wrapper receives the default values as opaque Python objects and doesn't know how to convert them to the correct C++ integer types.
%module say
%{
#include "say.hpp"
%}
%include <stdint.i>
%include "say.hpp"
Result:
>>> import say
>>> say.say('hello')
hello
>>> say.say('hello',1,2,3)
hello
Note you could also supply the typedefs directly, but better to use stdint.i:
%module say
%{
#include "say.hpp"
%}
typedef unsigned int uint32_t;
typedef int int32_t;
%include "say.hpp"

Related

Wrapper generator SWIG (C++/Perl): How to access "blessed" objects in a 1d vector<double>in Perl?

I have written a C++ library to extract simulation data (= simple vectors with (x,y) or (x,y,z) components) from electronic design automation (EDA) tools. More concrete, this data represents electrical signals for different points in time.
The C++ library offers several methods. Two important ones are:
std::vector<std::string> getSignalNames() // Returns all signal names in the file
std::vector<std::vector<double>> getSignals() // Returns the actual data as M x N matrix (with M rows and N columns)
Using the library in C++ works perfectly and yields the expected results, e.g.:
getSignalNames():
Signal1
Signal2
getSignals():
1 1 1 2
2 1 2 3
Perl programmers asked me to also offer the library to them and I decided to use the wrapper generator SWIG to create bindings. I worked through the tutorial and I was able to successfully set up a minimal working example.
Based on the example, I wrote a complete SWIG interface file for the C++ library. The wrapper generation and build process works smoothly and I can also use getSignalNames() without any problems:
// Perl snippet to read out signal names
my $parserPointer = new waveformparser::ScopeParser("input.file");
$signalNames = $parserPointer->getSignalNames();
foreach my $signalName ( #$signalNames ) {
print "$signalName\n";
}
// Output:
Signal1
Signal2
But, I ran into trouble when using the return value from getSignals():
// Perl snippet to read out the actual signal data
my $parserPointer = new waveformparser::ScopeParser("input.file");
$signalData = $parserPointer->getSignals();
foreach my $rowAsHashRef ( #$signalData ) {
print "reftype: " . reftype($rowAsHashRef) . "\n";
print "keys: " . keys(%$rowAsHashRef) . "\n"
}
// Output:
reftype: HASH
keys: 0
reftype: HASH
keys: 0
As you see, each row is represented as hash in Perl, but there are no keys in the Hash. Nevertheless, when using Perl's Data::Dumper, I can see the correct data type for each row:
my $parserPointer = new waveformparser::ScopeParser("input.file");
$signalData = $parserPointer->getSignals();
print Dumper $signalData;
// Output:
$VAR1 = [
bless( {}, 'waveformparser::vector_1d_double' ),
bless( {}, 'waveformparser::vector_1d_double' )
];
I.e., according to the data dumper, each row consists of several columns (i.e., 'waveformparser::vector_1d_double') which are defined in the SWIG interface file as following:
...
%include "std_vector.i"
%template(vector_1d_double) std::vector<double>;
%template(vector_2d_double) std::vector<std::vector<double>>;
...
My question is now: How can I access elements of this "blessed" (wrapped) vector_1d_double objects in Perl?
I thought, SWIG would provide convenient access methods for such objects. I.e., the underlying C++ data type is just a simple 1d vector of doubles (std::vector<double>).
You need to write an output typemap for std::vector<std::vector<double>> to convert to a proper Perl array of arrays. Here is an example:
VecVec.i:
%module VecVec
%typemap(out) std::vector<std::vector<double>> {
AV *array = (AV *) newAV();
auto vec_ptr = &$1;
std::vector<std::vector<double>>& vec = *vec_ptr;
for (const auto &item : vec) {
AV *subarray = (AV *) newAV();
for (const auto &subitem : item) {
av_push(subarray, newSVnv((NV) subitem));
}
av_push(array, (SV*) newRV_noinc((SV*)subarray));
}
$result = newRV_noinc((SV*) array);
sv_2mortal($result);
argvi++;
}
%{
#include "VecVec.h"
%}
%include "VecVec.h"
VecVec.h:
#ifndef VEVEC_H_
#define VECVEC_H_
#include <vector>
class VecVec
{
public:
VecVec() {}
~VecVec() {}
std::vector<std::vector<double>> getSignals();
private:
};
#endif
VecVec.cpp:
#include <string>
#include <vector>
#include <iostream>
#include "VecVec.h"
std::vector<std::vector<double>> VecVec::getSignals()
{
std::vector<std::vector<double>> vec {
{1, 2, 3},
{4, 5, 6}
};
return vec;
}
Then compile with:
perl_include_dir=$(perl -MConfig -e'print $Config{archlib}')"/CORE"
swig -perl5 -c++ -I/usr/include VecVec.i
g++ -fPIC -c VecVec.cpp
g++ -I${perl_include_dir} -c -fPIC -g -o VecVec_wrap.o VecVec_wrap.cxx
g++ -shared -L. VecVec.o VecVec_wrap.o -o VecVec.so
and test the module with test.pl:
use strict;
use warnings;
use Data::Dumper qw(Dumper);
use lib '.';
use VecVec;
my $p = VecVec::VecVec->new();
my $sig = $p->getSignals();
print Dumper($sig);
Output:
$VAR1 = [
[
'1',
'2',
'3'
],
[
'4',
'5',
'6'
]
];
Re:
I thought, SWIG would provide convenient access methods for such objects. I.e., the underlying C++ data type is just a simple 1d vector of doubles (std::vector).
It should. You do need to instantiate the templates before SWIG processes the functions, but you'll need to show a minimal, reproducible example to know for sure why it isn't working.
I'm not currently familiar with building a Perl extension, but here's a non-language-specific SWIG .i file that should work for Perl. I'll demonstrate with Python:
test.i
%module test
// Define templates before SWIG processes the code.
%include "std_vector.i"
%include "std_string.i"
%template(vector_string) std::vector<std::string>;
%template(vector_1d_double) std::vector<double>;
%template(vector_2d_double) std::vector<std::vector<double>>;
%inline %{
#include <vector>
#include <string>
std::vector<std::string> getSignalNames() {
return {"abc","123"};
}
std::vector<std::vector<double>> getSignals() {
return {{1.1,2.2},{3.3,4.4}};
}
%}
The above includes everything in a standalone file. I used swig -c++ -python test.i and compiled the resulting test_wrap.cxx file as a Python extension.
Demo:
>>> import test
>>> test.getSignalNames()
('abc', '123')
>>> test.getSignals()
((1.1, 2.2), (3.3, 4.4))
If SWIG's Perl support is comparable this .i file should work for you without defining your own output typemaps.

share data pointer between c and lua module compiled with SWIG

I need to provide data structure pointer in my main program, where I have Lua state defined, to the dynamically loaded Lua module created by wrapping a c++ code using SWIG.
This is my code example:
in SimpleStruct.h:
#pragma once
struct SimpleStruct
{
int a;
double b;
};
in exmaple.h (this one is compiled with SWIG) to Lua library:
#pragma once
#include "SimpleStruct.h"
#include <iostream>
class TestClass
{
public:
TestClass()
{
std::cout<<"TestClass created"<<std::endl;
}
~TestClass() {}
void ReadSimpleStruct(void * tmp)
{
std::cout<<"reading pointer: "<<std::endl;
SimpleStruct * pp = reinterpret_cast< SimpleStruct * >(tmp);
std::cout<<"Simple Struct: " << pp->a << " " << pp->b << std::endl;
}
};
in example.cpp only:
#include "example.h"
and this is my main program (LuaTest.cpp):
extern "C"
{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
#include <iostream>
#include "SimpleStruct.h"
int main(int argc, char ** argv)
{
lua_State * L = luaL_newstate();
luaL_openlibs(L);
SimpleStruct * ss = new SimpleStruct();
ss->a = 1;
ss->b = 2;
lua_pushlightuserdata(L,ss);
lua_setglobal( L, "myptr");
int s = luaL_dostring(L, "require('example')");
s = luaL_dostring(L, "mc = example.TestClass()");
s = luaL_dostring(L, "mc:ReadSimpleStruct(myptr)");
if(s)
{
printf("Error: %s \n", lua_tostring(L, -1));
lua_pop(L, 1);
}
lua_close(L);
std::cout<<"done"<<std::endl;
return 0;
}
example.i (copied from Lua examples in SWIG):
/* File : example.i */
%module example
%{
#include "example.h"
%}
/* Let's just grab the original header file here */
%include "example.h"
and I compile everything as follows:
swig -c++ -lua example.i
g++ -c -fpic example.cpp example_wrap.cxx -I/usr/local/include -I/usr/include/lua5.2/
g++ -shared example.o example_wrap.o -o example.so
g++ LuaTest.cpp -o luatest -llua5.2 -I/usr/include/lua5.2/ -Wall
on Ubuntu 16.04 (and on osx, with different paths and the same result).
In the last line of Lua script I've got segmentation fault (when I try to access pp->a in "mc:ReadSimpleStruct(myptr)").
So my question is: how can I provide a pointer to c++ object to the loaded Lua library using Lua light userdata?
In general: I have in my main program a class with game parameters and objects, and I would like to provide a pointer to that class to other loaded Lua libraries compiled with a SWIG.
With use of a debugger (or just printing a little extra inside TestClass::ReadSimpleStruct) we can see at least the superficial cause of the segfault quite quickly. The value of the tmp argument to your function is 0x20 on my test setup. That's clearly not right, but understanding why and how to fix it takes a little more investigation.
As a starting point I added one more call to luaL_dostring(L, "print(myptr)") and used a debugger to check that the global variable was indead working as intended. For good measure I added some assert statements after each call to luaL_dostring, because you're actually only checking the return value of the last one, although here that didn't really make any difference.
Having not exactly written much Lua in my life I looked a the documentation for 'Light userdata', which I saw you were using but didn't know what it was. It sounds ideal:
A light userdatum is a value that represents a C pointer (that is, a void * value)
The problem is though that if we inspect the generated example_wrap.cxx file we can see that SWIG is actually trying to be more clever than that and, if we trace the code for arg2 before the generated call to (arg1)->ReadSimpleStruct(arg2) we can see that it's calling SWIG_ConvertPtr (which eventually calls SWIG_Lua_ConvertPtr), which then does:
lua_touserdata(L, index);
//... Some typing stuff from the macro
*ptr=usr->ptr; // BOOM!
I.e. what you're doing is not what SWIG expects to see for void *, SWIG is expecting to manage them all through its typing system as return values from other functions or SWIG managed globals. (I'm slightly surprised that SWIG let this get as far as a segfault without raising an error, but I think it's because void* is being special cased somewhat)
This old question served as quite a nice example to confirm my understanding of lua_pushlightuserdata. Basically we will need to write our own typemap to make this function argument get handled the way you're trying to use it (if you really do want to not let SWIG manage this?). What we want to do is very simple though. The usage case here is also substantially similar to the example I linked, except that the variable we're after when we call lua_touserdata is a function argument. That means it's at a positive offset into the stack, not a negative one. SWIG in fact can tell us what the offset inside our typemape with the $input substitution, so our typemap doesn't only work for the 1st argument to a member function.
So our typemap, which does this for any function argument void * tmp inside our modified example.i file becomes:
%module example
%{
#include "example.h"
%}
%typemap(in) void * tmp %{
$1 = lua_touserdata(L, $input);
%}
%include "example.h"
And that then compiles and runs with:
swig -c++ -lua example.i
g++ -fPIC example_wrap.cxx -I/usr/local/include -I/usr/include/lua5.2/ -shared -o example.so && g++ -Wall -Wextra LuaTest.cpp -o luatest -llua5.2 -I/usr/include/lua5.2/
./luatest
TestClass created
userdata: 0x11d0730
reading pointer: 0x11d0730
Simple Struct: 1 2
done

The enums does not get passed to TCL when we have SWIG TCL Static Linking

In continuation to question
how to pass enum values from TCL script to C++ class using Swig
I have following code
1) File : example.i
%module example
%{
/* Put header files here or function declarations like below */
#include "example.h"
%}
%include "example.h"
2 File example.h
class myClass {
public:
enum Type {one,two};
myClass() {}
static bool printVal(int val);
static bool printEnum(Type val);
};
3) File example.cpp
#include "example.h"
#include <iostream>
using namespace std;
bool myClass::printVal(int val) {
cout << " Int Val = " << val << endl;
return 0;
}
bool myClass::printEnum(type val) {
cout << " Enum Val = " << val << endl;
return 0;
}
If I link the swig files in the form of shared library it is working fine
swig -c++ -tcl example.i
g++ -c -fpic example_wrap.cxx example.cpp -I/usr/local/include
g++ -shared example.o example_wrap.o -o example.so
setenv LD_LIBRARY_PATH /pathtoexample.so:$LD_LIBRARY_PATH
tclsh
% load example.so
% myClass_printVal $myClass_one
But if the swig code and example.* files are linked statically I am getting following error
% myClass_printVal $myClass_one
can't read "myClass_one": no such variable
Looking forward for guidance/help
Firstly, if you use more of the path to the shared library, you don't need to alter the LD_LIBRARY_PATH variable. In the case that it's in the current directory (convenient for testing) you can just do this:
load ./example.so
When it's in the same directory as the current script, you instead do this rather longer version:
load [file join [file dirname [info script]] example.so]
# This also probably works:
#load [file dirname [info script]]/example.so
Secondly, you should check what the example has actually created; it might simply be using a different name to what you expect. You can use info commands, info vars and namespace children to find this out; they list the commands, variables and namespaces currently visible.
[EDIT from comments for visibility]: The variables are in the ::swig namespace.

swig/c++ - why keep complaining undefined symbol

I am using SWIG 3.0.7.
I have the following simple c++ files:
myif.h
class If
{
public:
const std::string str() const;
If(const char *str);
private:
std::string m_str;
};
myif.c
#include "myif.h"
const std::string
If::get_str() const
{
return m_str;
}
If::If(const char *str)
{
m_str = str;
}
Here is the swig interface file:
myif.i
%module myif
%{
#include "myif.h"
%}
%include "stl.i"
%include "myif.h"
Here are the commands to build everything:
$PATH={swig_path}/bin swig -c++ -tcl myif.i
g++ -fPIC -c myif_wrap.cxx
g++ -shared myif_wrap.o -o myif_wrap.so
There is no error/warning. But inside tclsh when I load the library, I get the following error:
% load ./myif_wrap.so
couldn't load file "./myif_wrap.so": ./myif_wrap.so: undefined symbol: _ZNK2If3strEv
Is it saying I need to link STL libraries? But not sure how to do that.
[EDIT] Yes, my bad, the function name is wrong, I should have made a caller C++ and made sure everything is fine in C++ before loading library in tclsh; also the module name in .i file needs to match the library name.

Calling SWIG generated code from Python interpreter

I am trying to use SWIG in an effort to call member functions of a C++ object from Python. Currently I have a small example class with a getter and setter to modify a member variable of the C++ class. Here is the C++ header file:
#ifndef _square_
#define _square_
#include <iostream>
class Square
{
private:
double x;
double y;
const char *name;
public:
void setName(const char*);
const char* getName();
Square() {
name = "construct value";
};
};
#endif
Here is the .cpp implementation file:
#include <iostream>
using namespace std;
#include "Square.h"
const char* Square::getName()
{
return name;
}
void Square::setName(const char* name)
{
this->name = name;
return;
}
And the Square.i file for SWIG:
%module Square
%{
#include "Square.h"
%}
%include "Square.h"
SWIG seems to generate the Square_wrap.cxx file without issue, and the resulting object files seem to link fine:
$ swig -python -c++ Square.i
$ g++ -c -fpic Square.cxx Square_wrap.cxx -I/usr/include/python2.7
$ g++ -shared Square.o Square_wrap.o -o _Square.so
Now for some example Python to test the results:
$ cat test2.py
#!/usr/bin/python
import Square
s = Square.Square()
print s.getName()
s.setName("newnametest")
print s.getName()
If I run this through the Python interpreter everything works fine:
$ python test2.py
construct value
newnametest
But if I interactively enter in the test lines via Python's CLI, things do not work:
$ python
Python 2.7.4 (default, Apr 19 2013, 18:28:01)
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import Square
>>>
>>> s = Square.Square()
>>>
>>> print s.getName()
construct value
>>> s.setName("newnametest")
>>> print s.getName()
>>> s.getName()
'<stdin>'
>>> s.setName('newnametest')
>>> s.getName()
''
>>> s.setName("newnametest")
>>> s.getName()
''
Does Python handle a Python script file differently under the hood in comparison to the CLI, or am I somehow abusing the Python interface generated by SWIG? Any tips on how to debug or understand the issue under the hood would be much appreciated.
As far as I see, you are just storing the reference on the cpp file (this->name = name). It would be good to copy it, because there are high chances the string doesn't last enough and is just discarded after the function returns (and garbage collected slightly after that). This would explain why in the script it works (there is no GCollection nor anything else happens between the two calls).
Try making a copy with strdup or using std::string.