Skip to contentSkip to conversation

Function pointers to inline functions

DISCUSSION Shriramana Sharma 3 participants 12 messages Apr 4, 2007 – Jun 4, 2007

lore
Share & tools
lore
#0 lore raw

Hello.

As per my understanding, an inline function is replaced in place by the compiler with the body of the function, so it does not have a separate location in memory in contrast with a regular function. This being so, how is it possible to extract a pointer to an inline function and effectively use it?

See the attached two examples. They work and though it's a good thing for my project that I can extract a pointer to an inline function I do not understand how it is possible.

Please explain. Thank you.

Shriramana Sharma.

#include "stdio.h"

inline void square ( int a ) { printf ( "The square of %d is %d\n", a, a * a ) ; } inline void cube ( int a ) { printf ( "The cube of %d is %d\n", a, a * a * a ) ; } inline void fourthpower ( int a ) { printf ( "The fourth power of %d is %d\n", a, a * a * a * a ) ; }

void ( * functionpointers [ 3 ] ) ( int ) ;
/*
the above line declares an array called functionpointers that has three ( 3 ) elements which are all pointers ( * ) to functions which take a single integer as parameter ( int ) and return nothing ( void ) and do not modify the contents of the class in anyway ( const ) */

void executefunction ( int functionnumber, int inputforfunction )

{
	if ( functionnumber < 0 || functionnumber > 2 ) return ;
	( * functionpointers [ functionnumber ] ) ( inputforfunction ) ;
	// this line calls the function which is pointed ( * ) to by the functionnumber-th element of the functionpointers array which is a member of the object pointed to by the current object pointer ( this ) and passes the function the value of the variable inputforfunction as a parameter
}

int main ( void )

{
	functionpointers [ 0 ] = & square ;
	functionpointers [ 1 ] = & cube ;
	functionpointers [ 2 ] = & fourthpower ;
	
	int count ;
	for ( count = 0; count < 3; count ++ )
		executefunction ( count, 2 ) ;
}

#include "stdio.h"

inline int square ( int a ) { return a * a ; }
inline int cube ( int a ) { return a * a * a ; }
inline int fourthpower ( int a ) { return a * a * a * a ; }
inline int squarepluscube ( int a ) { return square ( a ) + cube ( a ) ; }
inline int squarepluscubeplusfourthpower ( int a ) { return squarepluscube ( a ) + fourthpower ( a ) ; }

int ( * functionpointers [ 5 ] ) ( int ) ;
/*
the above line declares an array called functionpointers that has three ( 3 ) elements which are all pointers ( * ) to functions which take a single integer as parameter ( int ) and return nothing ( void ) and do not modify the contents of the class in anyway ( const ) */

void executefunction ( int functionnumber, int inputforfunction )

{
	printf ( "%d\n", ( * functionpointers [ functionnumber ] ) ( inputforfunction ) ) ;
	// this line calls the function which is pointed ( * ) to by the functionnumber-th element of the functionpointers array which is a member of the object pointed to by the current object pointer ( this ) and passes the function the value of the variable inputforfunction as a parameter
}

int main ( void )

{
	functionpointers [ 0 ] = & square ;
	functionpointers [ 1 ] = & cube ;
	functionpointers [ 2 ] = & fourthpower ;
	functionpointers [ 3 ] = & squarepluscube ;
	functionpointers [ 4 ] = & squarepluscubeplusfourthpower ;
	
	int count ;
	for ( count = 0; count < 5; count ++ )
		executefunction ( count, 2 ) ;
}
#1 lore raw
Quoted 9 lines · Shriramana Sharma

As per my understanding, an inline function is replaced in place by the compiler with the body of the function, so it does not have a separate location in memory in contrast with a regular function. This being so, how is it possible to extract a pointer to an inline function and effectively use it?

See the attached two examples. They work and though it's a good thing for my project that I can extract a pointer to an inline function I do not understand how it is possible.

If you declare a function as "inline", the compiler will typically add a normal definition as well as inlining it. If you declare a function as "static inline", the compiler may omit the normal definition.

Also, inlining is only performed if the corresponding optimisations are enabled. Note the following gcc options:

`-fno-inline'
Don't pay attention to the `inline' keyword. Normally this option is used to keep the compiler from expanding any functions inline. Note that if you are not optimizing, no functions can be expanded inline.

`-finline-functions'
Integrate all simple functions into their callers. The compiler heuristically decides which functions are simple enough to be worth integrating in this way.

If all calls to a given function are integrated, and the function is declared `static', then the function is normally not output as assembler code in its own right.

Enabled at level `-O3'.

`-finline-limit=N'
By default, GCC limits the size of functions that can be inlined. This flag allows the control of this limit for functions that are explicitly marked as inline (i.e., marked with the inline keyword or defined within the class definition in c++). N is the size of functions that can be inlined in number of pseudo instructions (not counting parameter handling). The default value of N is 600. Increasing this value can result in more inlined code at the cost of compilation time and memory consumption. Decreasing usually makes the compilation faster and less code will be inlined (which presumably means slower programs). This option is particularly useful for programs that use inlining heavily such as those based on recursive templates with C++.

Inlining is actually controlled by a number of parameters, which may be specified individually by using --param NAME=VALUE'. The -finline-limit=N' option sets some of these parameters as follows:

`max-inline-insns-single'
is set to N/2.

`max-inline-insns-auto'
is set to N/2.

`min-inline-insns'
is set to 130 or N/4, whichever is smaller.

`max-inline-insns-rtl'
is set to N.

See below for a documentation of the individual parameters controlling inlining.

_Note:_ pseudo instruction represents, in this particular context, an abstract measurement of function's size. In no way, it represents a count of assembly instructions and as such its exact meaning might change from one release to an another.

`-fkeep-inline-functions'
Even if all calls to a given function are integrated, and the function is declared static', nevertheless output a separate run-time callable version of the function. This switch does not affect extern inline' functions.

Signature · 1 line
Glynn Clements <[email protected]>
#2 lore raw
Quoted 3 lines · Glynn Clements

If you declare a function as "inline", the compiler will typically add a normal definition as well as inlining it. If you declare a function as "static inline", the compiler may omit the normal definition.

Would the static keyword in such a case also restrict the visibility of that inline function to that file only?

I mean, if I use -fkeep-inline-functions to get the function output in the object file, and if the static keyword also meant that the function is visible only in the translation unit in which it appears, then -fkeep-inline-functions would be rendered useless, since the only conceivable (to me) purpose of outputting a run-time callable version of an inline function is to make it available to callers from *outside the translation unit*, such as a programmer using a library.

(In fact, I think the *only* conceivable use-case would be someone using a library since another translation unit in the same project as the one containing the inline function can very well use the definition of the inline function to directly inline the code into the caller -- why would they want a separate runtime callable version in that case? So the only valid case is a programmer using a library but not having access to the actual definition of an inline function via the API.)

So I presume that when static is used with inline, it does not retain its meaning of "local to this translation unit". Am I right?

Shriramana Sharma.

#3 lore raw
Quoted 6 lines · Shriramana Sharma
Quoted 3 lines

If you declare a function as "inline", the compiler will typically add a normal definition as well as inlining it. If you declare a function as "static inline", the compiler may omit the normal definition.

Would the static keyword in such a case also restrict the visibility of that inline function to that file only?

A function can only actually be inlined in the translation unit (source file plus any #include'd files) in which it occurs.

That's why you need "static inline" to eliminate the normal definition. A non-static inline function has to have an implementation in the object file so that it can be called from other object files.

Quoted 8 lines

I mean, if I use -fkeep-inline-functions to get the function output in the object file, and if the static keyword also meant that the function is visible only in the translation unit in which it appears, then -fkeep-inline-functions would be rendered useless, since the only conceivable (to me) purpose of outputting a run-time callable version of an inline function is to make it available to callers from *outside the translation unit*, such as a programmer using a library.

Quoted 4 lines

(In fact, I think the *only* conceivable use-case would be someone using a library since another translation unit in the same project as the one containing the inline function can very well use the definition of the inline function to directly inline the code into the caller

No, the compiler can only inline functions which are defined in the same translation unit. Inlining requires source code, and the compiler only has access to the source code for the current translation unit.

Quoted 7 lines

-- why would
they want a separate runtime callable version in that case? So the only valid case is a programmer using a library but not having access to the actual definition of an inline function via the API.)

So I presume that when static is used with inline, it does not retain its meaning of "local to this translation unit". Am I right?

No, "static" retains its original meaning.

There aren't many uses for -fkeep-inline-functions; the only one I can think of right now is if you want to be able to call the function from within a debugger (you can't call an inline function from a debugger).

Signature · 1 line
Glynn Clements <[email protected]>
#4 lore raw
Quoted 2 lines · Glynn Clements
Quoted 2 lines

I mean, if I use -fkeep-inline-functions to get the function output in the object file, and if the static keyword also meant that the function is visible only in the translation unit in which it appears, then -fkeep-inline-functions would be rendered useless,

... in the case of static inline functions.

Is this correct or no?

Would a function visible only in its translation unit be accessible from a debugger, as you say? I would test it myself, but I haven't any idea how to load just a single object file in gdb and test it so I am asking.

Shriramana Sharma.

#5 lore raw
Quoted 6 lines · Shriramana Sharma
Quoted 4 lines
Quoted 4 lines

I mean, if I use -fkeep-inline-functions to get the function output in the object file, and if the static keyword also meant that the function is visible only in the translation unit in which it appears, then -fkeep-inline-functions would be rendered useless,

... in the case of static inline functions.

Non-static inline functions are always generated so that they can be called from other translation units. It's only when a function is both static and inline that you can (normally) eliminate the function body altogether.

Quoted 4 lines

Is this correct or no?

Would a function visible only in its translation unit be accessible from a debugger, as you say?

Yes.

The debugger might still require the function to be in scope (i.e. the active stack frame must be in the same translation unit) in order to call it, but that's down to how the debugger resolves symbols which are entered by the user.

gdb doesn't have this requirement.

Quoted 2 lines

I would test it myself, but I haven't any idea
how to load just a single object file in gdb and test it so I am asking.

Add a minimal main.c so that you can run it as a program.

Signature · 1 line
Glynn Clements <[email protected]>
#6 lore raw
In reply to Glynn Clements
Quoted 2 lines · Glynn Clements

run-time callable version of the function. This switch does not affect `extern inline' functions.

What would be the purpose of "extern inline"?

I mean, inlining is done by the compiler, and extern is only useful to tell the linker to resolve references to a variable or function that is initially defined in another object file.

I tried putting an inline function *definition* in lib.cpp:

inline int foo ( void ) { return 2 ; }

and a *declaration* to that function qualified by extern inline in main.cpp:

# include <cstdio>
extern inline int foo ( void ) ;
int main ( void )
{
        printf ( "%d\n", foo () ) ;
}

The session transcript:

$ g++ -c main.cpp lib.cpp
main.cpp:3: warning: inline function ‘int foo()’ used but never defined
$ g++ -o main main.o lib.o
main.o: In function `main':
main.cpp:(.text+0x12): undefined reference to `foo()'
collect2: ld returned 1 exit status
$

So extern for inline functions does not work as for normal functions -- so what is the use of extern inline?

Shriramana Sharma.

-
To unsubscribe from this list: send the line "unsubscribe linux-c-programming" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#7 lore raw
In reply to Shriramana Sharma

Shriramana,

Quoted 11 lines · Shriramana Sharma

Hello.

As per my understanding, an inline function is replaced in place by the compiler with the body of the function, so it does not have a separate location in memory in contrast with a regular function. This being so, how is it possible to extract a pointer to an inline function and effectively use it?

See the attached two examples. They work and though it's a good thing for my project that I can extract a pointer to an inline function I do not understand how it is possible.

Whenever you request a pointer to an inline function the compiler will place a normal subroutine version of the inline's implementation in an object file and return its address. So, effectively, the function pointer does not point to an inline function at all.

\Steve

--

Steve Grägert <[email protected]>
Jabber xmpp://[email protected]
Internet http://eth0.graegert.com, http://blog.graegert.com
-
To unsubscribe from this list: send the line "unsubscribe linux-c-programming" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#8 lore raw

I did some experimentation with pointers to inline and static inline functions. Please unpack the attachment and run the build script.

You will observe that the nm files only differ in the last four lines, and that too in only three of them.

The difference is that the executable built from the source with just "inline" has the functions square, cube and fourthpower marked as "W" which according to man:nm means that "The symbol is a weak symbol that has not been specifically tagged as a weak object symbol".

In the same place, the executable built from the source with "static inline" has the same functions marked "t" which denotes a local symbol in the code section.

I actually expected static inline to make the entries for the functions disappear but OK, I'm calling pointers to those functions, so they won't disappear. But at least shouldn't they become weak objects, like they do for "inline"?

Though I do not profess to fully understand what a weak object is, (upon which matter I would like to learn more) it sounds like an object which does not have a full separate existence. If an inline function is such a weak object, how come a static inline one is not tagged as one?

Thanks.

Shriramana Sharma.

#9 lore raw
Quoted 11 lines · Shriramana Sharma

I did some experimentation with pointers to inline and static inline functions. Please unpack the attachment and run the build script.

You will observe that the nm files only differ in the last four lines, and that too in only three of them.

The difference is that the executable built from the source with just "inline" has the functions square, cube and fourthpower marked as "W" which according to man:nm means that "The symbol is a weak symbol that has not been specifically tagged as a weak object symbol".

Quoted 8 lines

In the same place, the executable built from the source with "static inline" has the same functions marked "t" which denotes a local symbol in the code section.

I actually expected static inline to make the entries for the functions disappear but OK, I'm calling pointers to those functions, so they won't disappear. But at least shouldn't they become weak objects, like they do for "inline"?

There's no point in making them weak if they're local.

Quoted 4 lines

Though I do not profess to fully understand what a weak object is, (upon which matter I would like to learn more) it sounds like an object which does not have a full separate existence. If an inline function is such a weak object, how come a static inline one is not tagged as one?

A weak symbol is one which is only used as a last resort. If another library defines a non-weak version of the same symbol, it will be used in preference to the weak version.

Signature · 1 line
Glynn Clements <[email protected]>
#10 lore raw
In reply to Steve Graegert
Quoted 3 lines · Steve Graegert

Whenever you request a pointer to an inline function the compiler will place a normal subroutine version of the inline's implementation in an object file and return its address.

Does this mean that the compiler will check the entire translation unit where the inline function is visible for pointer-request operations on the function and if any are present, the compiler will place a normal version in the object file?

This would mean that the function is output separately even if -finline-functions is used. Is that true?

Do I have a way of checking whether a function is output separately or not using nm? Do I just check to see whether the function name is there in the output or do I have to look for some flag on the function name? I am still not aware of the full depth of using nm.

Thanks as always,

Shriramana Sharma.

#11 lore raw
Quoted 8 lines · Shriramana Sharma
Quoted 3 lines

Whenever you request a pointer to an inline function the compiler will place a normal subroutine version of the inline's implementation in an object file and return its address.

Does this mean that the compiler will check the entire translation unit where the inline function is visible for pointer-request operations on the function and if any are present, the compiler will place a normal version in the object file?

Yes. A pointer to a function requires that the function actually exists somewhere in the process' address space. Inline functions don't "exist" in that sense.

Quoted 2 lines

This would mean that the function is output separately even if -finline-functions is used. Is that true?

Yes.

Quoted 4 lines

Do I have a way of checking whether a function is output separately or not using nm? Do I just check to see whether the function name is there in the output or do I have to look for some flag on the function name? I am still not aware of the full depth of using nm.

If the name appears in the output from "nm", then the function exists. If it's inlined, it won't appear in the output from "nm".

Signature · 1 line
Glynn Clements <[email protected]>