I try to create an example using SWIG and NodeJS on my M1 (arm64) Mac, but I want to mention this as early as possible: this issue appears also on an Intel (x64) Mac.
I create my simple example Files like this:
example.h
#pragma once
class Die
{
public:
Die();
~Die();
int foo(int a);
};
Die* getDie();
//to test if the issue also appears getting a simple functiom without any class context.
extern "C"
{
bool getFoo();
}
here is the implementation.
example.cpp
#include <iostream>
#include "example.h"
int Die::foo(int a)
{
std::cout << "foo: running fact from simple_ex" << std::endl;
return 1;
}
Die::Die()
{
}
Die::~Die()
{
}
// out of Class Context
Die* getDie()
{
return new Die();
}
extern "C"
{
bool getFoo()
{
return true;
}
}
my Swig interface is as follows:
example.i
%module example
%{
#include "example.h"
%}
%include "example.h"
then i create my example_warp.cxx file. But as actually the 4.0.2 Version of Swig is not compatible with NodeJs v16.0.0 (read SWIG support for NodeJS v12 #1520 and Prepare SWIG for Node.js v12 #1746).
Therefore i needed to build swig from source using master branch with the current version (4.1.0). Please keep that in mind.
Swig Command:
swig -Wall -c++ -javascript -node example.i
Here now some files preparing to create the .node file
package.json { "name": "SwigJS", "version": "0.0.1", "scripts": { "start": "node index.js", "install": "node-gyp clean configure build"
},
"dependencies": {
"nan": "^2.16.0",
"node-gyp": "^9.0.0"
},
"devDependencies": {
"electron-rebuild": "^3.2.7"
}
}
the package.json i got from a mate as an example an edited it to work with my project so there may be some lines not really needed by me.
binding.gyp
{
"targets": [
{
"target_name": "SwigJS",
"sources": [ "example_wrap.cxx" ],
"include_dirs" : [ "<!(node -e \"require('nan')\")" ]
}
]
}
now i build my SwigJS.node file using:
node-gyp configure
node-gyp build
it runs through without any errors.
Now i try to access the node-file in my JavaScript but i always get the error message:
missing symbol called
index.js
const Swigjs = require("./build/Release/SwigJS.node");
console.log("exports :", Swigjs); //show exports
die = Swigjs.getDie(); //try to get the Class
console.log(die.foo(5)); //call a function from the class
the output looks like this:
[Running] node "/Users/rolf/Documents/SwigJS/index.js"
exports : {
getDie: [Function (anonymous)],
getFoo: [Function (anonymous)],
Die: [Function: Die]
}
dyld[49745]: missing symbol called
[Done] exited with code=null in 0.12 seconds
What i have tried to find the error:
all of it (except last mentioned) ended with the same result "missing symbol called"
help would be appreciated big.
Your question is an interesting take on a FAQ on this site: What is an undefined reference/unresolved external symbol error and how do I fix it?.
The role of SWIG is indeed to generate glue code between Node.js and C++ code. But it does only that. If you inspect the .dylib
file that is associated with your NodeJS module using the nm
command, you will see that it has U
ndefined references to the C++ functions it wraps.
This is by design. SWIG expects that the code it wraps is somehow already loaded into memory. There are three approaches to do so:
example.a
) and link that statically into the wrapper. I think it suffices to add example.cpp
to the sources
section of binding.gyp
example.dylib
) and dynamically link it to the SWIG wrapper. I have not used GYP myself yet, but I think it means adding the following to your targets
entry in bindings.gyp
: 'link_settings': {
'libraries': [
'-lexample',
],
},
example.dylib
) and use dlopen
to load it explicitly. This puts a tremendous burden on your users and is very hard to debug. Do not do this.