views:

161

answers:

3

I've been developing a library of mostly template functions and managed to keep things organized (to some extent) in the following manner:

// MyLib.h
class MyLib
{
  template<class T>
  static void Func1()
  {
  }

  template<class T>
  static void Func2()
  {
  }
};

And obviously calls would be made like this:

MyLib::Func1();

As you can see, this can get quite ugly as more functions are added. At the very least, I'd like to separate it into different files!

I initially considered defining batches of functions in separate files in the MyLib namespace and then using a MyLib.h to consolidate all of them but I kept getting truckloads of linker errors - of course, I can take a closer look at this approach if it's recommended.

Any thoughts?

PS: Since most of these functions have different objectives it doesn't make sense to group them under a class from which we'd instantiate objects. I've used a class here so I won't have to worry about the order in which I've defined the functions (there is interdependence among functions within MyLib as well).

Linker Errors:

So the basic structure's like this: I have two classes (say A & B) which compile to static libraries and a master application which runs instances of these classes. These classes A & B use functions in MyLib. When A & B are compiling I get the LNK4006 warning which states that symbols belonging to MyLib have already been defined in an OBJ file within the project and it's ignoring it.

When it comes down to the application it becomes an LNK2005 error which states that it's already defined in the OBJ files of A & B.

UPDATE: Thank you Mike & Mathieu for the inline idea - it was the problem!

Except for one issue: I have some template functions which I've explicitly specialized and these are causing the already defined error (LNK2005):

template<class t> int Cvt(){}
template<> int Cvt<unsigned char>(){return 1;}
template<> int Cvt<char>(){return 2;}
template<> int Cvt<unsigned short>(){return 3;}

Any ideas?

Conlusion:

Solved the explicit specialization problem by defining the template functions in a separate file - thanks for the help!

+4  A: 

The MyLib namespace is the obvious way to go - after all, that's basically how the standard library, which is probably considerably bigger than yours does it. it's unusual to get lots of linker errors with templates, unless you have lots of forward declarations - something you should generally try to avoid.

anon
`std`'s definitely bigger :) - thanks, I take a closer look at the way of the namespace!
Jacob
I don't have any forward declarations - I've added a description of the linker errors I've been getting.
Jacob
+2  A: 

Using a namespace the right approach. Personally, I wouldn't consolidate them into one "include the whole world" header, as that will tend to increase compile times. Others might prefer the convenience of a single header.

If there are any non-template functions, then they must either be declared inline, or implemented in only one source file. Template functions, and class member functions implemented inside the class definition, are implicitly inline, but other functions aren't.

Mike Seymour
Thank you for the **inline** idea! I still have problems with explicit specialization... any thoughts?
Jacob
+3  A: 

You should prefer the namespace over your class with static methods:

  • namespace offer you the possibility to be shared among several files, one per logical group of methods
  • namespace may be omitted: either because ADL kicks in or with using myNamespace::MyFunc; (note: it's bad practice to write using myNamespace;, and you should shun the practice)

Now, let's speak of organization:

  • it's good practice to have your file hierarchy shadowing the namespace hierarchy [1]
  • it's good practice to split your methods by logical groups, so that the user does not have to include the whole world just because he wanted Hello, World! to be printed, commodity headers can help though (ie, headers that do a bunch of includes for lazy programmers to use)

[1] Here is what I mean:

#include "lib/string/manip.hpp"    // Okay, this files come from "lib"

int main(int argc, char* argv[])
{
  std::string s;
  lib::string::manip(s);           // Same hierarchy, easy to remember the header
  return 0;
}

A motivating example ? Boost does it (with commodity headers).

And what's more this does not cost much: just replace class by namespace and remove the static keywords, that's all folks.

For the linker problem: all methods that are not templated should be either declared as inline (try to avoid it unless they're one-liners) or be defined outside of the header (in a separate .cpp file).

UPDATE:

The problem of template specialization is that you end up defining a now "normal" method: there is nothing template about it any longer once you've fixed each and every parameter. The solution is thus to do like you did for normal functions: declaration in a header file and definition in a source file (and thus only once).

To be a bit more specific about this strange error: the problem of C++ is that each source files is compiled in isolation: the preprocessor will take the include and actually create a single text file that will contain every single included file (in order) and then your source at the end. The compiler takes this file and produces a ".o" file (for gcc). Then the linker kicks in and try to actually make a library (or binary) out of all these ".o" files, and it checks that each method is only defined once because otherwise how would it choose between the multiple definitions (unfortunately does not check if they are equivalent or not...) ?

There is a special allowance for template methods and classes though, and it picks up one (at random) among all of the instantiations (one instantiation for each combination of template parameters). Of course, this assumes that all of them are identical and you might end up with quite a headache for something like:

// foo.h
template <class T> int foo(T) { return 10; }

// foo.cpp
#include "foo.h"

char a;
std::cout << foo(a) << std::endl;

// bar.cpp
#include "foo.h"

template <> int foo<char>(char) { return 20; }

char b;
std::cout << foo(b) << std::endl;

Both lines will print the same output, whether it is 10 or 20 is unknown though, and could change between the builds!!!

Matthieu M.
Thanks for the detailed post as well as the inline suggestion, but as I've stated in the update I'm having problems with explicit specialization, any suggestions?
Jacob