How to call functions from a pre-compiled C library within Rust - c

Long story short, I want to call C code from Rust... I've seen crates like cc and cmake which (to my understanding, which might be wrong) can compile C code for use with the Rust language. Though I am capable of producing a compiled .lib file using the CLion IDE. Now I would like to simply link to said .lib file from Rust and just call the code within, is this possible? I have tried a few things, but they just do not seem to link at all...
project structure:
my_rust_project/
my_c_project/
cmake_build_debug/
...
my_c_project.lib
...
...
a_c_file.h
a_c_file.c
...
src/
main.rs
cargo.roml
build.rs
...
build.rs:
fn main() {
println!("cargo:rustc-link-search=my_c_project/cmake-build-debug/");
println!("cargo:rustc-link-lib=static=my_c_project/cmake-build-debug/my_c_project");
}
main.rs:
#[link(name = "my_c_project/cmake-build-debug/my_c_project", kind = "static")]
extern "C" {
fn hello();
}
fn main() {
unsafe {
//hello(); /* if commented the project compiles... */
}
}
Note how you could comment out any of the two prinln!'s in build.rs as well as the #link directive from main.rs in any combination you could think of (i.e. commenting out #link and leaving the println's, or commenting out one println and leaving the #link, and so on...) and the project will continue to compile, though as soon as you uncomment hello(); from fn main() in main.rs, the project will not compile and produce the following error:
LNK2019: unresolved external symbol hello referenced in function blah blah blah ...
I think there is two possible causes, either the library is not link at all, which seems to be the most logical issue to me, which raises the question: what exactly do the println!'s and #link do in this specific scenarion, they do not cause a panic! or error so they must definitly be doing something right? Whatever it is, it cannot be linking the project.
The other issue I can imagine is that there is in fact proper linking going on-ish? But Rust just doesn't understand where exactly to find the function hello within my_c_project.lib...
For clarity I will provide the contents of my_c_project:
a_c_file.h:
#ifndef MY_C_PROJECT_A_C_FILE_H
#define MY_C_PROJECT_A_C_FILE_H
void hello();
#endif //MY_C_PROJECT_A_C_FILE_H
a_c_file.c:
#include "a_c_file.h"
#include <stdio.h>
void hello() {
printf("Hello, World!\n");
}
I really want to stress that I want to stay away from cc and cmake (crates) since I have the .lib file ready to go... The question remains, how to call the C functions from a .lib file from within a Rust program?
Thanks for the help!
newly tried thing...
changed build.rs to:
fn main() {
println!("cargo:rustc-link-search=my_c_project/cmake-build-debug/");
println!("cargo:rustc-link-lib=static=my_c_project");
}
changed main.rs to:
extern {
fn hello();
}
fn main {
}
renamed my_c_project.lib to libmy_c_project.lib...
This still produces an error:
LINK : fatal error LNK1181: cannot open input file 'libmy_library.lib'
I guess it does not require a .a file per se as it says .lib in the message above.

If anyone ever has this problem... I solved this by continuing the project on Linux using cross compilers to generate the executable for windows.
The matter is to compile everything (source code and libraries/dependencies) for the target platform using the correct compilers ofcourse.

Related

Why linking .cc file works in make file but linking .c file doesn't?

I'm working on a quite large Makefile from the tensorflow repo and I need to add a file link.
After quite some debugging of a link error, I found out that if my file ends with .cc, then the link error disappears, whereas when linking a .c file, the error appears (file content remains the same).
I am linking the file in a Makefile.inc file:
.
.
.
FL_SRCS := \
tensorflow/lite/vis_mi/main.cc \
myFunctions.c \ -->>>>IF I CHANGE THE FILENAME TO myFunctions.cc and link to this .cc file here, it works!!
.
.
.
# Builds a standalone binary.
$(eval $(call vis_test,vis_mi,\
$(FL_SRCS),$(FL_HDRS)))
The link error when using the .c ending ends with:
tensorflow/lite/vis_mi/main.o: In function `main':
tensorflow/lite/vis_mi/main.cc:183: undefined reference to `printMsg()'
../downloads/gcc_embedded/bin/../lib/gcc/arm-none-eabi/7.3.1/../../../../arm-none-eabi/bin/ld: link errors found, deleting executable `tensorflow/lite/vis_mi'
collect2: error: ld returned 1 exit status
gmake: *** [tensorflow/lite/vis_mi/Makefile.inc:578: tensorflow/lite/vis_mi/bin/micro_speech] Error 1
The .c file code:
#include <stdio.h>
#include "myFunctions.h"
void printMsg(){
//do something here
}
And the header file:
#ifndef MYFUNCTIONS_H
#define MYFUNCTIONS_H
void printMsg();
#endif /* MYFUNCTIONS_H */
How can I include a file ending with .c? Makefiles are fairly new to me, and didn't want to include all the details, and if you need any further details from the Makefile to answer this question I'm happy to edit my post.
The name resolution for C and C++ functions is different. This is sometimes causing problem to "C" code that is also valid "C++". For example:
int foo(int x) { return x+1 } ;
The code Will create a function called 'foo' when compiled as "C", and will create a function called '_Z3fooi' when compiled as C++. Decoding the C++ name will (using c++filt) will show foo(int). The reason for the difference is that C++ support polymorphic functions (and methods), so that function names also identify the type of their parameters.
The proper solution is to decide on the language of the code. C++ and C are different language, and while it's possible to write code that will be valid for both, it will limit the ability to leverage each function abilities.
Important constraint: If a project contain both C and C++ code, it is important to remember that only functions that follow the "C" conventions can be called between the languages. This is usually implemented with extern "C" directive, and with #ifdef __cplusplus:
See more: https://www.thegeekstuff.com/2013/01/mix-c-and-cpp/ How to call C++ function from C? Call a C function from C++ code

Using Go code in an existing C project

Ever since Go 1.5 came out, I started taking another look at how I could integrate it into an existing project of mine.
The project's codebase is written entirely in C for low level access to to hardware and other fun stuff. However, some of the higher level things are tedious, and I would like to start writing them in a higher level language (Go)
Is there any way I can call Go code from a C program? I installed Go 1.5, which added -buildmode=c-archive (https://golang.org/s/execmodes) which I am trying to get working.
However, I can't seem to get Go to generate the appropriate header files to allow my project to actually compile. When I generate the archive, I see the function in the exported symbols (using objdump), but without the header files to include gcc complains about the function not existing (as expected)
I'm quite new to Go - however, I love the language and would like to make use of it. Is there any idiomatic way ("idiomatic" gets used a lot in the world of Go I see...) to get this to play nicely with each other?
The reason I asked this question and specifically mentioned Go 1.5 is that according to this document, https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit?pli=1#heading=h.1gw5ytjfcoke
Go 1.5 added support for non-Go programs to call Go code. Specifically, mentioned under the section "Go code linked into, and called from, a non-Go program"
To build an archive callable from C, you will need to mark them as exported CGo symbols.
For example, if I create a file foo.go with the following contents:
package main
import (
"C"
"fmt"
)
//export PrintInt
func PrintInt(x int) {
fmt.Println(x)
}
func main() {}
The important things to note are:
The package needs to be called main
You need to have a main function, although it can be empty.
You need to import the package C
You need special //export comments to mark the functions you want callable from C.
I can compile it as a C callable static library with the following command:
go build -buildmode=c-archive foo.go
The results will be an archive foo.a and a header foo.h. In the header, we get the following (eliding irrelevant parts):
...
typedef long long GoInt64;
...
typedef GoInt64 GoInt;
...
extern void PrintInt(GoInt p0);
...
So that's enough to call the exported function. We can write a simple C program that calls it like so:
#include "foo.h"
int main(int argc, char **argv) {
PrintInt(42);
return 0;
}
We can compile it with a command like:
gcc -pthread foo.c foo.a -o foo
The -pthread option is needed because the Go runtime makes use of threads. When I run the resulting executable it prints 42.
The code above work just fine, but gcc will complain about functions and headers.
The includes should be:
#define _GNU_SOURCE
#include <stdio.h>
#include "mygopkg.h"
If you forget the #define _GNU_SOURCE, the gcc will complain:
warning: implicit declaration of function 'asprintf'; did you mean 'vsprintf'? [-Wimplicit-function-declaration]
If you forget the #include "mygopkg.h", the gcc will complain:
warning: implicit declaration of function 'PrintString' [-Wimplicit-function-declaration]
The last but not less important. The build command line I recommend for production code is:
go build -ldflags "-s -w" -buildmode c-archive -o mygopkg.a
It'll save you 53% size of final mygopkg.a.

Linking to a DLL I created

I am trying to create my own DLL and then make another project load it statically.
My DLL file contains both a header file (called HelloFunc.h):
#include <stdio.h>
extern "C"
{
_declspec(dllexport) void HelloFromDll();
}
And a c file (called HelloFunc.cpp):
#include <stdio.h>
extern "C"
{
_declspec(dllexport) void HelloFromDll()
{
printf("Hello DLL. \n");
}
}
After building the project an Object File Library (.lib) was created.
Then, on my other project I tried to link to it statically.
In linker -> Input -> Additional Dependencies I added my library (I put it in my new project's directory) and then in linker -> Input -> Command Line I saw that it actually linked to it.
However, when I tried to call HelloFromDll() function in my new code, an error says that it is not identified. Note that I also included "HelloFunc.h" but an error says that the source file couldn't be opened.
I'm a little lost and don't know what I've done wrong. Any help will be appreciated :)
You must specify __declspec(dllimport) instead of __declspec(dllexport) when importing a library.
What error message did you received exactly?
[Edited]
When you compile a DLL, you specify __declspec(dllexport). When you compile an application that imports the DLL, you specify __declspec(dllimport).
The problem is that the compiler cannot find HelloFunc.h: Simply copy HelloFunc.h into your new project's directory.
Check that you client & library were compiled in the same mode (debug or release).
This is common bottleneck.

Call method from source file in another directory

I have a newbie question about the C programming language. I have looked around to find the answer in similar questions but I failed to figure it out.
Assume a simple project consisting of two dirs: src and test. The source and header files are defined by src/main.c, test/foo.h and test/foo.c.
src/main.c:
#include "../test/foo.h"
int main (void) {
int a = VAR; /* works, recognizes declared macro */
some_function(a); /* doesn't work, "undefined reference" */
}
test/foo.h:
#ifndef FOO_H
#define FOO_H
void some_function(int a);
#define VAR 2;
#endif
test/foo.c (redundant but to be complete):
#include "foo.h"
#include <stdlib.h>
void some_function(int a) {
printf("%d", ++a);
}
I created the project in Eclipse and I also compile with it, I figured it wasn't a linking error since the macro gets recognized but the method is not callable.
The reason why I'm using different directories is because I have a lot of files and would like my test code to be separate from my main source code. Note that src and test have the same parent directory.
Any ideas what's going on here? Am I missing something very obvious?
Any help would be much appreciated, thanks in advance!
edit: I'm working on a (Debian) Linux machine and Eclipse uses the gcc compiler.
edit2: Thanks to H2CO3's answer I learned it is indeed a linking error. Since compiling and linking manually every time is quite an overhead, I was wondering if anyone knows how to teach Eclipse to link executables from different directories?
--------------------- SOLUTION ---------------------
edit3: Lol the solution was very easy after all, all I had to do was create a "new source folder" rather than a "new folder". I feel stupid but thanks to you all for replying, H2CO3 in particular!
I figured it wasn't a linking error since the macro gets recognized but the method is not callable.
Non sequitur. Macros are expanded in the preprocessing phase. (And as such, they have nothing to do with linkage at all.) You do have a linker error.
What you have to do is compile both files then link them together, so something like this should work:
gcc -Wall -o dir_one/foo.o dir_one/foo.c
gcc -Wall -o dir_two/bar.o dir_two/bar.c
gcc -o my_program dir_one/foo.o dir_two/bar.o
Also, read this SO question/answer and/or this article to understand how the steps of the compilation process work together. (These are almost the same for C and C++, it's only the name mangling that usually differs.)

Include c file in another

I want to include a .c file in another. Is it possible right? It works well if I include a header file in a .c file, but doesn't work as well if I include a .c file in another .c file.
I am using Visual Studio and I get the following error:
main.obj : error LNK2005: _sayHello already defined in sayHello.obj
/* main.c */
#include "sayHello.c"
int main()
{
return 0;
}
/* sayHello.c */
#include <stdio.h>
void sayHello()
{
printf("Hello World");
}
I don't know what this error could mean. Time to ask more advanced C coders. :)
I want to include a .c file in another.
No you don't. You really, really don't. Don't take any steps down this path; it only ends in pain and misery. This is bad practice even for trivial programs such as your example, much less for programs of any real complexity. A trivial, one-line change in one file will require you to rebuild both that file and anything that includes it, which is a waste of time. You lose the ability to control access to data and functions; everything in the included .c file is visible to the including file, even functions and file scope variables declared static. If you wind up including a .c file that includes another .c file that includes another .c file und so weiter, you could possibly wind up with a translation unit too large for the compiler to handle.
Separate compilation and linking is an unequivocal Good Thing. The only files you should include in your .c files are header files that describe an interface (type definitions, function prototype declarations, macro defintions, external declarations), not an implementation.
It works, but you need to be careful with how you build the program. Also, as folks have pointed out in comments, it's generally considered a bad idea. It's unexpected, and it creates problems like these. There are few benetfits, especially for what seems like a trivial program. You should probably re-think this approach, altogether.
After doing something like this, you should only compile main.c, and not attempt to link it with the result of compiling sayHello.c, which you seem to be doing.
You might need to tell Visual Studio to exclude the latter file from the build.
Yes, any '.c' file can be included into another program.
As one include '.h' file like 'stdio.h' in the program.
After that we can call those function written into this external file.
test.c::
#include<stdio.h>
#include<conio.h>
void xprint()
{
printf("Hello World!");
}
main.c::
#include "test.c"
void main()
{
xprint();
getch();
}
Output:: Hello World!
This is a linker error. After compiling all your .c files to .obj files (done by the compiler) the linker "merges" them together to make your dll/exe. The linker has found that two objects declare the same function (which is obviously not allowed). In this case you would want the linker to only process main.obj and not sayhello.obj as well (as its code is already included in main.obj).
This is because in main.obj you will ALSO have the sayHello() function due to the include!
sayHello.h
#include <stdio.h>
void sayHello(void);
main.c
#include "sayHello.h"
int main()
{
sayHello();
return 0;
}
You probably defined two times this function
void sayHello()
{
printf("Hello World");
}
one in your current file and one in sayhello.c
You must remove one definition.
The problem seems to be that sayHello.c got compiled into an object file called sayHello.obj, and main.c (also including all the source text from sayHello.c), got compiled into main.obj, and so both .obj files got a copy of all the external symbols (like non-static function definitions) from sayHello.c.
Then all the object files were supposed to get "linked" together to produce the final executable program file, but the linking breaks when any external symbols appear more than once.
If you find yourself in this situation, you need to stop linking sayHello.obj to main.obj (and then you might not want to compile sayHello.c by itself into its own object file at all).
If you manually control every step of the build (like you might when using the CLI of your compiler), this is often just a matter of excluding that object file from the invocation of the linker or compiler.
Since you are using Visual Studio, it's probably making a bunch of assumptions for you, like "every .c file should be compiled into its own object file, and all those object files should be linked together", and you have to find a way to circumvent or disable this assumption for sayHello.c.
One easy and somewhat idiomatic solution might be to rename sayHello.c into sayHello.c.inc.
//THIS IS THE MAIN FILE//
#include "test.c"
int main()
{
multi(10);
}
//THIS IS THE TEST FILE//
#include<stdio.h>
void multi(int a)
{
printf("%d",a*2);
}
POINTS TO BE NOTED:
Here you need to run the program which contains "main()" function.
You can avoid the "stdio.h" header file in main function. Because, you are including the file which already contains the "stdio.h" header file.
You have to call the function from the "main" file.
The including file should be "file_name.c" not "file_name.h". Because usually we use .h extension for the header file. Since we are including another program file and not the header file, we have to use .c. Otherwise it will give you Fatal Error and the Compilation gets terminated.

Resources