swig/c++ - why keep complaining undefined symbol - c++

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.

Related

Pybind11 - Where to put PYBIND11_MODULE

Im currently playing around with pybind11 a bit. Im trying to create a C++ class which then gets passed to a python interpreter embedded in my C++ source.
I created some dummy class just to test the basic functionality I kept everything in a single source file. This approach compiled and ran without any problems.
Now I separated my dummy Class Test into a Test.h and Test.cpp
Test.h
#pragma once
#include<iostream>
#include"pybind11\pybind11.h"
namespace py = pybind11;
class Test
{
public:
Test(const std::string &s);
~Test();
void printStr();
private:
std::string _s;
};
Test.cpp
#include "Test.h"
PYBIND11_MODULE(TestModule, m)
{
py::class_<Test>(m, "Test")
.def(py::init<const std::string &>())
.def("printStr", &Test::printStr);
}
Test::Test(const std::string &s) : _s(s)
{
}
Test::~Test()
{
}
void Test::printStr()
{
std::cout << "---> " << _s << std::endl;
}
main.cpp
#include"Test.h"
int main(int argc, char **argv)
{
PyImport_AppendInittab("TestModule", PyInit_TestModule);
Py_Initialize();
PyRun_SimpleString("import TestModule");
PyRun_SimpleString("t = TestModule.Test(\"str\")");
PyRun_SimpleString("t.printStr()");
Py_Finalize();
getchar();
return 1;
}
After putting the Class Test into a new file the Compiler cannot find the PyInit_TestModule (main.cpp line: 6) anymore since this is generated by the PYBIND11_MODULE Macro which lives in the Test.cpp file(MSVS2017 Error: C2065).
I tried putting the PYBIND11_MODULE Macro into the Test.h. This however resulted in a linker error which said that "_PyInit_TestModule" is already defined in main.obj (MSVS2017 Error: LNK2005)
Putting the PYBIND11_MODULE Macro in the main.cpp file works.
However I feel like this will become quite unreadable as soon as you put a lot of custom Module definitions into main.cpp or even worse you have multiple Python-Interpreter being started from different source files where you then
need to put the same definition in all those files which will be a mess and most likely turn into a linker error.
Has one of you faced the same Problem and how did you solve it?
I created a file of his own for the bindings, and compiled/linked it together with the original c++ file. This way:
1) Test.h + Test.cpp contain only c++ code of your class
2) Test-bindings.cpp contains the PYBIND11_MODULE and #include <Test.h>
3) Building (with cmake). You will get a PyTest.so file out of it, that you can load in python.
# c++ libray
add_library(TestLib SHARED /path/to/Test.h /path/to/Test.cpp)
# bindings
add_subdirectory(pybind11) # you must have downloaded this repo
include_directories(/path-only/to/Test.h)
pybind11_add_module(PyTest SHARED /path/to/Test-bindings.cpp /path/to/Test.cpp)
4) (I suggest you to) write the main in python, using the python-binding you just created
5) In your main.py
import PyTest
# do something

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.

Boost.Python - Exposing a class

I have the following class called "Wav" which is stored in another directory, with the files "Wav.h" and "Wav.cpp" and looks like the following:
enum ReadType {
NATIVE = 0,
DOUBLE,
};
namespace AudioLib {
class Wav : public Signal {
public:
Wav();
Wav(const int M, const int N);
///... ->
};
};
The .cpp file contains the implementation of this class, everything compiles well.
I'm trying to implement a Python wrapper using boost.python and have the following file:
#include <boost/python.hpp>
#include "../src/Wav/Wav.h"
using namespace boost::python;
BOOST_PYTHON_MODULE(Wav)
{
class_<AudioLib::Wav>("Wav",
init<const int, const int>());
}
In my Makefile, I am compiling the Wav.cpp:
# Compile the .wav Python and Cpp file
$(WAV_TARGET).so: $(WAV_TARGET).o
g++ -shared -Wl,--export-dynamic $(WAV_TARGET).o -L$(BOOST_LIB) -lboost_python -
lboost_python -L/usr/lib/python$(PYTHON_VERSION)/config -lpython$(PYTHON_VERSION) -o
$(WAV_TARGET).so
$(WAV_TARGET).o: $(WAV_TARGET).cpp
g++ $(CFLAGS) ../src/Wav/Wav.cpp -I$(PYTHON_INCLUDE) -I$(BOOST_INC) -fPIC -c
$(WAV_TARGET).cpp
And whenever I try to import into Python I get the following:
ImportError: Wav.so: undefined symbol: _ZN8AudioLib3WavC1Eii
Where am I going wrong?
It looks like you have failed to define the second constructor:
Wav(const int M, const int N);
I can replicate the error message by making a working (but simplified) copy of your example with in-line definitions and just removing the definition of that constructor. So my advice would be to check carefully for the definition in Wav.cpp and try creating an in-line definition to experiment.
If the definition does exist, maybe the linker flags are not right.

Call a C++ function from a C++ library in Linux

I want to call a function from C++ library on linux. I have a shared object of that library.
I want to call method getAge() that return an int from ydmg library.
Following is the code that I have written:
testydmg.cpp
#include "ydmg/bd.h"
#include "yut/hash.h"
#include "dlfcn.h"
extern "C" int getAge();
class testydmg{
public:
testydmg::testydmg(const yutHash& user){
}
testydmg::testydmg(){
}
testydmg::~testydmg(){
}
int testydmg::getFunction(){
void *handle;
int (*voidfnc)();
handle = dlopen("ydmg.so",RTLD_LAZY);
if(handle == NULL){
printf("error in opening ydmg lib");
} else {
voidfnc = (int (*)())dlsym(handle, "getAge");
(*voidfnc)();
printf("class loaded");
}
ydmgBd obj;
obj.getAge();
printf("Inside getFunction()...");
dlclose(handle);
}
};
I compile and link the code as below:
gcc -fPIC -shared -l stdc++ -I/home/y/libexec64/jdk1.6.0/include -I/home/y/libexec64/jdk1.6.0/include/linux -I/home/y/include testydmg.cpp -o libTestYdmg.so libydmg.so
Then I check for the method in the new shared object i.e. libTestYdmg.so
nm -C libTestYdmg.so | egrep getAge
I get nothing by running the above command.
Does it mean that it did not get the method getAge() from the library.
Could you please correct where I am going wrong ?
You want to use ydmgDB::getAge() but you are asking to the library for getAge(). This is not correct, just simply create a ydmgDBobject and invoke it's method getAge() without loading the library that is linked with your compile command line.
You don't need to dlopen the library.
Besides, getAge is not really included in libTestYdmg.so. You must look for it in libydmg.so using:
nm -C libydmg.so | grep getAge
If you're interested in actually using dlopen in C++ code, take a look at the
C++ dlopen mini HOWTO, which includes example code and some possibly important
warnings.