I am testing CXX with a very simple project to link a Rust library into a C++ executable.
I write a foo() -> () Rust function and try to access it from C++ but the linker does not find it.
Here's what I have:
// lib.rs
#[cxx::bridge]
mod ffi {
extern "Rust" {
pub fn foo() -> ();
}
}
pub fn foo() -> () {
println!("foo")
}
# Cargo.toml
[package]
name = "cpprust"
version = "0.1.0"
edition = "2021"
[lib]
name = "cpprust"
path = "src/lib.rs"
crate-type = ["staticlib", "rlib", "dylib"] # EDIT: this is incorrect, see note at the end of question
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cxx = "1.0"
// main.cpp
void foo(); // I tried including lib.rs.h but it was not generated!
int main() {
foo();
}
Running cargo build generates target\debug\libcpprust.so.
I then try to make the project with (EDIT: g++ command is incorrect, see note at the end of question):
g++ -L../target/debug/ -lcpprust -o cpprust main.cpp
/tmp/ccOA8kJy.o: In function `main':
main.cpp:(.text+0x5): undefined reference to `foo()'
collect2: error: ld returned 1 exit status
make: *** [Makefile:2: cpprust] Error 1
What is wrong here?
EDIT: prog-fh's great answer correctly points out that I need to include build.rs with C++ compilation, even without having C++ to compile and access within the crate. However, even after implementing their answer, I was still getting the same error message. It turns out that I had two other problems: 1) the order of my arguments to g++ were incorrect, and I needed pthread -l dl as well. It should have been:
g++ -o cpprust main.cpp -I ../target/cxxbridge -L../target/debug -lcpprust -pthread -l dl
2) My Cargo.toml file was also generating "rlib", "dylib" library types, but that somehow also causes the error above; it works when only staticlib is generated.
Considering this documentation, the build.rs script should generate the lib.rs.h which was missing in your attempt.
Note that the example in the documentation considers that the main program comes from Rust, and that the C++ code is an extension.
In your question, it is the opposite situation: your main program comes from C++ but is extended by some Rust code.
This answer is made of two parts:
a minimal example very similar to yours (no C++ code to be called from Rust),
a more complete example with a bidirectional interaction between C++ and Rust (but the main program still is on the C++ side).
edit to answer subsequent questions in the comments
As said in the comment of the second build.rs below, the name chosen in .compile("cpp_from_rust") will be used to name a library containing the compiled C++ code (libcpp_from_rust.a for example).
This library will then be used by Rust to extend the Rust code: the libcpprust.a main target produced by Rust contains libcpp_from_rust.a.
If no C++ file is provided before .compile() (as in the first, minimal example below), this C++ library only contains the symbols enabling extern "Rust" access from C++.
$ nm ./target/debug/build/cpprust-28371278e6cda5e2/out/libcpp_from_rust.a
lib.rs.o:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T _Z13rust_from_cppv
U cxxbridge1$rust_from_cpp
On the other hand, you already found in the documentation that multiple invocations of .file() are allowed in order to provide the C++ library with much more code from various source files.
Another question was about the kind of library we want Rust to produce.
This documentation enumerates the various binary targets Rust can produce, especially various kinds of libraries.
Since in your original question you wanted the main executable to be on the C++ side, this means that Rust should produce a library which can be considered as a system library, not a Rust specific one, because Rust won't be involved anymore when generating the executable.
In the aforementioned documentation, we can see that only staticlib and cdylib are suitable for this usage.
In my examples, I chose staticlib for the sake of simplicity, but cdylib can be used too.
However, it is a bit more complicated because, as the main library (libcpprust.so) is generated as a dynamic one, Rust does not insert the C++ library (libcpp_from_rust.a) into it; thus, we have to link against this C++ library, which is not very convenient.
g++ -std=c++17 -o cpp_program src/main.cpp \
-I .. -I target/cxxbridge \
-L target/debug -l cpprust \
-L target/debug/build/cpprust-28371278e6cda5e2/out -l cpp_from_rust \
-pthread -l dl
And of course, because we are now dealing with a shared library, we have to find it at runtime.
$ LD_LIBRARY_PATH=target/debug ./cpp_program
I don't know whether some other kinds of libraries (crate-type) could work (by chance) with this C++ main program or not, but the documentation states that only staticlib and cdylib are intended for this usage.
Finally, note that if you use crate-type = ["staticlib", "rlib", "dylib"] in Cargo.toml (as stated in your comment), you will produce three libraries:
target/debug/libcpprust.a from staticlib,
target/debug/libcpprust.rlib from rlib,
target/debug/libcpprust.so from dylib.
Unfortunately, when linking with the command g++ ... -l cpprust ..., the linker will prefer the .so to the .a; you will be in the same situation as cdylib above.
The layout of the directory for the minimal example
cpprust
├── Cargo.toml
├── build.rs
└── src
├── lib.rs
└── main.cpp
Cargo.toml
[package]
name = "cpprust"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["staticlib"]
[dependencies]
cxx = "1.0"
[build-dependencies]
cxx-build = "1.0"
build.rs
fn main() {
// This will consider the ffi part in lib.rs in order to
// generate lib.rs.h and lib.rs.cc
// minimal example: no C++ code to be called from Rust
cxx_build::bridge("src/lib.rs")
.compile("cpp_from_rust");
}
src/lib.rs
#[cxx::bridge]
mod ffi {
extern "Rust" {
fn rust_from_cpp() -> ();
}
}
pub fn rust_from_cpp() -> () {
println!("called rust_from_cpp()");
}
src/main.cpp
/*
Building this program happens outside of the cargo process.
We simply need to link against the Rust library and the
system libraries it depends upon
g++ -std=c++17 -o cpp_program src/main.cpp \
-I .. -I target/cxxbridge \
-L target/debug -l cpprust \
-pthread -l dl
*/
// consider the ffi part of Rust code
#include "cpprust/src/lib.rs.h"
#include <iostream>
int
main()
{
std::cout << "starting from C++\n";
rust_from_cpp();
std::cout << "finishing with C++\n";
return 0;
}
The cargo build command will generate the libcpprust.a static library in target/debug.
Building the main program simply relies on usual commands, provided that we find the relevant headers and libraries (see the comments in the code).
Note that the C++ source code for the main program is in the src directory here, but it could have been put anywhere else.
The layout of the directory for the bidirectional example
cpprust
├── Cargo.toml
├── build.rs
└── src
├── cpp_from_rust.cpp
├── cpp_from_rust.hpp
├── lib.rs
└── main.cpp
We just added a pair of .hpp/.cpp files.
build.rs
fn main() {
// This will consider the ffi part in lib.rs in order to
// generate lib.rs.h and lib.rs.cc
// The generated library (libcpp_from_rust.a) contains the code
// from cpp_from_rust.cpp and will be inserted into the generated
// Rust library (libcpprust.a).
cxx_build::bridge("src/lib.rs")
.file("src/cpp_from_rust.cpp")
.flag_if_supported("-std=c++17")
.compile("cpp_from_rust");
}
Note that this time the build process actually handles some C++ code (see below) to be called from Rust.
src/lib.rs
#[cxx::bridge]
mod ffi {
extern "Rust" {
fn rust_from_cpp() -> ();
}
unsafe extern "C++" {
include!("cpprust/src/cpp_from_rust.hpp");
fn cpp_from_rust() -> ();
}
}
pub fn rust_from_cpp() -> () {
println!("entering rust_from_cpp()");
ffi::cpp_from_rust();
println!("leaving rust_from_cpp()");
}
src/cpp_from_rust.hpp
#ifndef CPP_FROM_RUST_HPP
#define CPP_FROM_RUST_HPP
// declare a usual C++ function (no Rust involved here)
void
cpp_from_rust();
#endif // CPP_FROM_RUST_HPP
src/cpp_from_rust.cpp
#include "cpp_from_rust.hpp"
#include <iostream>
// define a usual C++ function (no Rust involved here)
void
cpp_from_rust()
{
std::cout << "called " << __func__ << "()\n";
}
Cargo.toml, src/main.cpp and the build process (cargo build, g++ ...) still are the same as in the previous example.
I am trying to compile a small project which references a static library (.a) in /usr/local/lib and it's header (.h) files in /usr/local/include/test into a standalone wasm file. The following code runs fine with no compilation errors.
main.cpp
#include <iostream>
#include "libtest/libtest.h"
int main(int argc, char *argv[]) {
LibTest Test;
std::cout << Test.version() << std::endl;
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(test_project)
set(CMAKE_CXX_STANDARD 14)
include_directories(/usr/local/include)
link_directories(/usr/local/lib)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} test)
However, when I compile into wasm using the following command:
emcc main.cpp -I/usr/local/include -Os -s WASM=1 -s SIDE_MODULE=1 -o main.wasm
I get the error: Module not found: Can't resolve 'env' when importing the wasm file into the client application.
Here's how I'm importing it:
const Component = dynamic({
loader: async () => {
const module = await import("../../main.wasm");
},
ssr: false,
});
NOTE: This will work fine if remove the imported library and do just about anything else (e.g. the Hello World or add(x, y) function examples.)
Anyone ran into this problem?
I'm assuming that the emcc command I am running is failing to compile the library correctly.
You are compiling it into pure Wasm but the C++ code contains functions without a Wasm equivalent; they have to be replaced with JavaScript equivalents and "imported" into the Wasm.
This is normally done by Emscripten but you are telling it not to do this when you specify that you want a standalone Wasm file.
I want to use the DirectSound API (from DirectX) to process the audio Windows outputs. To do so, I created "libdsound.a" as described in the following paragraphs, because it seems like I cannot use .dll s or .lib s for the MinGW g++ compiler.
I installed the DirectX SDK and copied the dsound.lib to a folder called "directsound". From the mingw-w64 repository, I downloaded dsound.h and dsound.def to the directory "directsound" as well (all files I used were for x86).
Following this guide I used the dlltool command line to create the libdsound.a:
dlltool -d dsound.def -l libdsound.a
In CLion, I set up CMake in CMakeLists.txt the following way:
cmake_minimum_required(VERSION 3.9)
project(fourier)
set(CMAKE_CXX_STANDARD 11)
link_directories(PATHTOTHISDIR/directsound)
include_directories(PATHTOTHISDIR/directsound)
add_executable(fourier main.cpp)
target_link_libraries(fourier dsound)
CLion says the CMakeLists.txt file is fine. Every time I try to compile the program, building and linking seems to work fine, but then I get the following error message:
[ 50%] Building CXX object CMakeFiles/fourier.dir/main.cpp.obj
[100%] Linking CXX executable fourier.exe
CMakeFiles\fourier.dir/objects.a(main.cpp.obj): In function `main':
PATHTOPROJECT/main.cpp:11: undefined reference to `DirectSoundCreate8'
collect2.exe: error: ld returned 1 exit status
...
This is how main.cpp (the only source file) looks like:
#pragma comment(lib, "libdsound.a")
#include <windows.h>
#include <dsound.h>
int main() {
LPDIRECTSOUND8 directsound;
DirectSoundCreate8(NULL, &directsound, NULL);
WAVEFORMATEX waveformat;
// ....
EDIT Was able to resolve it thanks to the advice of VTT. The way I created the libdsound.a was wrong. It should've been done like this:
dlltool -D dsound.dll -d dsound.def -l libsound.a -k
I should have also created the .def file on my own.
reimp -c -d dsound.lib
The dsound.dll should be placed in the same directory as the executable.
I have a R package that I'm working on that contains code written in C and C++ under the src folder. Currently, the package compiles and works on Rstudio as it follows the default directory structure. As the project builds, I want to be able to organize my code under src, within subfolders. Following directions from "Writing R extensions" - Compiling under sub-directories, I have made a folder called 'test'(/src/test) which now contains all my files(*.c, *.cpp, *.h) and modified my Makevars like so -
SOURCES_C = $(wildcard test/*.c)
SOURCES_CPP = $(wildcard test/*.cpp)
PKG_CPPFLAGS= -I${R_HOME}/include -I.
PKG_LIBS = -L${R_HOME}/lib
CXX_STD = CXX11
OBJECTS =$(SOURCES_CPP:.cpp=.o) $(SOURCES_C:.c=.o)
all : $(SHLIB)
#PKG_CFLAGS= -Wall
clean : rm -f *.o
I want to be able to compile the program in this state, where the C/C++ files are under subfolders inside src. Using the aforementioned Makevars -> the separate object files are being built from the test folder with the correct flags and compiler, for all C/CPP files. However, there are some discrepancies with the build command for the shared object. This is the log when compiling the files under src/test which fails with an undefined symbol error.
gcc -std=gnu99 -shared -L/usr/local/lib64 -o BioCro.so test/BBox.o test/Climate.o test/Compound.o test/Grid.o test/LeafOptics.o test/Maths.o test/Normal.o test/Point3D.o test/Ray.o test/Triangle.o test/Vector3D.o test/runFastTracer.o test/Assigncropcent.o test/AuxBioCro.o test/AuxCropGro.o test/AuxMaizeGro.o test/AuxcaneGro.o test/Auxcropcent.o test/AuxwillowGro.o test/BioCro.o test/CalculateBiogeochem.o test/Calculate_Soil_Layer_Temperature.o test/CanA.o test/CanAC_3D.o test/Century.o test/Copy_CropCent_To_DayCent_Structure.o test/Copy_SoilWater_BioCro_To_CropCent.o test/CropGro.o test/CropGro_c.o test/Filling_BioCro_SoilStructure.o test/assignManagement.o test/c3CanA.o test/c3EvapoTrans.o test/c3photo.o test/c4photo.o test/caneGro.o test/createNULLc3tree.o test/cropcent.o test/dailywillow.o test/denitrify.o test/diffusiv.o test/eC4photo.o test/getIdirIdiff.o test/getsoilprop.o test/leachdly.o test/maizeGro.o test/methane.o test/microclimate_for_3Dcanopy.o test/nitrify.o test/nox_pulse.o test/pi_funcs.o test/printcropcentoutput.o test/test_mainC.o test/tgmodel.o test/tracegas.o test/update_3Dcanopy_structure.o test/wfps.o test/willowCent.o test/willowGro.o -L/usr/local/R-3.1.0/lib64/R/lib -L/usr/local/R-3.1.0/lib64/R/lib -lR
installing to /home/vashist1/R/x86_64-unknown-linux-gnu-library/3.1/BioCro/
** R
** data
** inst
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded
Error in dyn.load(file, DLLpath = DLLpath, ...) :
unable to load shared object '/home/vashist1/R/x86_64-unknown-linux-gnu-library/3.1/BioCro/libs/BioCro.so':
/home/vashist1/R/x86_64-unknown-linux-gnu-library/3.1/BioCro/libs/BioCro.so:
undefined symbol: _ZTVN10__cxxabiv117__class_type_infoE
Error: loading failed
Compared with the successful log which builds successfully!
g++ -shared -L/usr/local/lib64 -o BioCro.so Assigncropcent.o AuxBioCro.o AuxCropGro.o AuxMaizeGro.o AuxcaneGro.o Auxcropcent.o AuxwillowGro.o BBox.o BioCro.o CalculateBiogeochem.o Calculate_Soil_Layer_Temperature.o CanA.o CanAC_3D.o Century.o Climate.o Compound.o Copy_CropCent_To_DayCent_Structure.o Copy_SoilWater_BioCro_To_CropCent.o CropGro.o CropGro_c.o Filling_BioCro_SoilStructure.o Grid.o LeafOptics.o Maths.o Normal.o Point3D.o Ray.o Triangle.o Vector3D.o assignManagement.o c3CanA.o c3EvapoTrans.o c3photo.o c4photo.o caneGro.o createNULLc3tree.o cropcent.o dailywillow.o denitrify.o diffusiv.o eC4photo.o getIdirIdiff.o getsoilprop.o leachdly.o maizeGro.o methane.o microclimate_for_3Dcanopy.o nitrify.o nox_pulse.o pi_funcs.o printcropcentoutput.o runFastTracer.o test_mainC.o tgmodel.o tracegas.o update_3Dcanopy_structure.o wfps.o willowCent.o willowGro.o -L/usr/local/R-3.1.0/lib64/R/lib -lR
installing to /home/vashist1/R/x86_64-unknown-linux-gnu-library/3.1/BioCro/libs
1) The shared object compiles using g++ under default conditions, whereas under subdirectory conditions the compiler used is gcc. Can I change that behaviour via Makevars?
2) Further research allowed me to find that the undefined symbol error is a linking error fixed by the -L/-l flag. However, the -L flag is the same for both build commands. Is there any other library I am failing to link which is linked by default?
I ran into the same issue. Looking at the example of the RSiena package mentioned as an example in "Writing R Extensions" section 1.2.1.3 I noticed that that package still has some .cpp files not in a subdirectory. So I added a dummy.cpp file in src/ with the following contents:
void dummy (void)
{
}
After this g++ was correctly used for the linking step and the .so file was created as expected.
In my case it turns out that I don't need to change the Makevars files as I first mentioned in my answer. Even without the change below (so only having the dummy.cpp file present in src/) linking is done correctly.
I'll leave it in in case it may help someone else with a (slightly) different setup.
And add the corresponding .o file in the list of $(OBJECTS) variable in the Makevars file:
OBJECTS = $(SOURCES:.cpp=.o) dummy.o
I'd like to share a static/global variable only between a process and a dll that is invoked by the process. The exe and dll are in the same memory address space. I don't want the variable to be shared among other processes.
Elaboration of the problem:
Say that there is a static/global variable x in a.cpp. Both the exe foo.exe and the dll bar.dll have a.cpp, so the variable x is in both images.
Now, foo.exe dynamically loads (or statically) bar.dll. Then, the problem is whether the variable x is shared by the exe and dll, or not.
In Windows, these two guys never share the x: the exe and dll will have a separate copy of x. However, in Linux, the exe and dll do share the variable x.
Unfortunately, I want the behavior of Linux. I first considered using pragma data_seg on Windows. However, even if I correctly setup the shared data segment, foo.exe and bar.dll never shares the x. Recall that bar.dll is loaded into the address space of foo.exe. However, if I run another instance of foo.exe, then x is shared. But, I don't want x to be shared by different processes. So, using data_seg was failed.
I may it use a memory-mapped file by making an unique name between exe and dll, which I'm trying now.
Two questions:
Why the behavior of Linux and Windows is different? Can anyone explain more about this?
What would be most easiest way to solve this problem on Windows?
To get the behavior of linux where both the main program and a dll share the same x, you can export that variable from either the dll, or the main program. The other module must import that variable.
You do this by using DEF files (see microsoft's documentation), or by marking the uses with the variable with __declspec(dllexport) where it's defined, and __declspec(dllimport) in any other module it's used (see microsoft's documentation). This is the same as how any function, object, or variable is shared between modules in windows.
In the case where you'd like a program to load a library at runtime, but the main program may have to use the variable before the library is loaded, the program should export the variable, and the dll should import it. There is a little bit of a chicken and egg problem here because the dll depends on the main program, and the main program depends on the dll. See http://www.lurklurk.org/linkers/linkers.html#wincircular
I've written an example of how you can do this using both Microsoft's compiler and mingw (gcc in windows), including all the different ways a program and a library can link to each other (statically, dll loaded at program start, dll loaded during runtime)
main.h
#ifndef MAIN_H
#define MAIN_H
// something that includes this
// would #include "linkage_importing.h"
// or #include "linkage_exporting.h"
// as appropriate
#ifndef EXPLICIT_MAIN
LINKAGE int x;
#endif // EXPLICIT_MAIN
#endif // MAIN_H
main.c
#ifdef EXPLICIT_DLL
#include "dyn_link.h"
#endif // EXPLICIT_DLL
#include <stdio.h>
#include "linkage_exporting.h"
#include "main.h"
#include "linkage_importing.h"
#include "dll.h"
FNCALL_DLL get_call_dll(void);
int main(int argc, char* argv[])
{
FNCALL_DLL fncall_dll;
fncall_dll = get_call_dll();
if (fncall_dll)
{
x = 42;
printf("Address of x as seen from main() in main.c: %p\n", &x);
printf("x is set to %i in main()\n", x);
fncall_dll();
// could also be called as (*fncall_dll)();
// if you want to be explicit that fncall_dll is a function pointer
printf("Value of x as seen from main() after call to call_dll(): %i\n", x);
}
return 0;
}
FNCALL_DLL get_call_dll(void)
{
#ifdef EXPLICIT_DLL
return get_ptr("dll.dll", "call_dll");
#else
return call_dll;
#endif // EXPLICIT_DLL
}
dll.h
#ifndef DLL_H
#define DLL_H
// something that includes this
// would #include "linkage_importing.h"
// or #include "linkage_exporting.h"
// as appropriate
// declaration of type to hold a
// pointer to the function
typedef void(*FNCALL_DLL)(void);
#ifndef EXPLICIT_DLL
LINKAGE void call_dll(void);
#endif // EXPLICIT_DLL
#endif // DLL_H
dll.c
#ifdef EXPLICIT_MAIN
#include "dyn_link.h"
#endif // EXPLICIT_MAIN
#include <stdio.h>
#include "linkage_importing.h"
#include "main.h"
#include "linkage_exporting.h"
#include "dll.h"
int* get_x_ptr(void);
LINKAGE void call_dll(void)
{
int* x_ptr;
x_ptr = get_x_ptr();
if (x_ptr)
{
printf("Address of x as seen from call_dll() in dll.c: %p\n", x_ptr);
printf("Value of x as seen in call_dll: %i()\n", *x_ptr);
*x_ptr = 31415;
printf("x is set to %i in call_dll()\n", *x_ptr);
}
}
int* get_x_ptr(void)
{
#ifdef EXPLICIT_MAIN
return get_ptr("main.exe", "x"); // see note in dyn_link.c about using the main program as a library
#else
return &x;
#endif //EXPLICIT_MAIN
}
dyn_link.h
#ifndef DYN_LINK_H
#define DYN_LINK_H
// even though this function is used by both, we link it
// into both main.exe and dll.dll as necessary.
// It's not shared in a dll, because it helps us load dlls :)
void* get_ptr(const char* library, const char* object);
#endif // DYN_LINK_H
dyn_link.c
#include "dyn_link.h"
#include <windows.h>
#include <stdio.h>
void* get_ptr(const char* library, const char* object)
{
HINSTANCE hdll;
FARPROC ptr;
hdll = 0;
ptr = 0;
hdll = LoadLibrary(library);
// in a better dynamic linking library, there would be a
// function that would call FreeLibrary(hdll) to cleanup
//
// in the case where you want to load an object in the main
// program, you can use
// hdll = GetModuleHandle(NULL);
// because there's no need to call LoadLibrary on the
// executable if you can get its handle by some other means.
if (hdll)
{
printf("Loaded library %s\n", library);
ptr = GetProcAddress(hdll, object);
if (ptr)
{
printf("Found %s in %s\n", object, library);
} else {
printf("Could not find %s in %s\n", object, library);
}
} else {
printf("Could not load library %s\n", library);
}
return ptr;
}
linkage_importing.h
// sets up some macros to handle when to use "__declspec(dllexport)",
// "__declspec(dllimport)", "extern", or nothing.
// when using the LINKAGE macro (or including a header that does):
// use "#include <linkage_importing.h>" to make the LINKAGE macro
// do the right thing for importing (when using functions,
// variables, etc...)
//
// use "#include <linkage_exporting.h>" to make the LINKAGE macro
// do the right thing for exporting (when declaring functions,
// variables, etc).
//
// You can include either file at any time to change the meaning of
// LINKAGE.
// if you declare NO_DLL these macros do not use __declspec(...), only
// "extern" as appropriate
#ifdef LINKAGE
#undef LINKAGE
#endif
#ifdef NO_DLL
#define LINKAGE extern
#else
#define LINKAGE extern __declspec(dllimport)
#endif
linkage_exporting.h
// See linkage_importing.h to learn how this is used
#ifdef LINKAGE
#undef LINKAGE
#endif
#ifdef NO_DLL
#define LINKAGE
#else
#define LINKAGE __declspec(dllexport)
#endif
build mingw explicit both.sh
#! /bin/bash
echo Building configuration where both main
echo and dll link explicitly to each other
rm -rf mingw_explicit_both
mkdir -p mingw_explicit_both/obj
cd mingw_explicit_both/obj
# compile the source code (dll created with position independent code)
gcc -c -fPIC -DEXPLICIT_MAIN ../../dll.c
gcc -c -DEXPLICIT_DLL ../../main.c
gcc -c ../../dyn_link.c
#create the dll from its object code the normal way
gcc -shared -odll.dll dll.o dyn_link.o -Wl,--out-implib,libdll.a
# create the executable
gcc -o main.exe main.o dyn_link.o
mv dll.dll ..
mv main.exe ..
cd ..
build mingw explicit dll.sh
#! /bin/bash
echo Building configuration where main explicitly
echo links to dll, but dll implicitly links to main
rm -rf mingw_explicit_dll
mkdir -p mingw_explicit_dll/obj
cd mingw_explicit_dll/obj
# compile the source code (dll created with position independent code)
gcc -c -fPIC ../../dll.c
gcc -c -DEXPLICIT_DLL ../../main.c
gcc -c ../../dyn_link.c
# normally when linking a dll, you just use gcc
# to create the dll and its linking library (--out-implib...)
# But, this dll needs to import from main, and main's linking library doesn't exist yet
# so we create the linking library for main.o
# make sure that linking library knows to look for symbols in main.exe (the default would be a.out)
gcc -omain.exe -shared main.o -Wl,--out-implib,main.a #note this reports failure, but it's only a failure to create main.exe, not a failure to create main.a
#create the dll from its object code the normal way (dll needs to know about main's exports)
gcc -shared -odll.dll dll.o dyn_link.o main.a -Wl,--out-implib,libdll.a
# create the executable
gcc -o main.exe main.o dyn_link.o
mv dll.dll ..
mv main.exe ..
cd ..
build mingw explicit main.sh
#! /bin/bash
echo Building configuration where dll explicitly
echo links to main, but main implicitly links to dll
rm -rf mingw_explicit_main
mkdir -p mingw_explicit_main/obj
cd mingw_explicit_main/obj
# compile the source code (dll created with position independent code)
gcc -c -fPIC -DEXPLICIT_MAIN ../../dll.c
gcc -c ../../main.c
gcc -c ../../dyn_link.c
# since the dll will link dynamically and explicitly with main, there is no need
# to create a linking library for main, and the dll can be built the regular way
gcc -shared -odll.dll dll.o dyn_link.o -Wl,--out-implib,libdll.a
# create the executable (main still links with dll implicitly)
gcc -o main.exe main.o -L. -ldll
mv dll.dll ..
mv main.exe ..
cd ..
build mingw implicit.sh
#! /bin/bash
echo Building configuration where main and
echo dll implicitly link to each other
rm -rf mingw_implicit
mkdir -p mingw_implicit/obj
cd mingw_implicit/obj
# compile the source code (dll created with position independent code)
gcc -c -fPIC ../../dll.c
gcc -c ../../main.c
# normally when linking a dll, you just use gcc
# to create the dll and its linking library (--out-implib...)
# But, this dll needs to import from main, and main's linking library doesn't exist yet
# so we create the linking library for main.o
# make sure that linking library knows to look for symbols in main.exe (the default would be a.out)
gcc -omain.exe -shared main.o -Wl,--out-implib,main.a #note this reports failure, but it's only a failure to create main.exe, not a failure to create main.a
# create the dll from its object code the normal way (dll needs to know about main's exports)
gcc -shared -odll.dll dll.o main.a -Wl,--out-implib,libdll.a
# create the executable (exe needs to know about dll's exports)
gcc -o main.exe main.o -L. -ldll
mv dll.dll ..
mv main.exe ..
cd ..
build mingw static.sh
#! /bin/bash
echo Building configuration where main and dll
echo statically link to each other
rm -rf mingw_static
mkdir -p mingw_static/obj
cd mingw_static/obj
# compile the source code
gcc -c -DNO_DLL ../../dll.c
gcc -c -DNO_DLL ../../main.c
# create the static library
ar -rcs dll.a dll.o
# link the executable
gcc -o main.exe main.o dll.a
mv main.exe ../
cd ..
build msvc explicit both.bat
#echo off
echo Building configuration where both main
echo and dll link explicitly to each other
rd /s /q win_explicit_both
md win_explicit_both\obj
cd win_explicit_both\obj
rem compile the source code
cl /nologo /c /DEXPLICIT_MAIN ..\..\dll.c
cl /nologo /c /DEXPLICIT_DLL ..\..\main.c
cl /nologo /c ..\..\dyn_link.c
rem create the dll from its object code the normal way
link /nologo /dll dll.obj dyn_link.obj
rem create the executable
link /nologo main.obj dyn_link.obj
move dll.dll ..\
move main.exe ..\
cd ..
build msvc explicit dll.bat
#echo off
echo Building configuration where main explicitly
echo links to dll, but dll implicitly links to main
rd /s /q win_explicit_dll
md win_explicit_dll\obj
cd win_explicit_dll\obj
rem compile the source code
cl /nologo /c ..\..\dll.c
cl /nologo /c /DEXPLICIT_DLL ..\..\main.c
cl /nologo /c ..\..\dyn_link.c
rem normally when linking a dll, you just use the link command
rem that creates the dll and its linking library.
rem But, this dll needs to import from main, and main's linking library doesn't exist yet
rem so we create the linking library for main.obj
rem make sure that linking library knows to look for symbols in main.exe (the default would be main.dll)
lib /nologo /def /name:main.exe main.obj
rem create the dll from its object code the normal way (dll needs to know about main's exports)
link /nologo /dll dll.obj main.lib
rem create the executable
link /nologo main.obj dyn_link.obj
move dll.dll ..\
move main.exe ..\
cd ..
build msvc explicit main.bat
#echo off
echo Building configuration where dll explicitly
echo links to main, but main implicitly links to dll
rd /s /q win_explicit_main
md win_explicit_main\obj
cd win_explicit_main\obj
rem compile the source code
cl /nologo /c /DEXPLICIT_MAIN ..\..\dll.c
cl /nologo /c ..\..\main.c
cl /nologo /c ..\..\dyn_link.c
rem since the dll will link dynamically and explicitly with main, there is no need
rem to create a linking library for main, and the dll can be built the regular way
link /nologo /dll dll.obj dyn_link.obj
rem create the executable (main still links with dll implicitly)
link /nologo main.obj dll.lib
move dll.dll ..\
move main.exe ..\
cd ..
build msvc implicit.bat
#echo off
echo Building configuration where main and
echo dll implicitly link to each other
rd /s /q win_implicit
md win_implicit\obj
cd win_implicit\obj
rem compile the source code
cl /nologo /c ..\..\dll.c
cl /nologo /c ..\..\main.c
rem normally when linking a dll, you just use the link command
rem that creates the dll and its linking library.
rem But, this dll needs to import from main, and main's linking library doesn't exist yet
rem so we create the linking library for main.obj
rem make sure that linking library knows to look for symbols in main.exe (the default would be main.dll)
lib /nologo /def /name:main.exe main.obj
rem create the dll from its object code the normal way (dll needs to know about main's exports)
link /nologo /dll dll.obj main.lib
rem create the executable (exe needs to know about dll's exports)
link /nologo main.obj dll.lib
move dll.dll ..\
move main.exe ..\
cd ..
build msvc static.bat
#echo off
echo Building configuration where main and dll
echo statically link to each other
rd /s /q win_static
md win_static\obj
cd win_static\obj
rem compile the source code
cl /nologo /DNO_DLL /c ..\..\dll.c
cl /nologo /DNO_DLL /c ..\..\main.c
rem create the static library
lib /nologo dll.obj
rem link the executable
link /nologo main.obj dll.lib
move main.exe ..\
cd ..
First, I found that this article was a very interesting and a concise read on dynamic link libraries (the article is only specific to Linux, but the concepts surely apply to windows as well and you might get some insight as to the different behaviour you are seeing). Especially the fundamental difference between static and dynamic loading.
I think what you want or are trying to implement is a "cross-module singleton" pattern. If you read the answers to this thread, I don't know how I could possibly answer your question any better than Ben Voigt answered that post. I have implemented a cross-module singleton before (a few times actually) using the method he describes, and it works like a charm.
Of course, you will not be able to retain the cleaniness of just having the global variable sit there in the cpp file. You will have to use a static pointer and some accessor functions and reference counting. But it can work. I'm not so sure how it would be possible to avoid that foo.exe and foo.exe share the same instance of global data one bar.dll, I never had to do that and can't really think of a way to do it, sorry.
I found this to be such an interesting question that I took the time to write an extensive tutorial on how to use DLL's to share data among multiple DLLs (either implicitly, or explicitly linked) but also make sure the data is not shared among separate processes of the same executable file.
You can find the full article here: http://3dgep.com/?p=1759
A solution to this problem that I found to work quite well is to create a "common" or "shared" dll that defines all the data and methods that you want to share across multiple DLL's (but not shared among processes).
Let's suppose you want to define a singleton class that can be accessed from the main application code (the EXE) but you also want to access the singleton instance in shared (either implicitly or explicitly linked DLL). First, you would need to declare the singleton class in the "common" DLL:
// Export the class when compiling the DLL,
// otherwise import the class when using the DLL.
class __declspec(dllexport) MySingleton
{
public:
static MySingleton& Instance();
};
When compiling the CommonDLL project, you have to export the class declaratoin by decorating the class with __declspec(dllexport) and when you are using the DLL (in the application for example), the class definition needs to be imported by decorating the class with __declspec(dllimport).
When exporting a class by decorating the class with the __declspec(dllexport) specifier, all of the class's methods and data (even private data) are exported from the DLL and usable by any DLL or EXE that implicitly links to the common DLL.
The definition of the MySingleton class might look something like this:
MySingleton& MySingleton::Instance()
{
static MySingleton instance;
return instance;
}
When compiling the common dll, two files will be produced:
The Common.DLL file which is the shared library that defines the mehthods and data exported used by the DLL.
The Common.LIB file which declares stubs for the methods and members exported from the DLL.
If you link you application against the exported LIB file, then the DLL file will be implicitly linked at runtime (as long as the DLL file is found in the DLL search paths) and you will have access to the singleton defined in the CommonDLL.DLL file.
Also, any shared library (plug-ins for example) that also links against the CommonDLL.LIB file will have access to the same singleton instances when dynamically loaded by the application.
For a full explanation of this solution including a source code sample, check-out the following article I posted titled "Using Dynamic Link Libraries (DLL) to Create Plug-Ins":
http://3dgep.com/?p=1759
If foo.exe always loads bar.dll then you can implement the variable in bar.dll and export it. For example, some file b.cpp compiled only into bar.dll, not into foo.exe:
__declspec(dllexport) int x;
Then import it in a source file c.cpp compiled into foo.exe:
__declspec(dllimport) int x;
However, if sometimes foo.exe doesn't load bar.dll then this won't work. Also, I'm writing this from memory and so there might be some syntactical errors, but hopefully it's enough to point you in the right direction.
I can't answer why it's different Linux.
The difference between GCC and and Visual Studio is that on Linux, it implicitly allows code to see symbols from other, dynamically linked (shared) libraries, without you the programmer having to do anything special. All the symbols are available in the shared (dynamically linked) library for the dynamic linker to resolve when the program runs. On Windows, you have to specifically export the symbol from the DLL, and also explicitly import it into the program or library that's using it. (Usually this is done via a macro (#define) that expands to have the dllexport declaration in a header file when building the dll itself, but when the header file is included by some other program using the dll, it expands to have the dllimport declaration instead. In my opinion this is a pain in the neck, and GCC's behavior is easier, since you don't have to do anything special to get the behavior you usually want.
On newer version of GCC, you can set the default to hide symbols when building a dynamic (shared) library, if you want to.
Thanks for providing various solution on this. i have looked at these option and decided to implement the cross module singleton using the Shared Memory and it worked well for me as well.
i have used Qt QSharedMemory to achieve my task but the prototype i wrote using the Win32 CreateFileMapping & etc.
If I understand your question correctly, you are statically linking a.cpp into foo.exe and bar.dll, so you get 2 instances of x.
If you created a third dll (say a.dll), and you dynamically link foo.exe and bar.dll to a.dll, you will get the behaviour you desire:
foo.exe loads a.dll and creates x.
bar.dll gets loaded and sees that
a.dll is loaded and doesn't load it
again, they share x.
Another process loads a.dll, it gets
its own x.
I have seen many answers to this question and since it is a bit tricky and unclear I would like to bring the following scenario.
We want to share a global variable between a DLL and a main program, and also allow access to this variable from different modules in the DLL and in the main program.
The variable is a BOOL indicating if the program should continue running or stop. The variable name is ShouldRun;
In the main program we need to put:
__declspec(dllexport) bool ShouldRun;
In the DLL's main module we need to put:
extern "C" BOOL __declspec(dllexport) ShouldRun = TRUE;
In any other module inside the DLL project we will use:
extern "C" BOOL ShouldRun;