3 ms·
Any guesses as to why C chose such crazy semantics for inline functions? "The compiler can decide to use the inline or extern definition" is begging for trouble
by ridiculous_fish 5y ago
Any guesses as to why C chose such crazy semantics for inline functions? "The compiler can decide to use the inline or extern definition" is begging for trouble.
"inline functions cannot have their address taken and cannot have static variables" seems so natural and obvious.
- klodolph 5y ago> "The compiler can decide to use the inline or extern definition" is begging for trouble. IMO this behavior seems like the obvious choice, but I've been writing C long enough that I'm sure my perspective is distorted and my sense of what is obvious is completely out of whack. C++ made the choice that linkers should be able to coalesce duplicates, C made the choice that linkers do not need this feature. If you want to inline, you want definitions in every translation unit. If you don't want to have to inline, you want to pick a translation unit which gets the code. The compiler is what chooses whether a function is inline, and programmers should not think of the "inline" keyword as affecting that choice. > "inline functions cannot have their address taken and cannot have static variables" seems so natural and obvious. Yes, obvious that they should not have static variables. There's nothing wrong with taking the address of an inline function, though. It behaves just like a normal function. Pointers to a function will compare equal if and only if they point to the same function--a function with external linkage is the same function in every translation unit, a function with internal linkage is a different function in every translation unit. Whether or not the function is inline does not matter (why would it?) This means that you can take functions in an existing C code base, and you can make them inline, without any change in functionality. This should always work, barring... you know... those weird unexpected consequences. Isn't that nice though? Whether a function is inline is a detail that you don't have to care about, unless you're defining the function. Remember that "inline" does not mean, "this function will be inlined". Instead it means, "the compiler may choose to inline this, or not, at its discretion." Which really just means, "the definition for this function is visible in the translation unit where it is called", because the compiler can generally choose to inline any function if the definition is there. So, the keyword could be renamed to reflect what it means. Instead of: // file.h inline int add_one(int x) { return x + 1; } // file.c int add_one(int x); You could choose a different name for the keyword: // file.h do_not_emit_code_in_this_translation_unit_by_default int add_one(int x) { return x + 1; } // file.c override_default_and_omit_code_in_this_translation_unit int add_one(int x); I suspect "inline" was considered easier to type.
- ridiculous_fish 5y ago> C++ made the choice that linkers should be able to coalesce duplicates Yeah and shared libraries are where it gets ugly. For example, libstdc++ has an empty string singleton `_S_empty_rep_storage`. Its headers compare a std::string's storage against this, by address, so the dynamic linker is on the hook to coalesce the empty string symbols. It doesn't always work, and then things break mysteriously when you have two copies of the same variable. > There's nothing wrong with taking the address of an inline function, though. It behaves just like a normal function. I think you're proposing that inline functions should have a single definition? This breaks header-only libraries, and also gets sticky with shared libs; whose symbol wins? If everything is one big static link then it's probably fine. If your compiler is deciding to emit code now, or link to future code, dependent on optimization level...it's painful!
- klodolph 5y ago> I think you're proposing that inline functions should have a single definition? This breaks header-only libraries, and also gets sticky with shared libs; whose symbol wins? You can have the definition in as many translation units as you like, it's just that only one of the definitions can have extern linkage. This works right now, today, both with header-only libraries and across shared library boundaries. If you take the address of an inline function defined this way, you just get the address of the extern linkage definition. The C compiler will do the right thing, as long as you keep in mind the limitations: - Anything static will get duplicated in each translation unit. (Regardless of whether something is inline--the "inline" keyword isn't relevant here.) - No two translation units have an extern definition for the same symbol. (Again, regardless of whether the symbol is an inline function.) All "inline" lets you do is two things: - You can define a function without creating an extern linkage symbol. - Static inline functions do not cause warnings if not used. So, with your shared library, you have to pick one translation unit in one library which gets the extern definition. This is the same restriction you have with non-inline functions--if you are linking a static library into two different shared libraries, and then combining these shared libraries, you will run into problems regardless of "inline". Or put another way... none of these issues are related to the "inline" keyword in C. The "inline" keyword does not create any new problems.