Dependent Origination

Posts Tagged ‘c++

This is a follow up to the previous post about multiple definitions during linking. In it, we used inline so the functions will disappear from symbol table and no linker complain. When it comes to string literals, it is apparent that we cannot just define them in the header file — there is no way of taking them out from the symbol table and it is inevitable for the multiple definitions error. However, we can move the definition to the implementation file and leave an extern declaration in the header file. This way, the linker won’t complain — at the place of reference, the header file is inlcuded, which has the extern declaration so the compiler is happy. The actual definition (and storage allocation, entering into symbol table etc) happens in the implementation file, which if we compile it into a separate library, will only be introduced once by the smart linker.

Problem solved. Old school way.

Advertisements

say I have this template function in a header file.

class A {

template<typename T>

bool my_function(T& t);

}

And its implementation is in a separate cc file. There has been undefined reference errors during linking and I was puzzled. Luckily, amid the puzzling, I noticed that my class A has other functions, which are not template, and which are compiling and linking fine. So this is no visibility issue but gotta be related to it being a template function.

This realization then makes subsequent googling easy: here is exactly same problem on StackOverflow. The solution is you have to put implementation into the same header file, because the compiler has to see the implementation at the time of compiling — it has to be able to instantiate the function during compiling stage. In other words, it has to produce a compiled version of the instantiation of the templated function. It cannot wait until link time.

Remember I have met the same problem years ago, while I was new at the previous job and spent hours figuring out this. Hopefully now I remember this and won’t be puzzled any more in the future ๐Ÿ™‚

Let’s say we have two libraries A and B. They both need library C, which is a header file only library since there are only two simple functions in the header file. Now I am building D, which needs both A and B. The linker is complaining multiple definitions of functions of C since both .o files have them.

Solution: make the functions in the header file (of C) inline. This way, it won’t be compiled as a separate function into the object files. Even if the compiler couldn’t make it inline, compiler will find an anonymous namespace and put the function there.

The other solution we can separate out a .cc file from the header that way we would have a third .o file and that solves everything too. But I find the inline solution better in this case, and it increases my knowledge about compiler too ๐Ÿ™‚

let’s say you want such a map

unordered_map<pair<int, int>, pair<int, int>> m;

you have to provide a hash function, otherwise you are going to run into compiler complaints about std:hash<your customized key type> is not specialized or something like that. in fact, if your type is really customized, you have to provide an equality function too since the map needs to know which bucket to put a key in, and how to tell if a key is equal to another key.

since we are using pair, which has already defined == operators, we just need to provide a hash function to the definition of the map.

the function is defined like this. note we are using the first component of the pair to be the hash key. you can do any fancy algorithms here.

typedef struct {

long operator() (const pair<int, int> &k) const {

std::hash<int> hasher;

return hasher(k.first);

}

} PairKeyHash;

then

unordered_map<pair<int, int>, pair<int, int>, PairKeyHash> variable_;

and all done!

References:

1.ย pair type reference

2.ย unordered_map reference

3. thisย articleย helps the most

I have encountered a ‘cannot find reference to function’ error from the linker today and couldn’t figure out why. Thanks to a colleague who recently worked on the build system — we figured out why.

library file:

namespace A {

namespace {

struct D { definition; }

}

return_type example_function(const vector<D>&);

}

test file:

#include “library.h”

vector<D> v;

example_function(v);

this test cannot pass linker because linker would complain it couldn’t find reference to example_function.

How did we figure out?

1. grep through the actual compile command to see the *.o file is in it

2. use nm on the *.o file so we can see the function symbol is in the object file

3. clearly this is about parameter types now — i confirmed it passing a simple type

4. then colleague realizes that it is because of the unnamed space — it is assigned to a random number while compiling the library — and another random number assigned while compiling the test — so the two symbols won’t match up in the symbol table and linker couldn’t find the prototype of the function

Solution:

1. take out the unnamed space or give it a name

This solution isn’t found by me — it is found by my teammate. But since we have spent quite some hours trying to get our thing compiled together, I took the liberty of writing it down here ๐Ÿ™‚

The problem is when we include header files from mongo they have macros defined and undefined in their module. Classes are fine since they are in a separate namespace. Macros are really messy. I tried to include our own headers after theirs so we have those macros available after they undefined theirs (which have the same name as ours, such as LOG!). But it only works to a certain extent, we soon faced mysterious error messages that a template parameter is not instantialized or something like that. My teammate realized that instead of using their classes, you can use them as pointers and that way you only need to forward declaration without include the actual header files. Then you include the actual header files in the implementation files. This way their module is only pulled in during compile time which isn’t polluting everyone else’s namespace with their macros. Well that is the theory we will probably encounter more problems but I think the solution got us really far and is clever and warrants a note ๐Ÿ™‚

I saw code like this in a header file:

namespace X {

class Y;

}

I thought that was wieldy and changed it to

class X::Y;

Guess what? It wouldn’t compile!

The reason? Here. In short, C++ only allows fully-qualified names refering to existing (previously declared) entities. They cannot be used to declare new entities.ย Once you have this wieldy forward declaration, you can reference it in fully qualified name subsequently. I guess the tricky point here is the compiler couldn’t figure out whether X is a class or a namespace for a form of X::Y.


August 2019
M T W T F S S
« Jun    
 1234
567891011
12131415161718
19202122232425
262728293031  

Twitter

Flickr Photos

Advertisements