3

I am aware that definitions of C++ templated functions have to be placed in header files. However, for reasons of improved readability and structure of a (potentially) big library I am making, I separated the declarations from the implementations, into "mock" headers (which #include the implementation files, quite like this structure of files). Note that am I am aware that the implementation of templated functions must be included at compile time, and I am doing that.

In short, I have a "multiple definition" error when I add a non-templated function declaration into the implementation file. Long explanation with examples follows.

When the pair of "mock" header + implementation files only contain the declaration/implementation pair of the templated function, everything works fine. It also works fine when I add an implementation of a new templated function only in the implementation file.

Working example (I would #include "algo.h" in my main.cpp when I wanted to use this functionality):

"Mock" header file algo.h:

#ifndef ALGO_H
#define ALGO_H

namespace fl{
    template <typename Compare>
    void algo(.. non-templated args .., Compare order = std::less<int>());
}

#include "tpp/algo.cpp"

#endif // ALGO_H

Implementation file tpp/algo.cpp: (currently just algo.tpp)
Note: Using the tpp/.cpp file was in the initial version, now I am using a .tpp file per @πάντα ῥεῖ's suggestion, explanation in the end.

#ifndef TPP_ALGO
#define TPP_ALGO

#include "../algo.h"

namespace fl{

    template <typename Compare>
    void subFunctionality(Compare order, .. args ..){ /* impl */ }

    template <typename Compare>
    void algo(.. non-templated args .., Compare order){
         subFunctionality(order, .. args ..);
        // implementation
    }
}

#endif // TPP_ALGO

The problem arises when I add a non-templated function implementation in the implementation file. (Non-working) example of the tpp/algo.cpp (currently just algo.tpp) (using the same algo.h):

#ifndef TPP_ALGO
#define TPP_ALGO

#include "../algo.h"

namespace fl{

    template <typename Compare>
    void subFunctionality(Compare order, .. args ..){ /* impl */ }

    void moreSubFun(.. args ..) { /* impl */ }

    template <typename Compare>
    void algo( .. non-templated args ..., Compare order){
         subFunctionality(order, .. args ..);
         moreSubFun(.. args ..);
         // more stuff
    }
}

#endif // TPP_ALGO

I get the "multiple definition" error (from where I included it in main.cpp), like so:

obj/Release/main.o                 In function `fl::moreSubFun(...)':
main.cpp                           multiple definitions of `fl::moreSubFun(..)'
obj/Release/../tpp/algo.o:algo.cpp first defined here

Why does this happen only to non-templated functions, while it works fine for the templated, and more importantly, how do I solve this problem?

I looked all around SO, and I can't find anything useful :( Ideally, I am looking for something as close to my own file-structure as possible (but I'll take anything that works while still using some separation into "mock" .h + tpp/.cpp). Do I have to take out the additional sub-functionalities into a separate, non-templated pair of .h/.cpp files, or is there other solutions? (The sub-functionalities should ideally not be visible to the end-user).

I am reluctant to use inline when defining fl::moreSubFunc(..) as the function is pretty big (and I was taught inline should ideally only be used with small functions). This does solve the problem, but I'm looking to see if there is a different solution.

I am working in Code::Blocks, using gcc version 4.7.2. This is the initial reason my implementation file is tpp/.cpp (.cpp extension), since Code::Blocks does not support it by default. This is changed in the current implementation following @πάντα ῥεῖ's suggestion (look below).


Late edit (After I taught the solution was found) I taught @πάντα ῥεῖ's answer solves the problem. I tweaked Code::Blocks to accept .tpp files (either treating it as header or source files). Initially, this solution worked.

However, this solution worked only when the algo.h file was included in only one other file: when I included it only in main.cpp. However, as soon as I tried including it in another source file (e.g. algo2.cpp) that would use those algorithms (in addition to main.cpp), the multiple definition problem came back.

Bottom line, the problem still persists as soon as I include the algo.h in more than one file, and I am still looking for a solution.

Community
  • 1
  • 1
penelope
  • 8,251
  • 8
  • 45
  • 87
  • Check my updated answer and comments please. – πάντα ῥεῖ Aug 05 '14 at 14:07
  • 2
    According to [this link](http://forums.codeblocks.org/index.php?topic=18051.0), you should be able to configure extensions using Project -> Project tree -> Edit file types and categories – Daniel Frużyński Aug 05 '14 at 14:32
  • @penelope I've updated my answer again, on behalf of your clarifications about using code-blocks. – πάντα ῥεῖ Aug 05 '14 at 15:53
  • 1
    The solution is to do the reverse of what the error indicates you did. Namely, just don't have multiple definitions of such functions. They belong in a `.cpp` file that is separately compiled and not included anywhere. In other words: there's no *solution* that you imply, because what you wish to do is forbidden by the rules of the langauge. The errors usually tell you what you did wrong, you know :) – Kuba hasn't forgotten Monica Aug 09 '14 at 12:35
  • @KubaOber So basically: break this in to 3 files: `.h` file for declarations, `.tpp` file (that will be included) for templated functions (just for increased readability), and `.cpp` file for all other definitions, just like @JasonR's answer suggests? – penelope Aug 09 '14 at 13:10
  • @penelope Yes. Again, sometimes it's as simple as sticking to the rules and not doing what the compiler tells you not to do :) It of course doesn't matter where those definitions go, as long as that file is compiled exactly once per linked result. – Kuba hasn't forgotten Monica Aug 09 '14 at 13:12
  • @KubaOber The "problem" with this kind of solution in that the _sub-functionalities_ are not supposed to be accessible to the end-user via including the `.h` file: which is why they are not even declared in the `.h` file, but only defined in the source file. However, I think this is out of scope of this question: I'll ask a new one about the proper organization of such functions and post the link here. – penelope Aug 09 '14 at 13:18
  • I made a more general question about proper organization of templated/non-templated functions into different files [here](http://stackoverflow.com/q/25219232/884412). – penelope Aug 09 '14 at 13:49
  • @penelope Late reply: _"either treating it as header **or source files**"_ The treatment as source files is probably causing the problem. If it's treated as source, the IDE will try to compile it as a separate TU as mentioned earlier. – πάντα ῥεῖ Oct 04 '14 at 23:02

3 Answers3

5

Your problem occurs because function templates are treated differently at link time from "plain" free functions. Functions must obey the One Definition Rule (ODR); that is, they must be defined in no more than one translation unit. Otherwise, when you get to link time, you end up with multiple definition errors like the one you cited. This same rule also applies to classes, types, and objects in general.

This would seem to preclude the use of templates at all; they must be fully included in every translation unit in which they are used. However, the ODR makes an exception for a few cases. Quoting from Wikipedia:

Some things, like types, templates, and extern inline functions, can be defined in more than one translation unit. For a given entity, each definition must be the same. Non-extern objects and functions in different translation units are different entities, even if their names and types are the same.

This is why you don't run into multiple definition errors with the template functions. At link time, the linker finds the duplicate symbol definitions and removes all duplicates (as long as they are all equivalent; otherwise, this would be an error). Therefore, your program links successfully with exactly one definition of each required symbol.

For your case, your problem occurs because you are including non-template functions in more than one translation unit (everywhere that the .cpp file is included). There would be a few ways of fixing this:

  1. If the template functions are part of a class, you could move the non-template functions to lie in that class as well. This would bring it under the symbol-deduplicating umbrella of the owning template class.

  2. Mark the functions as inline.

  3. Break the non-template functions out into another .cpp file that you then compile separately. That will be the only translation unit that houses them.

Jason R
  • 11,159
  • 6
  • 50
  • 81
  • Thank you for the very nice reply. Unfortunately, the functions were not part of the class (I was implementing some algorithms that _work on structures_, but can not be considered (parts of) structures themselves). I also taught of separating them into another `.cpp` file as a last scenario solution. But surprisingly, when `C::B` is configured properly, the proposed @πάνταῥεῖ solution works -- I was just so surprised by it that I had a hard time accepting it was correct (I am very ashamed to say). – penelope Aug 05 '14 at 16:26
0

As a good rule of thumb:

"Never ever include a .cpp file."

You shouldn't give these template implementation files a .cpp extension. Your build system might include them automatically then.

The conventionally used extensions for such files are e.g. .tcc or .icc. Add these to your project, as you would add other header files.
Don't include them in the project as separately build and linked translation units, if they are used with an #include statement from another header file.

UPDATE:
As you were asking for Code Blocks specifically, you'll just need to tweak your file extension settings a bit, to get these files included in your project correctly, and have them syntax colored as usual:

1. Add the new file type to the file extension settings enter image description here

2. Add the file type to the project file extension settings Project->Project Tree->Edit file types & categories
enter image description here

As soon a .tcc file is added to the project now, it will be opened using the text editor, and syntax colored as usual:
enter image description here

The corresponding .hpp file looks like this enter image description here

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • @penelope _'my build system for some reason refuses to include .tcc or .tpp files in the project at all'_ That's exactly the point, you **shouldn't** include these files to be build and linked separately if you're `#include`ing them in the header file. You didn't tell anything about your concrete IDE/build system BTW. – πάντα ῥεῖ Aug 05 '14 at 13:51
  • I added info about my IDE and compiler. The implementations of templated functions _should_ be included (automatically or otherwise). Renaming my file from '.cpp' to '.tcc' or '.icc' causes them to be un-managable by my IDE `Code::Blocks`, which refuses to let me see them as files belonging to my project, stops coloring the code and does not treat the as files containing source code at all. As far as `C::B` is concerned, they might as well be named `.txt`. When compiling by hand, changing the extension to `.tcc` or `.icc` (while leaving other things the same` does not change a darn thing. – penelope Aug 05 '14 at 14:12
  • Actually, this works perfectly. I am so so sorry, I am a bit nervous today for no good reason, and I think I might have been a bit rude. This works like a charm -- and also solves my long standing `C::B` problem with `.tcc` / `.tpp` files (I don't know why, but I was always taught to name them `.tpp` and not `.tcc`). Also, I deleted most of my old comments to your post. – penelope Aug 05 '14 at 16:16
  • Coincidentally, just for info, it also works like a charm when I tell `C::B` to treat `.tpp` files as "sources" -- because that is what it is conceptually, it contains the implementations even tho it has to be included with the headers at compile time. We might think about cleaning out our obsolete comments now -- and again, sorry if I was rude. – penelope Aug 05 '14 at 16:21
  • @penelope I felt no rudeness, just didn't really get the downvote. I just intended to show what's _the right way to do_ IMHO, and pointed out the essentials (don't use such as separate TUs). – πάντα ῥεῖ Aug 05 '14 at 16:24
  • Once you explained it, I reversed my downvote. Once I've tried it, I upvoted and accepted it -- now I don't get the downvotes either. But, while I'm picking your brain -- do you see anything wrong with including such `.tpp` files as "sources" instead of "headers" (explanation in my last comment)? It works equally fine once `C::B` is set up. – penelope Aug 05 '14 at 16:30
  • @penelope I'm not that well experienced with CodeBlocks actually (I have the installation only to answer such questions ;) ), but when I categorized such file as `C++ Source Files` in Eclipse CDT, they were handled wrong for automatic build generation and such. When marking these as headers though, the indexer seemed to have some problems, but I considered this the minor bad then. I'd say just use what works best for you in your current environment. – πάντα ῥεῖ Aug 05 '14 at 16:35
  • Hey! Sorry, but I had to unaccept your answer, as I discovered today it does not actually solve my problem: as soon as I include my `.h` file in multiple other sources, the problem shows up again. (Your solution only worked when I included this pair of `.h/.tpp` in one other source file, such as `main.cpp` for example) I updated my question with these additional details. – penelope Aug 09 '14 at 12:28
0

In general you should compile .cpp file separately and link it later. But if you want (or must) do it this way, mark all non-template functions as inline. This should help.

Daniel Frużyński
  • 2,091
  • 19
  • 28
  • When not working with templates, of course `.cpp` files are compiled and linked separately. I would have called this file `.tpp` but my build system does not allow it (that's why, unlike any of my other files, it's in a folder _tpp_). `inline` does help, however, isn't it bad practice to use `inline` with big functions? (I taught it should be only used with small functions) -- is this the only solution? – penelope Aug 05 '14 at 13:50
  • 1
    `inline` is hint for compiler, and it still can decide (by using its heuristics) that function is too big for inlining. There are also some cases when function cannot be inlined, e.g. when function address is needed. BTW, If you do not want to use `inline` at all, there is one more solution - wrap your non-template functions inside `#ifdef ALGO_IMPL`, and in one of files which include algo.h add `#define ALGO_IMPL` before `#include "algo.h"`. After this change these functions will be compiled only once. – Daniel Frużyński Aug 05 '14 at 14:20
  • I like the idea of that solution -- except one small thing: I really would not like to force the end-user to `#define ALGO_IMPL`. The idea is to `#include algo.h` in `main.cpp` (or wherever I would like to use if from finally). Is there any similar workaround which only modifies the `algo.h` / `tpp/algo.cpp` files? – penelope Aug 05 '14 at 14:36
  • If you want to touch algo.h/algo.cpp files only, you have to make functions inline. You can also try another solution - compile these code as part of your library, or create a new one only with them. This way end users will not have to compile anything, only link with your library. And you will be able to choose any method to compile these files, e.g. include all of them from `mocks.cpp` or anything else. – Daniel Frużyński Aug 05 '14 at 15:00