3

If I use C code in a c++ environment and I include all of the code inside the header, everything works fine. If I try to declare C functions in a header and them implement them in a .c or .cpp file, I get the following error:

Undefined symbols for architecture x86_64:
  "vec2_norm(Vec2)", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Vec2.h

#ifndef Physics_Engine_Test_Vec2_h
#define Physics_Engine_Test_Vec2_h

typedef struct
{
    float x;
    float y;
} Vec2;

inline Vec2 vec2_norm(Vec2 v);

#endif

Vec2.c or .cpp

#include "Vec2.h"
#include <math.h>

inline Vec2 vec2_norm(Vec2 v) {
    float len = v.x*v.x + v.y*v.y;
    if (len) {
        len = 1 / sqrtf(len);
        v.x *= len;
        v.y *= len;
    }
    return v;
}
Stas Jaro
  • 4,747
  • 5
  • 31
  • 53
  • 1
    [Name mangling...](https://en.wikipedia.org/wiki/Name_mangling#Name_mangling_in_C.2B.2B) –  Jun 22 '13 at 20:01
  • possible duplicate of [Linker error calling C-Function from Objective-C++](http://stackoverflow.com/questions/9334650/linker-error-calling-c-function-from-objective-c) –  Jun 22 '13 at 20:01
  • I just tested and it compiles fine for me using g++ as well as when I use clang – zmo Jun 22 '13 at 20:03
  • Does error persist if you remove "inline" clauses? – Yury Schkatula Jun 22 '13 at 20:14

2 Answers2

4

though I tested your code sample, and it compiled fine for me using g++/gcc and clang++/clang, when you want to compile C based source code, it's always a good idea to add extern "C" {} around it so the compiler does not do C++ name mangling on those functions:

#ifdef __cplusplus
extern "C" {
#endif

typedef struct
{
    float x;
    float y;
} Vec2;

inline Vec2 vec2_norm(Vec2 v);

#ifdef __cplusplus
};
#endif

and

extern "C" {

inline Vec2 vec2_norm(Vec2 v) {
    float len = v.x*v.x + v.y*v.y;
    if (len) {
        len = 1 / sqrtf(len);
        v.x *= len;
        v.y *= len;
    }
    return v;
}

};

btw, about the inline you're using in your code, even though it is not mandatory to have an inline function defined only in the header, it's strongly advised to do it, so you don't have to copy the inline body to every translation unit where you'll include that header, because of the one definition rule.

As wikipedia says on the topic:

Some things, like types, templates, and extern inline functions, can be defined in more than one translation unit. For a given entity, each definition must be the same. Non-extern objects and functions in different translation units are different entities, even if their names and types are the same.

But in the end, whether it is a good or a bad idea depends on your design choices.

HTH

zmo
  • 24,463
  • 4
  • 54
  • 90
  • The implementation is in a `.cpp` file, right? So no need to test `__cplusplus` there. Also, the correct test is `#if __cplusplus`. Some C/C++ compilers will `#define __cplusplus 0` when running in C language mode. – Ben Voigt Jun 22 '13 at 20:13
  • Though I think it does not hurt for a C++ file to keep `#if define __cplusplus` you're right, I didn't think about the second case that may still be true (though I don't know which compilers do that). – zmo Jun 22 '13 at 20:31
  • funny thing: just got one edit to add `#ifdef __cplusplus` back :-) – zmo Jun 22 '13 at 20:49
  • The header should use `#if __cplusplus`. The implementation file just needs unconditional `extern "C"`. – Ben Voigt Jun 22 '13 at 21:16
  • "so the compiler does not do name mangling on those functions" - rather, so that the compiler uses C mangling on those functions. – Pete Becker Jun 22 '13 at 22:05
0

There's actually two things wrong here:

  1. If you declare a function inline, it won't be compiled unless needed in the given compilation unit. So the Vec2.o file won't contain it, and the linker won't be able to find it. Inline functions always have to be put into headers, so their implementation is seen by the compiler in every compilation unit it is needed in.

  2. As H2CO3 said, C++ uses name mangling: it encodes the parameter types into the function name to allow for overloading. C doesn't do this. So, if you compile a c++ file that uses your function, it will want to link against some weird name, not just against "vec2_norm". To be able to link C code into C++ code, you have to tell the compiler that it should use the C symbol names.

Most often, people do that by writing headers of this form:

#ifdef __cplusplus
    extern "C" {
#endif

Vec2 vec2_norm(Vec2 v);

#ifdef __cplusplus
    }
#endif

By the way, does anybody know how to correctly format preprocessor directives in stackoverflow?

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • about your problem, it's not the fault of the `#`, but because it bugs when you do a code block after a list. Just add some text before the code block, and it won't get messed up. cf my edit. – zmo Jun 22 '13 at 20:51