Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it clear that a template function instantiation will not be inlined?

Tags:

c++

templates

Regarding Function passed as template argument, the community wiki answer provided by Ben Supnik discusses the issue of inlining instantiated function templates.

In that answer is the following code:

template<typename OP>
int do_op(int a, int b, OP op)
{
  return op(a,b,);
}

int add(int a, b) { return a + b; }

int (* func_ptr)(int, int) = add;

int c = do_op(4,5,func_ptr);

The answer goes on to say this (in regards to the final line, which instantiates the function template do_op):

clearly this is not getting inlined.

My question is this: Why is it clear that this is not getting inlined?

like image 315
Dan Nissenbaum Avatar asked Dec 02 '12 23:12

Dan Nissenbaum


2 Answers

What he's saying (I think) is that the add function is not getting inlined. In other words, the compiler might inline do_op like this:

int c = func_ptr(4, 5);

but it won't also inline add like this:

int c = 4 + 5;

However, he might be wrong in that simple example.

Generally, when you call a function through a pointer, the compiler can't know (at compile-time) what function you'll be calling, so it can't inline the function. Example:

void f1() { ... }
void f2() { ... }

void callThroughPointer() {
    int i = arc4random_uniform(2);
    void (*f)() = i ? f2 : f1;
    f();
}

Here, the compiler cannot know whether callThroughPointer will call f1 or f2, so there is no way for it to inline either f1 or f2 in callThroughPointer.

However, if the compiler can prove at compile-time what function will be called, it is allowed to inline the function. Example:

void f1() { ... }
void f2() { ... }

void callThroughPointer2() {
    int i = arc4random_uniform(2);
    void (*f)() = i ? f2 : f1;
    f = f1;
    f();
}

Here, the compiler can prove that f will always be f1, so it's allowed to inline f1 into callThroughPointer2. (That doesn't mean it will inline f1…)

Similarly, in the example you quoted in your post, the compiler can prove that func_ptr is always add in the call to do_op, so it's allowed to inline add. (That doesn't mean it will inline add…)

like image 155
rob mayoff Avatar answered Nov 10 '22 00:11

rob mayoff


When calling a function through a function pointer, the compiler is highly unlikely to avoid the call through the function pointer. Only if the compiler can prove that it knows what the function pointer is being initialized with and that it cannot be changed, it could possibly avoid the function call through the function pointer and, thus, inline the function. In the quoted setup, i.e.,

int (* func_ptr)(int, int) = add;

the function pointer func_ptr is modifiable and the compiler is, thus, not guaranteed that it will never change. As a result, it cannot possibly inline the call to add.

If the snippet of code is, indeed, complete, things happen during initialization and the compiler could actually, indeed, know that func_ptr is initialized to contain add.

like image 33
Dietmar Kühl Avatar answered Nov 10 '22 02:11

Dietmar Kühl