2

I have a very strange problem with the GCC 5.4.0 linker. I have these files:

spline.h, utils.h/cpp, main.cpp

spline.h is a header-only utility class for fitting points to splines.

1) I create a library with utils.cpp and CMake:

add_library(utils_lib utils.cpp)

utils.h is #includeing spline.h.

2) I create my binary from main.cpp:

add_executable(hello_world main.cpp)
target_link_libraries(hello_world utils_lib)

3) Inside utils.cpp, I have this function:

tk::spline fitSpline(const std::vector<double>& x,
                     const std::vector<double>& y)
{
    tk::spline output;
    output.set_points(x,y);
    return output;
}

So, if I try to use this function inside main.cpp:

auto my_spline = fitSpline(x,y);

Then I get this linker error:

undefined reference to `fitSpline(std::vector<double, std::allocator<double> > const&, std::vector<double, std::allocator<double> > const&)'

However if I change the return value of fitSpline to double for example:

double fitSpline(const std::vector<double>& x,
                 const std::vector<double>& y)
{
    tk::spline output;
    output.set_points(x,y);

    return 0.0;
}

Then I don't get the linker error anymore! It compiles just fine. I really don't understand what the problem is, any hints?

Thanks!

hnefatl
  • 5,860
  • 2
  • 27
  • 49
user1011113
  • 1,114
  • 8
  • 27
  • Is there an existing function signature for `fitSpline` in `utils.h`? Only thing I can think of at the moment. If possible, post the relevant code in `utils.*` and `main.cpp`. – hnefatl Jul 27 '17 at 14:13
  • @hnefatl yes, the signature is there. As I said, if I just change the return value type, everything works just fine. I just realized that `spline.h` has everything in an anonymous namespace, for sure that must be the reason! Do I need to `#include` it in every cpp file then? – user1011113 Jul 27 '17 at 14:25
  • If they're truly in an [anonymous namespace](https://stackoverflow.com/questions/154469/unnamed-anonymous-namespaces-vs-static-functions), then nothing outside the file should be able to access them. By signature, I meant to ask whether you had a split definition and implementation of the function, and had forgotten to update one of them. Including `spline.h` in `main.cpp` might help, but I can't really work out why it would. Posting more code would help. – hnefatl Jul 27 '17 at 14:29
  • Yes they have the same signature in `utils.cpp` and `utils.h`. I have just tried `#include`ing `spline.h` everywhere, in `main.cpp`, `utils.cpp` and `utils.h`, but still it doesn't work. It only works if I skip `utils`, and use `spline.h` inside `main.cpp` directly instead of wrapping it in a function. – user1011113 Jul 27 '17 at 14:32
  • Actually `spline.h` starts with: // unnamed namespace only because the implementation is in this // header file and we don't want to export symbols to the obj files namespace { namespace tk { (Sorry for formatting, can I do proper code in the comments section?) – user1011113 Jul 27 '17 at 14:33
  • Try commenting out the anonymous namespace - leaving just the `tk` namespace. We do want to export the implementations if we're building to a library, I think. It's just shots in the dark at the moment. RE. formatting, yep just use backticks ` to format it. – hnefatl Jul 27 '17 at 14:36

1 Answers1

1

There are two translation units. The TU for utils_lib and the TU for main.cpp.

Names in unnamed namespaces are both in a unique namespace AND effectively have internal linkage (since C++11 they are defined to have internal linkage).

So, trying to reference a type like tk::spline from a different TU will fail due to the effective internal linkage.

Duplicating use of spline.h would normally be fine, if the objects were stateless and not shared. But here you're not doing that. You are treating them as fungible by creating an object in one TU and trying to store them via a type in a different TU. This will fail because they have different unique namespace names.

If you really don't want to export unnecessary symbols in a header-only library, you should use a internal "detail" namespace for only those symbols, while leaving the ones that should be known to users in the outer namespace.

None of this, btw, has anything to do with cmake or any build system. However, since your question uses cmake to make the structure much clearer (good job btw), the tag is probably fine.

utopia
  • 1,477
  • 8
  • 7