3

I have written a C++ program which is bound using cmake and runs on a Debian machine. I am happy to say all known major bugs are corrected and I feel ready to bust it from

add_definitions(-Wall)
set(CMAKE_BUILD_TYPE Debug)
add_definitions("-O0 -std=c++0x")

all the way up to

add_definitions("-O2 -std=c++0x")

I did just that, and all of a sudden a templated class within my project (naively declared in some header file and defined for all use-cases in some separate .cpp) breaks with a load of "undefined" errors during linkage.

Some research on this very forum yielded threads like this one: Why can templates only be implemented in the header file? I believe I see what Luc in his celebrated answer is driving at: The compiler actually 'writes code' for each needed version of the templated class (i.e. T=int, T=float, T=what_ever_else) and for that purpose needs access to the actual implementation.

Fair enough. But what totally baffles me here: This apparently only is an issue when trying to optimize compilation. Using "-O0" all works fine. How can that be?

Appendix

1.) About the duplicate suspect: The question here is not what I have done that is wrong. My sin is clear: I defined templated functions out of reach of the header file which apparently is not allowed for reasons described above and within the post I already cited.

Given the truth of said cited posting it is obvious that my program cannot compile. What I do not understand is: Why does it compile with -O0.

2.) nos asked for some explicit errors. The ending of my last attempt using -O2 was still in my terminal buffer (sorry, compilation takes about an hour and thus is not easily reproduced). But as it is it breaks during linkage. Here goes what I still could lay my mouse pointer on:

/home/kochmn/projects/free_sentinel_gl/sentinel/src/game/game.cpp:432: undefined reference to `game::Board<game::Figure>::get(QPoint)'
libqt.a(game.cpp.o): In function `game::Game::hyperspace_jump()':
/home/kochmn/projects/free_sentinel_gl/sentinel/src/game/game.cpp:950: undefined reference to `game::Board<game::Square>::get(QPoint)'

[... many more like that all concerned about the templated class "Board"
  which is declared in landscape.h, defined in landscape.cpp and
  used pretty much everywhere else ... ]

collect2: error: ld returned 1 exit status
CMakeFiles/sentinel.dir/build.make:548: recipe for target 'sentinel' failed
make[2]: *** [sentinel] Error 1
CMakeFiles/Makefile2:127: recipe for target 'CMakeFiles/sentinel.dir/all' failed
make[1]: *** [CMakeFiles/sentinel.dir/all] Error 2
Makefile:76: recipe for target 'all' failed
make: *** [all] Error 2

3.) Concerning the comment question "How are the use-cases defined?": Let's take advantage of the fact that this is on GitHub.

All use-cases are defined here: https://github.com/kochsoft/free_sentinel_gl/blob/master/sentinel/src/game/landscape.cpp#L507 starting at line 507.

The header is here: https://github.com/kochsoft/free_sentinel_gl/blob/master/sentinel/src/include/landscape.h#L363 the pertinent passage starting at line 363.

Community
  • 1
  • 1
Markus-Hermann
  • 789
  • 11
  • 24
  • 2
    possible duplicate of [Why do optimisation flags cause linker errors for some template functions?](http://stackoverflow.com/questions/6828576/why-do-optimisation-flags-cause-linker-errors-for-some-template-functions) – m.s. Jun 02 '15 at 12:59
  • 1
    You should post your test case here, and the actual output/error your compiler is throwing at you. – nos Jun 02 '15 at 13:01
  • 1
    *"and defined for all use-cases in some separate .cpp"* How have you *defined all use-cases*? For implicit instantiations, the compiler can assume that everyone who needs them can create them. If in some translation unit, it doesn't need an implicit instantiation (e.g. since a function call is inlined), it doesn't have to export the function template instantiation. [Live demo](http://coliru.stacked-crooked.com/a/49536ad5bad4141e) – dyp Jun 02 '15 at 13:47
  • which compiler are you using? – m.s. Jun 02 '15 at 14:06
  • "g++ --version" returns: g++ (Debian 4.9.2-10) 4.9.2. My complete CMakeLists.txt may be found here: https://github.com/kochsoft/free_sentinel_gl/blob/master/sentinel/build/CMakeLists.txt – Markus-Hermann Jun 02 '15 at 14:09
  • can you create a minimal testcase which reproduces the behavior? I just tried to create a simple example and failed (does not link for both `-O0` and `O2`) – m.s. Jun 02 '15 at 14:11
  • 1
    `Landscape::expand_nuclei(int n, bool neglect)` uses `board::get(QPoint)`, which causes the implicit instantiation of the latter function template. In `-O3`, this call is most probably inlined, which allows the compiler omitting to export this symbol. – dyp Jun 02 '15 at 14:15
  • In answer to m.s.: Maybe that is in fact sensible, for then I could experiment and try to find a solution that does not involve cluttering innocent header files with definitions. What makes this so frustrating is that every change to the compiler options seems to require a recompilation of the whole program which takes an hour for each attempt. – Markus-Hermann Jun 02 '15 at 14:15
  • In answer to dyp: Yes, reading around the net I, too, start thinking about "class instanciation". Probably that is what I am looking for. Will experiment with this. – Markus-Hermann Jun 02 '15 at 14:18
  • 1
    An hour?? How much code do you have in that project? o.O For templates, the usual solution is to put their definitions in separate files that are *included* in the header. Another way is to use *explicit instantiations*. – dyp Jun 02 '15 at 14:18
  • At dyp: Not that much code. However, I use Qt and the qrc resource scheme. Apparently Qt generates from my resources a qrc_application.cpp.o file that contains all resource informations. Texture graphics, sound and all. This one object takes the hour. – Markus-Hermann Jun 02 '15 at 14:20

1 Answers1

2

My issue is solved and dyp was closest with his/her comment. The magic word is "explicit template instanciation" and may be found here: http://www.cplusplus.com/forum/articles/14272/

I did NOT clutter my header files with definitions. Nor did I add code like #include "some.cpp" into the header, which in my opinion amounts to the same. Instead I added the lines

template class Board<Figure>; 
template class Board<Square>;

right under the definitions in landscape.cpp and was able to compile with -O2.

According to Why can templates only be implemented in the header file? I won't be able to use anything else but Figures and Squares on my Boards though (which suits me fine).

As to why -O0 works: As dyp stated in above comment the optimizing compiler probably removed some code fragment that under -O0 was used for the template instanciation. Probably... I still do not claim to have it all understood completely.

@dyp: Why don't you post a comprehensive answer to that question carifying your earlier comment?

Community
  • 1
  • 1
Markus-Hermann
  • 789
  • 11
  • 24
  • There are some parts of linkage of templates that I could not verify in the C++ Standard. I'll probably ask a question on SO myself later, if I'm unable to find a similar question on SO. – dyp Jun 02 '15 at 15:44
  • In any case, I am convinced your earlier post hit the mark. Thanks also for commenting that my question is in fact unique. After all the complaint actually was about something working where everyone else was concerned about something _not_ working! ;-) That also is the reason why I am not going to accept my own answer. But I might accept one of yours. – Markus-Hermann Jun 02 '15 at 15:48
  • IMO, your question is sort-of related to *"[Why do optimisation flags cause linker errors for some template functions?](http://stackoverflow.com/questions/6828576/why-do-optimisation-flags-cause-linker-errors-for-some-template-functions)"*, but I don't really like the answers there (esp when applied to your case). – dyp Jun 02 '15 at 15:51
  • It certainly appears to be the same issue. – Markus-Hermann Jun 02 '15 at 15:54
  • Several weeks later: In order to close the issue I accept my own answer after all. – Markus-Hermann Jun 14 '15 at 07:35