Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linking C from C++ in OS X Mavericks

Having transitioned to OS X Mavericks and XCode 5.0.1, I can no longer gracefully link compiled C files (output from gcc) to a C++ project (output from g++).

The offending pair of commands produced from my makefile are:

gcc `pkg-config --cflags glib-2.0` -g -Wall -O3 `pkg-config --cflags flann`   -c -o vec.o vec.c
g++ `pkg-config --cflags glib-2.0` -g -Wall -O3   -stdlib=libstdc++ -lstdc++  layoutquality.cpp vec.o  `pkg-config --libs glib-2.0`  -L/usr/local/Cellar/flann/1.8.4/lib -lflann -o layoutquality

To which the linker complains:

Undefined symbols for architecture x86_64: "load_dmat(char const*)", referenced from: _main in layoutquality-I8HOqy.o ld: symbol(s) not found for architecture x86_64

Where load_dmat is just a function in the file vec.c . If I replace the gcc with g++ in the first line, then everything compiles and links fine, but clang says:

clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated

Is there an inoffensive, non-deprecated way of compiling and linking these? Linking with g++ together with object files from gcc worked fine before I upgraded to OS X Mavericks and the new command line tools. Any insight into what changed and how to go forward would be great, thanks.

like image 379
stephen f Avatar asked Oct 28 '13 20:10

stephen f


2 Answers

Adding a "-x c" (without quotes) before "vec.c" should fix it.

If you compile multiple .c/.cpp files in the same line you can use "-x c" or "-x c++" before each list of C or C++ filenames to switch context appropriately. For example:

g++ -x c  alpha.c beta.c  -x c++  gamma.cpp
like image 105
AbePralle Avatar answered Sep 30 '22 19:09

AbePralle


Here's an example Makefile that let us use C++ code/function in a C program.

CC=clang
CXX=clang++
CFLAGS=-Wall -g
CXXFLAGS=-Wall -g -std=c++11 -I.

DEPS=CPP.h
OBJ=main.o CPP.o

RM=rm -f

# compile only, C source
%.o: %.c
    $(CC) -c -o $@ $< $(CFLAGS)

# compile only, C++ source
%.o: %.cpp $(DEPS)
    $(CXX) -c -o $@ $< $(CXXFLAGS)

# link
main: $(OBJ)
    $(CXX) -o $@ $^ $(CXXFLAGS)

clean:
    $(RM) $(OBJ)

As you can see, we generate our objects separately using CXXFLAGS and CFLAGS in two separate calls to the compiler. In the context of Mac using Xcode's clang, clang (CC) and clang++ (CXX) are actually the same thing. Only the differing flags matter. I am just being pedantic by stating the definitions of CC and CXX in the above example Makefile.

Once the object files are generated, we are good to go to link them together.

Note however that you have to do one extra step to make your C++ code usable by the C program.

In CPP.h in this example, you have to explicitly use extern "C" to specify linkage for your C++ code for use by C.

For example, like this:

#ifdef __cplusplus
extern "C" {
#endif

double timesTwo(double number);

#ifdef __cplusplus
}
#endif

The preprocessor macros #ifdef __cplusplus and #endif are to make sure that our header file won't cause C-mode compilation errors and is only in-effect during C++-mode compilation.

This complete example comprises only 4 files.

  • Makefile
  • main.c
  • CPP.h
  • CPP.cpp

The Makefile source and CPP.h are explained above.

For a complete understanding, I am including main.c and CPP.cpp here as well.

main.c:

#include <stdio.h>
#include "CPP.h"

int main()
{
    printf("Running main.c\n");
    double ans = timesTwo(3.0);
    printf("The answer from our C++ timesTwo function, when given 3.0, is %f\n", ans);

    return 0;
}

CPP.cpp:

#include "CPP.h"

double timesTwo(double number)
{
    return 2 * number;
}

I trust that this explanation and example clarifies how we can set up our Makefile, specify the #ifdef __cplusplus preprocessor macro and extern "C" linkage declaration to allow C++-to-C interop, and with no erroneous clang warning when we run make.

like image 21
Calvin Cheng Avatar answered Sep 30 '22 17:09

Calvin Cheng