4

I'd like to provide a library that provides template code. But I would also like to keep the most possible the ownership of this code (generated code) when I can guess the usage of different usual types of my template. Here is an example of what I am trying to do:

lib1.h

#include <iostream>

template<int N>
void print_me() {
    std::cout << "I am function number " << N << std::endl;
}

lib1.cpp

#include "lib1.h"

/* Force symbols to be defined here. */
template void print_me<0>();
template void print_me<1>();

I compile my library using:

g++ -shared -fPIC lib1.cpp -o lib1.so

And when I use my library:

main.cpp

#include <lib1.h>

int main() {
    print_me<0>();
    print_me<1>();
    print_me<2>();
}

Compiled with:

g++ main.cpp -l1

Here I would expect that the symbol print_me<0>() and print_me<1>() are defined and used from lib1.so and print_me<2>() defined and used for my executable (checked with nm --defined-only). But it seems that this is not the case! The symbols for 0 and 1 are well defined in lib1.so but as weak symbols. And are redefined in my executable (0, 1 and 2) again, weak. That implies that the code for 0 and 1 for my executable is taken from main.cpp which is not what I want (I checked with specification in main.cpp).

Is there a way (in the lib1.h for instance) to say at compile time of main.cpp that the symbols are already defined somewhere and that it does not need to add these symbols?

joetde
  • 1,556
  • 1
  • 16
  • 26
  • Why do you need something like this? Both your library as well as the user share the same templates (and specializations!). Defining a template means to compile the eager instead on deamnd (lazy). Though the `print_me<0>` of your lib aswell of your user code is exactly the same, as the template they have been generated from is the same. – Sebastian Hoffmann Jan 31 '14 at 14:04
  • Does declaring `template<> void print_me<0>(); template<> void print_me<1>();` in header help ? – Jarod42 Jan 31 '14 at 14:16
  • Is it possible to use C++11 features? – Constructor Jan 31 '14 at 17:10
  • @Constructor C++03 preferred, but if you have an elegant solution in C++11, feel free to suggest :). – joetde Feb 01 '14 at 09:49
  • @Paranaix since the symbols are resolved at link time and are weak, the user of my library can "hack" my symbols just by specialization. And if I use my template function in lib1.so, he can change its behavior! – joetde Feb 01 '14 at 09:50

3 Answers3

3

I would separate the implementation and the interface, using template specialization:

lib1.h:

#include <iostream>
template <int T> void print_me(void);
template <> void print_me<0>(void);
template <> void print_me<1>(void);

lib1_internal.h (NOTE: this does not need to be disclosed):

#include <iostream>

template<int N>
void print_me_internal() {
    std::cout << "I am function number " << N << std::endl;
}

lib1.cpp:

#include "lib1.h"
#include "lib1_internal.h"

template <> void print_me<0>() {
  print_me_internal<0>();
}
template <> void print_me<1>() {
  print_me_internal<1>();
}

your main.cpp will correctly lead to a linker error:

$ g++ main.cpp -L. -l1
/tmp/ccZSDqkp.o: In function `main':
main.cpp:(.text+0xf): undefined reference to `void print_me<2>()'
collect2: ld returned 1 exit status

Just add the definition of template <int T> void print_me(void) in lib1.h in place of its declaration and it will be used whenever not found in the specialized versions.

Sigi
  • 4,826
  • 1
  • 19
  • 23
  • Specialization is not really necessary here...the fact that you need to repeatedly specialize the functions the same way bothers me...i started down that path, and then thought "What if I just wanted all values between 0 and 100?". In your solution, we'd need 100 specializations, which is just more annoying than 100 initialization to be worth avoiding. – IdeaHat Jan 31 '14 at 17:24
  • the OP asked for a method able to cope with types for which he can guess the usage. He clearly asked to compile them and store them in a .so library, so I think this solution address his problem. @Guillaume, what do you think about it? – Sigi Jan 31 '14 at 20:17
  • 1
    @Sigismondo Yes, your solution is working fine and does what I want. This is a bit too bad I need to create another header to be able to do that :(. I see two drawbacks in this solution. First, I will need to have two bodies: one in the internal header and one in the external header and duplicating code bothers me a bit. Second, the user is still able to specialize the template for 0 or 1 and "override" my code. – joetde Feb 01 '14 at 09:34
  • Note that you can just write the internal template in the .cpp: it's not intended to be shared - I'd write the code in that way however. I proposed this solution because I thought you wanted to store specialized code in the library - I don't get the point in doing it if the same code is in lib1.h, exposed to the uses of the library. However it's possible that a different solution exists, with which you use the unspecialized template in the specialized code (doing it directly is forbidden). Maybe [this post](http://stackoverflow.com/questions/4617518) can be useful. – Sigi Feb 01 '14 at 10:12
3

C++11 solution: use extern templates. Simply add these strings to your main.cpp file:

extern template void print_me<0>();
extern template void print_me<1>();

Thus you tell compiler not to instantiate print_me function template in main.cpp (for template arguments 0 and 1). So linker should search definition of void print_me<0>(); and void print_me<1>(); in other translation units.

Constructor
  • 7,273
  • 2
  • 24
  • 66
2

You have to hide the implementation in the header file.

//lib1.h    
template<int N>
void print_me();

//lib1.cpp
#include <iostream>
#include "lib1.h"
template <int printme>
void print_me()
{
  std::cout << printme << std::endl;
}

template void print_me<0>();
template void print_me<1>();

What is happening is how templates are typically used: they are only built when needed (else you'd have to build a print_me for all integers), so they figure out the implementation when the software runs (which is why it slows down compilation time so much, it rebuilds the class for every compilation unit that uses the template). As long as you have the definition of the template, you can build it for whatever argument you need. This is one of the few cases where hiding the definition in the .cpp file is semi valid, but the end user will typically get one hell of a useless error for print_me<2>(). It'd be better (imho) to use this framework (C++11):

//lib1.h
#include <type_traits>
template<int N>
typename std::enable_if<N == 1 || N == 0>::type print_me();

//lib1.cpp
#include <iostream>
#include "lib1.h"

template<int N>
typename std::enable_if<N == 1 || N == 0>::type print_me()
{
  std::cout << N << std::endl;
}

template void print_me<0>();
template void print_me<1>();

now the error is "no matching function for call to print_me()" which is a little better...you could do a nested function with a static_assert to make it even better.

IdeaHat
  • 7,641
  • 1
  • 22
  • 53
  • As described in Sigismondo's post, I should still be able to use the template of cases that I did not anticipated (like 2 in this example). It should not raise a compilation error. – joetde Feb 01 '14 at 09:54