Skip to content

Instantly share code, notes, and snippets.

@bkaradzic
Last active December 8, 2023 03:24
Show Gist options
  • Save bkaradzic/6e5d447233137eeabaf240211aeb490c to your computer and use it in GitHub Desktop.
Save bkaradzic/6e5d447233137eeabaf240211aeb490c to your computer and use it in GitHub Desktop.
Notes on header files

Notes on header files

After over 20 years working with C/C++ I finally got clear idea how header files need to be organized. Most of the projects in C++ world simply dump everything in .h file, and most of my C++ code was organized this way. Lately I started to separate function declaration from implementation as it was done in C. Now .h headers are exclusevely intended for function and class declaration with doxygen documentation style comments. Inline implementation of functions, class method implementation, etc. goes into .inl headers or .cpp files.

For example .h file would contain only declaration and doxygen style documentation comment:

	/// Convert size in bytes to human readable string.
	void prettify(char* _out, int32_t _max, uint64_t _value)

Implementation if it's in .inl file would prefix function with inline:

	inline void prettify(char* _out, int32_t _max, uint64_t _value)
	{
		...
	}

Or if it's in .cpp file function would be implemented without inline:

	void prettify(char* _out, int32_t _max, uint64_t _value)
	{
		...
	}

This solves multiple issues. Immediately obvious one is that functions are trivially transferable between .inl and .cpp file with minimum modification, adding or removing inline keyword. Non-obvious part is that functions now can be declared in any order in .h file, it can be sorted by name, or grouped by some other logic, which improves documentation. You won't need to move whole functions around in some unnatural way just because functionA calls functionB. Documentation for functions is located in one place, it's easy to glance since function is one to a few lines of code. The only downside of this approach is that there is two places where function signature exist, which in case of inline functions wasn't the case.

Anyhow, it's not some huge discovery, rather notes since it's not common practice in C++ world.

@erichocean
Copy link

Why wouldn't you just have the inline function definitions at the bottom of the .h, where you'd otherwise include the .inl? I'm not seeing the purpose of a separate file, and it has all of the other benefits you outlined above…

@naruse
Copy link

naruse commented Feb 14, 2017

agree, why not have the inline declarations at the end of the .h?

@agauniyal
Copy link

.inl files aren't necessary here, you can put their content in header file itself.

@jcelerier
Copy link

Immediately obvious one is that functions are trivially transferable between .inl and .cpp file with minimum modification,

I don't know for you but transferring the implementation of a function from a header to a source file (and the other way around) is a 2-clicks or 1-keyboard-shortcut operation in any respectable IDE

@wrosecrans
Copy link

Most of the projects in C++ world simply dump everything in .h file, and most of my C++ code was organized this way.

Wat? This seems counterfactual. Most IDE's I've used default to creating a .cpp file and a header file when you select their "new class" function, and if you went out of your way to fight it you would frequently get build errors from ultiple definition when a header is included in multiple places and gets built twice because this violates the One Definition Rule. An exception would be template code, where you might need to put implementation mechanics into a publicly available header so that a user of the template can create arbitrary variations of it. But if only specific templates are needed you can use explicit template instantiation to keep the template code out of public headers that get included everywhere. You will also see things like the PIMPL idiom to reduce what needs to be in public headers. Among other things, this helps significantly improve compile times since the compiler only needs to touch an implementation once.

You won't need to move whole functions around in some unnatural way just because functionA calls functionB.

I can haz forward declarations?

@milleniumbug
Copy link

After over 20 years working with C/C++ I finally got clear idea how header files need to be organized.

This part is hilarious.

@bkaradzic
Copy link
Author

.inl files aren't necessary here, you can put their content in header file itself.

@agauniyal You're an idiot that didn't read this at all but just left the comment... The whole point of this note is to not do that, rather put inline implementation somewhere else so that .h file is neat and easy to comprehend.

@weaseltron-ahirst
Copy link

I've started moving to this too - but a large motivation being compile speed - particularly in the case of utility classes. As it's a lot quicker for the preprocessor to #include the now smaller, neater .h file than and not any weighty implementation details. Particularly for templates.
So I can declare a UtilityClass as a member of ContainingClass without feeling the guilt at #including lots of code every time ContainingClass.h is #included. ContainingClass.cpp will likely #include "UtilityClass.inl" but that just gets compiled the once.
Though I've not converted enough to get a time measurement as of yet.

@matt77hias
Copy link

@Weaseltron foobar.inl is included at the end of foobar.h, so the foobar.h is at least as big as before.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment