Avoiding multiple definition errors when using C++ header libraries

8,021 views
Skip to first unread message

granta

unread,
Dec 24, 2016, 2:26:33 PM12/24/16
to bazel-discuss

I can break a C++ build if I have a function definition in a header file, and also have a target that depends on the header and uses it in more than one of its own files. Here is a simple example:

**** BUILD file ****
cc_library(
    name = "foo",
    hdrs = ["foo.h"],
)
cc_library(
    name = "bar",
    hdrs = [
        "a.h",
        "b.h",
    ],
    srcs = [
        "a.cc",
        "b.cc",
    ],
    deps = [
        ":foo",
    ],
)
********

**** foo.h ****
#ifndef FOO_H
#define FOO_H
int foo() {
  return 1;
}
#endif  // FOO_H
********

**** a.cc ****
#include "a.h"
#include "foo.h"
int a() {
  return foo();
}
********

**** b.cc ****
#include "b.h"
#include "foo.h"
int b() {
  return foo();
}
********

(a.h and b.h simply declare a() and b()).

I am using Bazel release 0.4.3 on Ubuntu 16. If I run "bazel build :bar" then the object files for a.cc and b.cc both include foo() and the link step fails with the error:
b.cc:(.text+0x0): multiple definition of `foo()'
bazel-out/local-fastbuild/bin/_objs/bar/a.pic.o:a.cc:(.text+0x0): first defined here

However, if I move the definition of foo() to a new file "foo.cc" and leave only the declaration in foo.h, then the problem goes away.

This seems like a bug to me since lots of header files include definition code for various reasons. But I'm fairly new to bazel and could be using it improperly too, so I thought I'd ask here first.

So, what is the proper way to depend on a header that has definition code, especially for the case of header-only libraries and when multiple source files refer to it?

Thanks.


Austin Schuh

unread,
Dec 24, 2016, 3:20:08 PM12/24/16
to granta, bazel-discuss
Your example violates the https://en.wikipedia.org/wiki/One_Definition_Rule.  To quote the article:

  In the entire program, an object or non-inline function cannot have more than one definition; if an object or function is used, it must have exactly one definition. You can declare an object or function that is never used, in which case you don't have to provide a definition. In no event can there be more than one definition.

Bazel is being helpful and has managed to configure the linker to detect ODR violations.  This is a feature.  The compiler and linker is allowed to do what it likes when it runs into an ODR violation, including randomly fail at runtime.

The solution is to leave the declaration of foo in the header and pull the definition into foo.cc.
 
Austin

granta

unread,
Dec 24, 2016, 5:16:16 PM12/24/16
to bazel-discuss, grant...@gmail.com
Thanks for clarifying Austin. I mistakenly thought I'd seen bazel being clever in the past at link time with its knowledge of the dependency graph, but this is really just a classic case of ODR.
Reply all
Reply to author
Forward
0 new messages