4

I have a utility library of C99 code used by C++11 application code. A few inline functions are declared in the C99 style with code explicitly generated in the translation unit like:

// buffer.h
inline bool has_remaining(void* obj) {
...
}

// buffer.c
extern inline bool has_remaining(void * obj);

However, when I try to use has_remaining in the C++ application, I get errors about multiple definitions at link time. It seems that g++ is instantiating the inline code that already exists in the library, despite the extern "C" header guards specifier.

Is there a way to coerce g++ into working with this type of definition?

It looks like if I #ifdef __cplusplus an extern definition with the gnu_inline attribute, the right thing will happen, but surely there is a more portable way to keep modern C headers compatible with modern C++?

-- Edit: Working Example --

buffer.h:

#ifndef BUFF_H
#define BUFF_H

#include <stdbool.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

inline bool has_remaining(void const* const obj) {
    return (obj != NULL);
}

#ifdef __cplusplus
}
#endif

#endif /* BUFF_H */

buffer.c:

#include "buffer.h"

extern inline bool has_remaining(void const* const obj);

app.cpp:

#include <stdlib.h>
#include <stdio.h>

#include "buffer.h"

int main(int argc, char** argv) {
  char const* str = "okay";
  printf(str);

  has_remaining(str);

  return (0);
}

compile:

$ gcc -std=gnu99 -o buffer.o -c buffer.c
$ g++ -std=gnu++11 -o app.o -c app.cpp
$ g++ -Wl,--subsystem,console -o app.exe app.o buffer.o

buffer.o:buffer.c:(.text+0x0): multiple definition of `has_remaining'
app.o:app.cpp:(.text$has_remaining[_has_remaining]+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

--Edit 2-- The __gnu_inline__ attribute does indeed fix the problem of multiple definitions. I'd still like to see a (more) portable approach or some conclusive reasoning why one doesn't exist.

#if defined(__cplusplus) && defined(NOTBROKEN)
#define EXTERN_INLINE extern inline __attribute__((__gnu_inline__))
#else
#define EXTERN_INLINE inline
#endif

EXTERN_INLINE bool has_remaining(void const* const obj) {
  return (obj != NULL);
}
gibbss
  • 2,013
  • 1
  • 15
  • 22
  • Why have the `extern` declaration? Including the header without it won't work? – Pubby Jan 17 '13 at 04:26
  • 5
    `extern "C"` and header guards are unrelated. And why an `extern inline` (huh) function declaration in a `.c` file...? – Lightness Races in Orbit Jan 17 '13 at 04:28
  • I'm not quite sure what to call the #ifdef __cplusplus blocks that protect the C-mangled functions if not "header guards". For an explanation of extern inline, see here, http://www.greenend.org.uk/rjk/tech/inline.html -- it forces the compiler to emit the symbol in a specific translation unit. – gibbss Jan 17 '13 at 05:17
  • But what about standard solution: define any preprocessor constant and use #ifdef to not include buffer.h more than 1 time per each C++ file? – Vitalii Jan 17 '13 at 07:06
  • I am not seeing multiple definitions. In C++, those are weak and get merged by the linker. What platform is this? Do you have a complete short example so we can reproduce it? – Marc Glisse Jan 17 '13 at 08:35
  • The platform is GCC 4.7.2 on MinGW32 – gibbss Jan 17 '13 at 13:50
  • See here for what is happening: http://stackoverflow.com/questions/2217628/multiple-definition-of-inline-functions-when-linking-static-libs/2218034#2218034 – Johannes Schaub - litb Jan 18 '13 at 22:20
  • @MarcGlisse You may want to paste your answer from http://gcc.gnu.org/ml/gcc-help/2013-01/msg00155.html as it is the only correct one so far. – gibbss Jan 24 '13 at 14:10

3 Answers3

0

C++11 standard states (3.2.3), that:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and 12.8). An inline function shall be defined in every translation unit in which it is odr-used.

C++ also knows about extern+inline, but understands it as "An inline function with external linkage shall have the same address in all translation units" (7.1.2)

So extern+inline as you are using it is pure C99 feature, and there must be sufficient for you to make something like:

#ifdef __cplusplus
#define C99_PROTOTYPE(x)
#else
#define C99_PROTOTYPE(x) x
#endif

And refer in buffer.c:

// buffer.c
C99_PROTOTYPE(extern inline bool has_remaining(void * obj);)

inline function in header for C++11 is ok and should work fine without C99-style prototypes.

Konstantin Vladimirov
  • 6,791
  • 1
  • 27
  • 36
0

Using static with inline should fix 'multiple definitions' problem. Even for a compiler which can't decide on its own that it shouldn't generate symbols for 'inline'd functions.

kerim
  • 2,412
  • 18
  • 16
  • `Static` may cause multiple copies of inline function to be linked into final executable (if inlining fails) so is generally not recommended. – yugr Jan 11 '22 at 11:20
0

This was reported to gcc: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56066 after a discussion starting here: http://gcc.gnu.org/ml/gcc-help/2013-01/msg00152.html

On linux, gcc emits weak symbols for the inline functions, and a strong symbol for the extern inline one. At link time, the weak ones are discarded in favor of the strong one. Apparently, on windows, things are handled differently. I don't have any experience with windows, so I can't tell what happens there.

Marc Glisse
  • 7,550
  • 2
  • 30
  • 53
  • Windows has no "weak symbol" support in its object files (or at least it's not implemented in the GNU tools). It is a feature that lies outside the scope of the C and C++ languages, and so implementation defined. – rubenvb Jan 24 '13 at 14:54
  • How is the merging of C++ inline functions handled on windows? – Marc Glisse Jan 24 '13 at 15:04
  • I suppose you get a multiple defition error for the symbol. I don't know the details of how `__gnu_inline__` works though. – rubenvb Jan 24 '13 at 16:31
  • Generally, gnu_inline allows C89 code to work with C99 extern inlines by making the definition available for inlining only and preventing the generation of a symbol. – gibbss Jan 24 '13 at 19:10
  • I'm looking at the objdump -x output for the c++ object and I note that there's a separate section for the inlined function. It appears to be annotated with a COMDAT field value of 4. According to http://www.skyfree.org/linux/references/coff.pdf a value of 4 corresponds to IMAGE_COMDAT_SELECT_EXACT_MATCH: "The linker chooses an arbitrary section among the definitions for this symbol. A multiply defined symbol error is issued if all definitions don’t match exactly." The C99 symbol has no COMDAT selection field. – gibbss Jan 24 '13 at 19:37
  • Ah, looks similar to weak (can be merged) except that it isn't given lower priority than strong (multiply defined). Apart from getting extern inline to generate such a comdat 4 thing, which sounds a bit radical, I don't really see how they will work around this issue. – Marc Glisse Jan 24 '13 at 19:43