-
-
Notifications
You must be signed in to change notification settings - Fork 234
Concept docs for code organization (closes #597) #598
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "blurb": "C++ code is often organized in header and source files and managed via include directives.", | ||
| "authors": [ | ||
| "vaeng" | ||
| ], | ||
| "contributors": [ | ||
| "b8horpet" | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # About | ||
|
|
||
| TODO: Copy from introduction.md after that is finished. | ||
| INFO: No real difference in the UX after the concept is finished. | ||
| If the about.md is placed with a higher visibility in the future, | ||
| this file should have a different treatment. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,163 @@ | ||||||
| # Introduction | ||||||
|
|
||||||
| Big projects often span over several files to keep the code organized in logical components. | ||||||
|
|
||||||
| ## Forward Declarations | ||||||
vaeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| C++ code is evaluated procedurally. | ||||||
| If you want to use a function, it has to be known to the compiler at the moment of usage. | ||||||
| Sometimes it is not possible to find a linear fashion to lay out the definitions in the source code. | ||||||
| Take a look at the example below: | ||||||
|
|
||||||
| ```cpp | ||||||
| int myFunction(int n) { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a general comment, I'd suggest sticking with the idiomatic way of naming things in C++ which is used by the standard library and things like Boost: use snake_case for most things. (So for example, this function might be called |
||||||
| if (n < 10) { | ||||||
| return n; | ||||||
| } else { | ||||||
| return myOtherFunction(n / 10); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| int myOtherFunction(int m) { | ||||||
| return myFunction(m / 2); | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| When `myFunction` is defined, the compiler does not know about `myOtherFunction` yet. | ||||||
| Unfortunately, the circle-reference problem cannot be solved by switching the order. | ||||||
|
|
||||||
| C++ offers **forward declarations** to let the compiler know of `myFunction` and `myOtherFunction` before they are defined. | ||||||
| The compiler assumes that the definition will follow at some later point after the declaration. | ||||||
| The next example shows how a forward declaration is used for functions. | ||||||
|
|
||||||
| ```cpp | ||||||
| int myFunction(int n); // Forward declaration of myFunction | ||||||
| int myOtherFunction(int m); // Forward declaration of myOtherFunction | ||||||
|
|
||||||
| // Definition of myFunction | ||||||
| int myFunction(int n) { | ||||||
| if (n < 0) { | ||||||
| return 0; | ||||||
| } else { | ||||||
| return myOtherFunction(n - 2); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // Definition of myOtherFunction | ||||||
| int myOtherFunction(int m) { | ||||||
| return myFunction(m / 2); | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Attention: the `;` is needed after the declaration, but not after the definition. | ||||||
|
|
||||||
| ## Header Files | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it might be better to call this "Header and Source Files", to really make the case for both type of files usually used in C++ programs. |
||||||
|
|
||||||
| Often, declarations are bundled together in header files. | ||||||
| The most common file extension for header files is `.h`. | ||||||
| Some projects use `.hpp` or skip the extension completely. | ||||||
vaeng marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it might be best to separate the talk about non-standard file extensions in a separate paragraph. Something a bit like this:
Not too use the content is perfect, so feel free to reword as appropriate. |
||||||
| The definitions are located in a separate `.cpp` file. | ||||||
|
|
||||||
|
|
||||||
| ## Includes | ||||||
|
|
||||||
| It is possible to include the content of other files with the `include` directive. | ||||||
| Includes should be stated at the top of the file. | ||||||
vaeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| The snippet below shows how to use the `example()` function that was declared in the `myProject.h` file. | ||||||
|
|
||||||
| ```cpp | ||||||
| #include "myProject.h" | ||||||
| example(); | ||||||
| ``` | ||||||
|
|
||||||
| If the `myProject.h` is located in the `some_folder` and not in the root directory, you would have to use `#include "some_folder/myProject.h"`. | ||||||
vaeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ## Include Guards | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a pretty advanced topic since it involves explaining the workings of the preprocessor, in a way. Did you plan to have a preprocessor topic at some point? If so I'd simplify this entry to avoid talking about the specifics and defer to that later topic.
Even then I'm not sure it's perfect. I don't really know how to explain this in an easy way... C++ is such a strange beast for most people used to more modern languages 😂
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Agreed! |
||||||
|
|
||||||
| It does not matter if the same file is included multiple times within a project. | ||||||
| Header files should not contain definitions. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a bit unsure what a definition here is pointing at, is it just a function definition or is there more stuff that comes in the "definitions" group?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are several entities that can be defined. Classes, enumeration, constants, functions. Would "not saying what can be defined" confuse you, or is that okay?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I have understood is this concept is very much about definition and declaration. In the language where I come from most of the time you would do the definition and declaration at the same time. There is some expectation but in general, this rule stays true. |
||||||
| The complete project cannot have the same definition more than once. | ||||||
vaeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| This is called the "One definition rule". | ||||||
| It will be enforced by the compiler. | ||||||
|
|
||||||
| It is easy to avoid multiple unintended inclusions of the same definition with include guards. | ||||||
| They are formed by a special procedure during the compilation stages. | ||||||
| The aim is to only include a file only if a certain variable has not been set and then set it once the file is included. | ||||||
| Often the sequence of this variable is chosen as a variation of the name of the file. | ||||||
| Another method is the generation of a UUID to reduce the risk of accidentally using the sequence twice. | ||||||
| The syntax can be seen below with `MY_HEADER_FILE_H` as a variable. | ||||||
|
|
||||||
| ```cpp | ||||||
| #ifndef MY_HEADER_FILE_H /* any name uniquely mapped to file name */ | ||||||
| #define MY_HEADER_FILE_H | ||||||
| // file content | ||||||
| #endif | ||||||
| ``` | ||||||
| A modern form, that will achieve the same result is the #pragma directive `#pragma once`. | ||||||
| The problem is, that pragmas are not an official part of the C++ language and the implementation differs from compiler to compiler. | ||||||
vaeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| Many big projects have switched to the simpler pragma method, but a few are still cautious. | ||||||
|
|
||||||
| ## Namespaces | ||||||
|
|
||||||
| An important method for code organization are namespaces. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would perhaps avoid the use of the word "method" here since it can be confused with the topic of member functions. Perhaps use the word "feature" or "tool"? |
||||||
| Two functions `int foo()` can be used side-by-side if they are defined in different namespaces. | ||||||
| They can be called with the scope resolution operator `::`. | ||||||
| Namespaces can be nested. | ||||||
| They are kept as they are after includes. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do we mean with this sentence? It's not clear to me. 🤔 |
||||||
|
|
||||||
| ```cpp | ||||||
| namespace my_ns { | ||||||
| int foo() { | ||||||
| return 44; | ||||||
| } | ||||||
| namespace my_inner_ns { | ||||||
| int baz() { | ||||||
| return 90; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| namespace my_other_ns { | ||||||
| int foo() { | ||||||
| return -2; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| int myresult{my_ns::foo() + my_other_ns::foo() * my_ns::my_inner_ns::baz()}; | ||||||
| ``` | ||||||
|
|
||||||
| Deeply nested namespaces might be too verbose. | ||||||
vaeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| It is possible to remove the verbosity with a namespace import via `using`. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd add a note here (perhaps in a second paragraph) to warn against the use of |
||||||
| This moves all names into the global namespace. | ||||||
| To keep some differentiation aliases might be useful. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Namespace aliasing seems to me like a topic we might want to keep until later (if at all). It's not widely used in C++ code (in my experience) so might be considered an optional feature. |
||||||
|
|
||||||
| ```cpp | ||||||
| int my_other_result{my_ns::my_inner_ns::baz()}; | ||||||
|
|
||||||
| using namespace my_ns::my_inner_ns; // importing the complete namespace | ||||||
| int also_other_result{baz()}; | ||||||
|
|
||||||
| namespace m = my_ns; // setting an alias | ||||||
| namespace o = my_other_ns; | ||||||
|
|
||||||
| int also_my_result{m::foo() + o::foo() * m::my_inner_ns::baz()}; | ||||||
| ``` | ||||||
|
|
||||||
| ## Standard Library Header Files | ||||||
|
|
||||||
| The standard library offers many common functions, algorithms, and data structures. | ||||||
| They are grouped into header files. | ||||||
| They are included with angled braces `<>` instead of double quotes `"`. | ||||||
| The difference with the braces is the location, where the compiler searches for the header files. | ||||||
| The search in the current project is skipped for the angled braces version, and it directly starts in the system's include directories. | ||||||
|
Comment on lines
+148
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this will feel pretty strange for most people coming from other languages. I don't really know if it's worth mentioning, but on the other hand it can be a source of error... I'm torn about this. How about mentioning that angle brackets are used to include library files (external to the project) while double-quotes are used to include project files, with perhaps a link to an external source (like cppreference)? |
||||||
| The standard library headers are located in the `std` namespace. | ||||||
vaeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| One example is the C numerics library, with its "cmath" header. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very nitpicky, but using double-quotes here could be confusing since you're mentioning angle brackets before for standard library headers. Perhaps do it like this:
Suggested change
|
||||||
| The header file has declarations of many common mathematical operations. | ||||||
|
|
||||||
| ```cpp | ||||||
| #include <cmath> | ||||||
|
|
||||||
| int cube_me(int a) { | ||||||
| // raise `a` to the third power | ||||||
| return std::pow(a, 3); | ||||||
| } | ||||||
| ``` | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| [ | ||
| { | ||
| "url": "https://en.cppreference.com/book/intro/namespaces", | ||
| "description": "More information on namespaces" | ||
| }, | ||
| { | ||
| "url": "https://en.cppreference.com/w/cpp/preprocessor/include", | ||
| "description": "More information on source file inclusion" | ||
| }, | ||
| { | ||
| "url": "https://learn.microsoft.com/en-us/cpp/cpp/header-files-cpp?view=msvc-170", | ||
| "description": "Examples and information on header files and include guards" | ||
| } | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is quite big. This seems to imply that either:
Ideally, the introduction should be fairly short.