How to use Swift static library (.a) in C++ project? - c++

I have a pure Swift package that I built with the Swift Package Manager. My Package.Swift looks like this:
// File: Package.swift
// swift-tools-version:5.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "SwiftPackage",
products: [
.library(
name: "SwiftPackage",
type: .static,
targets: ["SwiftPackage"]),
],
dependencies: [
],
targets: [
.target(
name: "SwiftPackage",
dependencies: []),
.testTarget(
name: "SwiftPackageTests",
dependencies: ["SwiftPackage"]),
]
)
This Swift code I'm building contains a public function that I want to call from my C++ code:
// File: SwiftPackage.swift
public func StartWatcher() {
// code ...
}
I created a header file SwiftPackage.hh where I define the StartWatcher function like so:
// File: SwiftPackage.hh
void (*StartWatcher)();
Now I have my main.cc file where I include the SwiftPackage.hh and call the StartWatcher function:
// File: main.cc
#include <SwiftPackage.hh>
int main() {
StartWatcher();
return 0;
}
However, when I run the built executable I'm getting the following error
./swift_package' terminated by signal SIGSEGV (Address boundary error)
Building
My build process is following:
First, I build the Swift package by running swift build --package-path SwiftPackage. This creates the libSwiftPackage.a library.
Second, I build the C++ project where I link the libSwiftPackage.a library that was created in the previous step:
g++ -std=c++11 -L./SwiftPackage/.build/debug/ main.cc -lSwiftPackage -o swift_package
What am I doing wrong? I suspect that the Swift library isn't properly linked.
Edit
Based on the #Acorn's answer I did two things:
Added my StartWatcher declarition in an extern "C" block
Added an attribute #_cdecl("StartWatcher") to my StartWatcher Swift function which should make sure that the name isn't mangled in the library.
Now I get a different output which is a bunch of messages like this:
Undefined symbols for architecture x86_64:
"static Foundation.Notification._unconditionallyBridgeFromObjectiveC(__C.NSNotification?) -> Foundation.Notification", referenced from:
#objc SwiftPackage.AppDelegate.applicationDidFinishLaunching(Foundation.Notification) -> () in libSwiftPackage.a(AppDelegate.swift.o)
#objc SwiftPackage.AppDelegate.applicationWillTerminate(Foundation.Notification) -> () in libSwiftPackage.a(AppDelegate.swift.o)
It seems to me that the there is some kind of problem accessing other libraries that are used in the Swift package?

Summary: this can be made to work (probably), but if this is intended to be used in production code, then the only correct approach is to consult the Swift documentation and, if it is still the case there is no official support, ask the Swift team how to approach the problem.
You should follow whatever documentation Swift has for exporting functions in the C ABI convention. It is doubtful doing it blindly works, and if it does, it is probably by chance and may stop working at any point in the future.
Sadly, there does not seem to be official support for such thing, at least according to questions like:
Passing a Swift string to C
What is the best way to call into Swift from C?
To make any kind of FFI to work unofficially, there are several things to take into account:
Figure out which calling convention Swift follows, including if it uses hidden parameters or things like that. The best case scenario is that Swift uses the usual one in your system.
Check what are the names of the actual symbols exported by Swift. Perhaps they are mangled.
Research if there are any semantics to hold that the other language runtime does automatically. For instance, if some code needs to be called before/after, or perhaps something need to be initialized, etc.
On the C++ side, you should write your declaration in an extern "C" block so that the symbol is not expected to be C++-mangled:
extern "C" {
void StartWatcher();
}
There should be no need to declare it as a function pointer either.

Related

Compiling Rust that calls C++ to WASM

I've found this How do I use a C library in a Rust library compiled to WebAssembly?, but this relies on wasm-merge, which has been discontinued. My problem is the following, I have some C++ code that I would like to call from Rust in order to have the option to compile the resulting package either to native code for use in mobile apps or to Webassembly for use in Node.js. At the moment, I have the following setup:
libTest.cpp
extern "C"{
int test_function(int i){
return i;
}
}
lib.rs
use wasm_bindgen::prelude::*;
#[link(name = "Test")]
extern "C"{
pub fn test_function(i: i32) -> i32 ;
}
#[wasm_bindgen]
pub fn test_function_js(i : i32) -> i32{
let res = unsafe{test_function(i)};
res
}
build.rs
fn main() {
cc::Build::new()
.cpp(true)
.file("libTest.cpp")
.compile("libTest.a");
}
This compiles and works when compiling to native code using a simple cargo build, but does not work for building to wasm, for which I'm doing cargo build --target wasm32-unknown-unknown. There I get the two errors
= note: rust-lld: error: /[path to my project]/target/wasm32-unknown-unknown/debug/build/rustCpp-cc5e129d4ee03598/out/libTest.a: archive has no index; run ranlib to add one
rust-lld: error: unable to find library -lstdc++
Is this the right way to go about this and if yes, how do I resolve the above error? If not, how do I best go about calling C++ from Rust and compiling it to wasm?
(This is not really a full answer, but too long for a comment.)
I can compile your example with
cc::Build::new()
.archiver("llvm-ar") // Takes care of "archive has no index" - emar might be an alternative
.cpp_link_stdlib(None) // Takes care of "unable to find library -lstdc++"
… // rest of your flags
but I'm not sure whether the resulting binary is useful to you. Especially, it contains WASI imports when compiled in debug mode, and you'll probably get linker errors if you start using any interesting functions (e.g. sin).
You could in theory give the C++ compiler a full stdlib to work with through .flag("--sysroot=/usr/share/wasi-sysroot/") (if you have wasi-sdk or wasi-libc++ installed), but
I'm unsure how to best account for differences of where this folder is usually installed (maybe like this)
I think you have to also pass this flag at link time, but I don't know how (it seems to work without, though)
that would target wasi, and may not be useful for whatever bindgen-based environment you have in mind.

Using C++ inside an iOS CocoaPod

I am building a private cocoapod for iOS, and am running into issues with some C++ code. The project builds fine in XCode, but when I attempt to run pod lib lint MyProjectName.podspec I get the following error:
- ERROR | xcodebuild: /path/to/aheader.h:2:10: error: 'string' file not found
The header has the following first line:
#include <string>
Searching for possible solutions, I added the following to podspec (based on CocoaPods: Linking with C++ symbols defined in libPods.a)
s.source_files = "MyProjectName/**/*.{swift,c,m,h,mm,cpp,plist}"
s.library = 'c++'
s.xcconfig = {
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11',
'CLANG_CXX_LIBRARY' => 'libc++'
}
But it made no difference to the error. Another suggestion I saw was to "use a wrapper", but this piece of code (which is 3rd-party IP that I can not port to Objective C) is already using a wrapper.
How can I build the pod successfully by mixing both Objective C and C++ along with Swift? Any (non-null) pointers would be appreciated.
I had to simply renamed the C++ header extension to .hpp, so it was not included by default in the source_files filter. This resolved the issue, as the wrapper was including the header. Posting this in case someone else runs into the same issue.
Update: Nope, it just lets the app build, but using the pod still doesn't work.
Try explicitly adding stdc++ in the linker options.
s.pod_target_xcconfig = {
'OTHER_LDFLAGS' => '-l"stdc++"'
}

How do I use arduino libraries with standard C code

I am using Eclipse kepler for AVR development.
The code that I have is C (Open Source), and I've gotten it adjusted so it runs perfectly. My target is an ATmega2560, in the form of an arduino mega2560.
Using the arduino board is strictly for hardware convenience; we are developing the hardware to be a custom board with most of the core arduino mega2560 components.
I need to use several libraries with this project that are only available as arduino libraries, namely libraries for an e-paper screen (from seeedstudio) and Nordic's BLE nRF8001.
If I create a new arduino project using the plugin in eclipse, I can build and run the tests for the arduino libraries perfectly.
When I try to merge the 2 code bases together, I can't seem to call the functions in the added arduino libraries - if I call them the compiler throws a linking error.
Building target: Virgin2ManualArdInsert.elf
Invoking: AVR C Linker
avr-gcc -Wl,-Map,Virgin2ManualArdInsert.map -mmcu=atmega2560 -o "Virgin2ManualArdInsert.elf" ./avr/adc.o ./avr/eeprom.o ./avr/lcd_and_input.o ./avr/main.o ./avr/strings.o ./avr/unimplemented.o ./avr/usart.o ./aes.o ./baseconv.o ./bignum256.o ./ecdsa.o ./endian.o ./fft.o ./fix16.o ./hash.o ./hmac_sha512.o ./messages.pb.o ./p2sh_addr_gen.o ./pb_decode.o ./pb_encode.o ./pbkdf2.o ./prandom.o ./ripemd160.o ./sha256.o ./statistics.o ./stream_comm.o ./test_helpers.o ./transaction.o ./wallet.o ./xex.o
./avr/main.o: In function `main':
main.c:(.text.startup.main+0xc): undefined reference to `writeEink'
collect2: error: ld returned 1 exit status
makefile:53: recipe for target 'Virgin2ManualArdInsert.elf' failed
make: *** [Virgin2ManualArdInsert.elf] Error 1
As a test, I'm just trying to call a basic "write to display" call in eInk.cpp from main.c:
extern "C"{
void writeEink()
{
EPAPER.begin(EPD_SIZE); // setup epaper, size
EPAPER.setDirection(DIRNORMAL); // set display direction
eSD.begin(EPD_SIZE);
GT20L16.begin();
// int timer1 = millis();
EPAPER.drawString("testing", 10, 10);
EPAPER.drawNumber(12345, 60, 40);
EPAPER.drawFloat(-1.25, 2, 80, 65);
EPAPER.display(); // use only once
}
Is a static library built from the arduino cores the way to go here? I've tried it (though it seems most of the procedures are outdated) and the libraries do not want to link/be called.
What is the correct procedure for including C++/Arduino calls in my C code?
I've tried using extern "C" {function()}; in my .cpp files and .h files but to no use.
Thank you for any help or pointers to where I can figure it out for myself.
You can try to compile your C code as C++ by simply renaming the files to *.CPP, but chances are that you have to modify your code to make it compile as C++ code. There are things that are allowed for C, but not for C++ (like calling functions that are not declared).
The other solution is to wirte wrappers around the C++ functions that you want to use from C.
You have to consider two limitations of C against C++:
C is not object oriented
C does not support overloading of functions
This example for Serial.print() shows how you can handle this with a wrapper:
extern "C" void SerialPrintInteger( int value )
{
Serial.print( value );
}
In this example you would write similar functions like SerialPrintFloat(), SerialPrintString() etc.
The extern "C" prefix tells the compiler to create the function in a way that makes it callable from C.
The error you received above isn't a compiler error, it's a linker error. I haven't used Eclipse for Arduino development, I just stick with the Arduino IDE, but the standard Arduino projects expect all of your code to be in a single source file, which it compiles and then links with the Arduino libraries. Arduino programs don't have a C/UNIX-style "main" function, the standard functions are "setup" and "loop."
I recommend going back to one of the Arduino example programs, blink for instance, and watching the console log as Eclipse compiles and links the program. What's happening here is:
The C/C++ compiler compiles your source code, including setup(), loop(), and any other functions you have created, into an object file.
The Linker links this single object file with the Arduino runtime, and any Arduino libraries you have specified. The output of this is an image of the program, in your example above it's trying to make 'Virgin2ManualArdInsert.elf'.
The uploader (probably avrdude) loads this image into your Arduino and resets it.
The Arduino comes out of reset and runs your new code.
If your program is reasonably small, say not more than a few hundred lines, just put all the functions in the one source file, then you won't have to learn how to drive the linker.
If you need, for some reason, to have the sources in a separate file (maybe they're shared with another program, or another platform), then you'll have to learn how to get Eclipse to link the object files from your multiple source files. This may just involve adding the sources into your Eclipse project properly, or you may have to write a Makefile or something similar.
As for C vs C++ source code, you can usually drop a C function into a C++ source file and compile it. There are a few differences, but this way you don't need to worry about "C" linkage or any of that silliness.

Linker issues in C++ / ObjC

I'm porting a sizeable codebase to iOS. The simplified version of my scenario is as follows:
I have a C++ library, built from the command line. I can run code from it from the iOS simulator, so I believe it's correctly built.
I have the skeleton iOS application created by Xcode
I want to add logging to the C++ library. I have a trace() method which takes fmt, .... I compile that .cpp using -x Objective-c++, the code is as follows:
void trace (const char* sFmt, ...)
{
va_list args;
va_start(args, sFmt);
NSString* sFmt2 = [ NSString stringWithUTF8String: sFmt ];
NSLogv(sFmt2, args);
va_end(args);
}
The library compiles just fine. However, when I try to link the app, I get a linker error:
".objc_class_name_NSString", referenced from: literal-pointer#__OBJC#__cls_refs#NSString in lib.a(trace.o)
This is strange because I can use NSString and NSLog from a .mm file in the project itself. The Foundation framework is linked. Moreover, just to test, instead of calling NSString from my library I added a helper foobar() to the .mm in the project, which does this
void foobar (const char* sFmt)
{
NSLog([NSString stringWithUTF8String:sFmt]);
}
When this is called from the library function above, it works!
Everything I read about this kind of error involves a "just updated my SDK" scenario, which is not my case. I started doing iOS stuff literally two days ago, I haven't changed the default project settings, etc.
My guess is that name mangling is failing at some point, because I know NSString is indeed linked, but it seems the name refrenced by the library is different to the linked one.
Any ideas?
Your linker output indicates that your command-line-built library is lib.a, which indicates to me that it's a static library. I'd guess that, not being a dylib, it doesn't know how to use the dyloader to find missing symbols at runtime. Relatedly, I suspect that the iOS project will link in these System libraries dynamically (even though non-OS developers can't create dynamic frameworks), whereas your static C++ library will expect those symbols to be resolved at link time.
I suspect that any of these three things would resolve the issue: (in descending order of attractiveness)
statically link against the right libraries when building the C++ library
make the C++ library a dylib instead of a static library (so it will expect to find missing symbols at runtime using the dyloader)
link your whole app statically (so the NSString class symbols are present at app link time) (This is a bad idea, if it's even possible at all, but would likely solve the problem.)
Hope this helps!
I was able to fix this by using libtool instead of ld, passing it -framework Foundation, and passing -fobjc-abi-version=2 to gcc.

Import C++ function into Python program

I'm experimenting with python functions right now. I've found a way to import python functions into c/c++ code, but not the other way around.
I have a c++ program written and it has a certain function in it. I'd like to "import" the compiled c++ program into my python script and call the c++ function.
For simplicity, say the c++ function is as simple as:
int square(x)
{
return x*x;
}
and the compiled program is named Cprog.
I'd like my python script to be something like:
import Cprog
print Cprog.square(4)
Is this possible? I've searched the internet to no avail and I'm hoping one of you gurus might have a clever way of going about this...
Here is a little working completion of the simple example above. Although the thread is old, I think it is helpful to have a simple all-embracing guide for beginners, because I also had some problems before.
function.cpp content (extern "C" used so that ctypes module can handle the function):
extern "C" int square(int x)
{
return x*x;
}
wrapper.py content:
import ctypes
print(ctypes.windll.library.square(4)) # windows
print(ctypes.CDLL('./library.so').square(4)) # linux or when mingw used on windows
Then compile the function.cpp file (by using mingw for example):
g++ -shared -c -fPIC function.cpp -o function.o
Then create the shared object library with the following command (note: not everywhere are blanks):
g++ -shared -Wl,-soname,library.so -o library.so function.o
Then run the wrapper.py an the program should work.
If you build your program as a shared library/DLL, you could use ctypes to call it.
import ctypes
print ctypes.windll.cprog.square(4) # windows
print ctypes.CDLL('cprog.so').square(4) # linux
You need to create a python module with that function in it. There are three main ways:
Using Swig - this reads your c code and creates a python module from it.
Hand coded using the python c api.
Using Boost::Python (often the easiest way).
This pdf covers 1 and 2. This page will tell you how to use Boost::Python.
You cannot (easily) use a function that is in a c/c++ program - it must be in a static library (which you can also link your c/c++ program against).
EDIT -
Cython Is also worth a mention.
You want to extend python with a C/C++ module. The following Python documentation is a good place to start reading: http://docs.python.org/extending/extending.html
There are a lot of different ways to wrap C++ code to be used in Python. Most are listed on the Python wiki here.
I've found a decently easy way to automate it is to use py++ to automatically generate the wrappers, then compile the generated files. But this is more appropriate for larger projects where wrapping everything by hand is just not very feasible and would cause a maintenence nightmare.