Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping C++ template function

I try to wrap a template function with the help of GNU's linker wrap option. The code looks like this:

// f.h
template<typename T>
void f(T t) {
}

// bar.h
void bar();

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

void bar() {
  f(42);
}

// test.cpp
extern "C" {
  extern void __real__Z1fIiEvT_(int i);
  void __wrap__Z1fIiEvT_(int i) {
    __real__Z1fIiEvT_(i);
  }
}

int main() {
  bar();
}

The code shown above is linked with the following command:

g++ -Xlinker -wrap=_Z1fIiEvT_ -o test test.o bar.o 

Unfortunately, this does not work and always the original function f is called instead of my wrapped version __wrap__Z1fIiEvT_. Do you see any mistakes I've made?

Edited: As advised, I append the output of nm here to make sure that the I haven't done any mistakes with the mangled name of the template function:

$ g++ -c bar.cpp -o bar.o
$ nm bar.o
0000000000000000 W _Z1fIiEvT_
like image 468
Mike Avatar asked Jan 17 '12 14:01

Mike


2 Answers

From http://linux.die.net/man/1/ld:

--wrap=symbol
Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to "_wrap symbol ". Any undefined reference to "_real symbol " will be resolved to symbol.

I think the word "undefined" could be the key here. Your symbol of interest is certainly defined in bar.o, and the nm output confirms it, as undefined symbols are marked with "U", not "W".


Update:

I guess it is therefore not possible to wrap template functions?

I think it depends more on where the function is defined (or instantiated), and if this definition is available to the linker. In your case, if you defined a non-template function in bar.cpp where it is used, the result would be the same. Even if you defined the function in bar.cpp but used it in main.cpp, I guess it would be the same, though not completely sure (you may give it a try). And I am sure that if you linked bar.cpp and main.cpp into different modules (a shared library and an executable), then you would be able to wrap a function from bar.cpp used in main.cpp, again no matter whether it's template or not.


Update 2: I was not sure, but Mike confirmed with experiment (see his own answer and the comments there) that wrapping works if the symbol is undefined in an object file, even if that object file is linked together with another object file containing the symbol definition. Great!

like image 175
Alexey Kukanov Avatar answered Oct 24 '22 03:10

Alexey Kukanov


The comments were helpful, but I don't think it is necessary to split it into an executable and a (shared) library. The key is to have a forward declaration for the template function in the calling origin and to instantiate the template function with the used type in a separate translation unit. This guarantees that f is undefined in bar.o:

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

template<typename T> void f(T);

void bar() {
 f(42);
}

//f.h
template<typename T>
void f(T t) {
}

//f.cpp
#include "f.h"
template void f(int);


$ nm bar.o
                 U _Z1fIiEvT_
0000000000000000 T _Z3barv
like image 1
Mike Avatar answered Oct 24 '22 02:10

Mike