Linking C from C++ in OS X Mavericks

15,164

Solution 1

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

Solution 2

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.

Solution 3

Most probably you are a victim of Name mangling. To avoid name mangling in C++, use extern "C" around declarations, like:

#ifdef __cplusplus 
extern "C" {
#endif
    void load_dmat(char const*);
#ifdef __cplusplus
}
#endif
Share:
15,164
stephen f
Author by

stephen f

Updated on June 27, 2022

Comments

  • stephen f
    stephen f almost 2 years

    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.

  • bames53
    bames53 over 10 years
    If this solves the problem then I'm curious how the source could have built and linked before.
  • stephen f
    stephen f over 10 years
    Thanks for the pointer to name mangling, but unfortunately, this doesn't solve the problem. I added the edit to my vec.h and the linker error persists.
  • bhaller
    bhaller about 8 years
    Thanks for the -x c and -x c++ tip, very helpful. But I need to supply -std=c++11 to use C++11 with my .cpp files. I'm getting error: invalid argument '-std=c++11' not allowed with 'C/ObjC' now. Is there a way to do a one-line build with both c++ and c files, specifying the c++11 standard?
  • AbePralle
    AbePralle about 8 years
    No, I don't know of any way to do a one-line g++ build with a C file and a C++ 11 file, though compiling to intermediate .o files and then linking them should work.