BETTER Header Files and Preprocessor Debugging

preview_player
Показать описание


💰 Links to stuff I use:

This video is sponsored by Brilliant.
Рекомендации по теме
Комментарии
Автор

Defining a function in the header (inline) makes it more likely to be inlined simply because it's definition is accessible in other translation units. If the compiler compiles a file that includes the header containing that function and the body is not in there, it can't inline it, unless you enable link time optimization (LTO), which is usually pretty slow and only enabled for release builds. But you can enable and should LTO for release builds, so while the remark makes sense, it's not very relevant.

allNicksAlreadyTaken
Автор

20:33 a little tip here: if you don't have visual assist, you can still create definition by pressing "Alt+Enter" (opens quick fix dialog with first option to create definiton) and pressing "Enter" again, but annoyingly it opens the definiton in a preview window instead of a new tab. If you want the definition to be opened in a new tab then go to "Tools>Options>Text Editor>C/C++>Advanced->Refactoring”, set option “Navigation after Create Declaration/Definition” to “Open Document”. And here you go:)

kostiapereguda
Автор

Thanks Cherno. I have learned c++ by your videos as a hobby. I am studying electronic engineering and next semester we are going to do a bit of c++ and I am sure I will be able to nail it because of all your videos that explain so much. Keep it up like it when you get deep ;)

gideonjonkers
Автор

When it's inlined in the header, the compiled has a chance to inline it. Otherwise the linker is the only component that can inline it.
This is why the LTO is popular.

adama
Автор

1:33 I think part of the reason java can just do this is the built in javadoc support. Even if there is not a lot of actual 'documentation' written the generated documents can be a great way to overview classes.

gwch
Автор

Have you looked into C++20 modules ? would be interesting to see how we would change the way we write code (I know I know not fully supported yet but i saw that libfmt was able to be modularized for the major compilers, although i dunno if premake support them yet)

TheJoKeR
Автор

Nowadays, in Visual Studio 2022, you can hover over a macro and it gives you the option to expand it out to its evaluated state. It is great for checking it is working as intended

bencrabbe
Автор

6:20, 6:30 I heard all the same things. Because of all the ambiguity, if I REALLY want something "inlined" and it's relatively small (2-5 lines) I implement it as a macro so there's no doubt what the compiler is doing with it.

CR
Автор

At work we use a lot of forward declarations reducing the amount includes. It is a pain having a code style change in a header file that triggers a waterfall of compilations.

deeplazydev
Автор

Here are my 50 cent about inline functions and why they belong into the header file. Maybe this is already outdated, but that is what I learned about it - long time ago. Imagine you have two compilation units a.cpp and b.cpp and a.cpp contains the inline function. Both units are compiled independently, it's not that the compiler compiles all compilation units at the same time, it compiles each single file individually into the corresponding a.o and b.o (or for windows a.obj and b.obj). And by individually I mean it's a separate compiler execution for each compilation unit. So when compiling b.cpp the compiler does not know anything about a.obj, but the compiler needs to know the body of the function to decide if it is worth to inline it or if it is better to use a function call. That is way the inline function is forced to be in the header, so that each single compilation unit can know the function body.

And just to mention it, this also brings disadvantages to put functions in the header. If they do not get inlined, you end up with the same function in every single object file, which is an automated way of code duplication and can end up increasing your binary size. Just to avoid anyone saying "Oh but then just put every function in the header.".

If this is not right or no longer right, please let me know. Always willing to learn something new.

dradux
Автор

Dude, I love you and appreciate these videos so much. Thank you for creating and sharing the series!

bobbyv
Автор

I love this new way of titling, where you put the topic your covering instead of like an ep #

unmeinks
Автор

My theory for the declared functions in the header having more chances to be inline is that it is visible in other translation units. if you define the function in a header file (and use something like __forceinline) and declare it on a cpp file, this inline function won't be visible on other cpp files.

guitarcat
Автор

2:30 There is good analogy for that. Epigraph and content page in books. It comes first to tell you what you can get from it.

severgun
Автор

Here's why the compiler is more likely to inline header code than source file code:
If it's in the header, the compiler has access to the implementation, so it can be done at compile time.
If it's in the source file, then it has to be done at link time. Unless you enable LTO, there is much less guarantee that this happens, especially on less mainstream platforms for your target toolchain.

The inline function in methods is a weird one, I don't really see why you'd use it most of the time. In non-member functions in headers it's basically required (static works too, but then you have a duplicate implementation floating around for each source file that includes it, which may be what you want in some cases, but then you're probably doing black magic involving sacrificing babies).

nextlifeonearth
Автор

I believe it is actually impossible to inline the function when it is in a different compilation unit. It would HAVE TO be done by the linker. So unless linker optimizations can do inlining, it is impossible. Not sure if linker does inlining or not.

tomasruzicka
Автор

You probably won't read this comment, but I'd love to share the reason why some methods are implemented in the header file. Unreal Engine is a great example. The code is divided into modules that are dynamically loaded, such as DLL files. Modules that depend on other modules can access their header files, but the implementation (the CPP file) is only available during the compilation of the module that owns the header file. If a header file contains only the declaration for method M and another module includes M, it won't be able to inline this method because it lacks the definition. The definition is provided through dynamic linking. However, if the definition is in the header, the compiler can inline this method for the dependent modules.

More commonly used and smaller methods will likely have their definitions in the header file. Deciding to inline every possible method could cause your libraries to consume more memory since methods will not be shared. Thus, this represents a tradeoff between performance and memory usage.

Thefmc
Автор

How your visual studio look soo good, which theme is this

ashutoshchar
Автор

Compile time can be a big deal. I've worked on projects that started as a few seconds to build, then a few minutes, eventually getting to several hours and for one particularly poor performing compiler a few days. The geometric growth of number of lines the compiler needs to parse can easily get out of hand. Modern compilers do a lot better job, especially with multiple cores. The standard practice is to use #include guards or the #pragma once. I tend to use #include guards these days. The second thing is to use forward declarations where possible. This works well if your header doesn't need to see the full class definition, such as if you have only a pointer or reference to a class. At one point I wrote an analysis tool that walked include paths and counted number of lines each header contributed to a build. In doing this I could then focus on eliminating the heavy hitters. The decrease in build time that was realized was more than worth the time. These days I tend not to work on such large applications and compilers tend to handle things better.

For you discussion on in header definition of functions. A compiler can't inline what it hasn't seen. Depending on the compiler, that usually means that a function fully defined in the header is a candidate for inlining. However, if a compiler uses some sort of compilation database, which I believe Visual Studio does, then it may be able to inline functions that are not defined in a header.

One tactic that I have used is to always put every function definition into the CPP file until I find that performance can be improved by putting it in the header and marking it inline. Most of the time inline functions don't buy you much performance gain. The main exception is cases that are very trivial like accessing a member variable.

The first thought I had when you showed the header is that I would use the using statement to simplify those complicated container declarations. A simple example might be if I have a vector of std::unique<MyClass>. I might declare using Ptr = std::unique<MyClass>: followed by using MyClassVector = std::vector<Ptr>; This pattern also makes it a lot easier to swap out the container later on. It's basically the same as typedef, but with a much easier to remember syntax.

I also prefer putting the member data and private functions at the end like you demonstrated and for most of the same reasons. The public interface should be all you need to use a class. I apply that idea to documenting code as well. If I can't document a class such that anyone using it won't need to see the header or other source, then I probably need to improve the API to make it simpler.

dougpark
Автор

Cherno with proper support coming for modules soon(TM) for C++, what are your thoughts on it? It claims it will reduce the messiness of inclues(which just copy paste whole files) from what I heard.

gwch