Inline function definitions at link time

I am in the process of building LVGL with my own C compiler (C99) and wonder about the use of inline functions.
In C99 a static inline function is just local to a compilation unit. It is not exported unless there is a declaration to export it, which is a declaration of the function without the static keyword in one of the C files.
In LVGL there are plenty of inline functions and in cases where they are not inlined there will be a call to an external definition which does not exist at link time and I get a linker error about undefined symbol.
I am probably missing something here and would be happy to be enlightened about it.

My understanding is that there’s no need to export it. The static inline functions are all in header files, and thus whenever they are referenced, they should always be available to the compilation unit.

If it helps to explain it this way: we essentially use them as glorified macros.

I understand that part, but from what I understand it is not how C99 works.

I checked with the standard, C A Reference Manual (5th ed), various sources online, like discussions on Stack Overflow and Wikipedia and they agree on that a static inline function is never exported as a separate function. If it is not inlined, there will be a function call to an external definition and the way to get such external definition is to have it declared either without the inline keyword and using ‘extern inline fundecl’.
Wikipedia states that " In C99, a function defined inline will never, and a function defined extern inline will always, emit an externally visible function."

This is different from C++ which will emit tentative/weak definitions and have the linker resolve it. This is not how it works in C99. The rationale is that there was a desire that it should work with existing linkers.

Now, as there seems to be “no issues” from doing it the way it is done in LVGL. I suspect that many tool chains actually “cheat” on this and do emit tentative definitions for ‘static inline’ functions also for C99, but I would like to hear if this issue have come up before or if you have considered this “issue”, as I could not find anything when about I searched.

A couple of references:

https://www.keil.com/support/man/docs/armcc/armcc_chr1359124228026.htm

The keil link is pretty well written and is in line with what I read in C reference manual and the standard.

If I understood the problem correctly, the gist is that there is no external linkage here. All static inline functions are in header files. If a header file is included it’s “copy-pasted” by the compiler (pre-processor?) into the file that included it. Finally all static inline functions will be “copy-pasted” in a C file.

So they are not different from normal not-inlined static function in this regard. They are inlined to be sure that there is no overhead on calling these functions.

Yes, the problem is that there is no external linkage generated.

It works as long as they are inlined, but the compiler is not forced to inline a function and may decide to make an ordinary function call instead.

A static-inline function differs from a static function in that the compiler may assume it can be called without generating a definition for it in the current compilation unit (if it is not given an external linkage). The idea is that someplace else in the project that function is given external linkage and the linker can resolve it.

I suppose I may need to make a small example project and experiment with different compilers to see how they react. What compilers are people typically using with LVGL?

How? Isn’t a static function always local to the current compilation unit only, and therefore must always have a definition generated even if not inlined?

That is a very good point and most certainly the explanation. It needs to generate a local copy of the function in that case and there may be duplicates if it is used in more than one compilation unit.

Furthermore, the behavior I hinted about is true for inline functions (without the static keyword). I checked with various sources and they seem to agree on this.

Frankly, so far I couldn’t understand with 100% certainty how plain inline (non static) functions work. So I don’t use them. :slight_smile: But IMO static inlines are quite straight forward.

It seems they are similar, except that there is no local function generated unless you explicitly ask for it. You can request that in (one) C file and then it provides a single copy to the rest of the application. This is done using an extern declaration (prototype) of the function. I think it suffices to have a declaration without the inline keyword.

The differences are that as there is only one copy of the physical function, a function pointer to the function will compare equal. This is not necessarily the case for static inline functions which can have multiple copies and different function pointer values for the “same” function.

Another thing is that if you use inline and do not export the function, you will get a linker error. This can be beneficial if the function must be inlined as you will get an error trying to build the application if that is not the case.

Hmmm, I thought the gist of the inline functions is that there are as many copies of the functions as many places it is called. It avoid the cost of function calls and allows local optimizations for the compiler.

The concept of function pointers is really interesting here. I’ve never thought about it :smiley:

What I meant is “only one copy of a separate/standalone function”. You will of course have one “copy” each time it is inlined.