Skip to content
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

Get familiar with C++ #2

Open
12 tasks done
vicennial opened this issue Mar 24, 2019 · 6 comments
Open
12 tasks done

Get familiar with C++ #2

vicennial opened this issue Mar 24, 2019 · 6 comments

Comments

@vicennial
Copy link
Owner

vicennial commented Mar 24, 2019

@vicennial
Copy link
Owner Author

Michael VanLoon “STL Algorithms in Action ”

Every C++ developer should strive to learn and use STL to its maximum capability.
Some reasons to use STL are:

  • It is predefined and comes for "free" with GCC
  • It is a general purpose library containing many different functions for a variety of use cases and applications.
  • It is implemented by professionals of the C++ community and is tested by the millions of users who use the library.
  • Raw loops can be avoided by using STL. This avoids having the raw loop accidentally interfere with the surrounding code.
  • It makes the surrounding code easier to understand.

In STL, algorithms can be divided into:

  • Non-Modifying: These algorithms work directly on the iterators and do not modify any attribute of the container. Some examples are: all_of(), any_of(), for_each() etc.
  • Mutating: These algorithms can modify the container they work on. The primary example of this type would be transform().
  • Sorting: These algorithms are largely sorting related. sort() is a general sort function. Algorithms such as partial_sort(), nth_element() etc are also included here.
  • Numeric: Algorithms such as partial_sum() and accumulate() are defined here.
  • General Algorithms: STL also contains a huge number of general functions like rotate(), slide(), gather(), set_difference() etc. The beauty of these functions is that they are all coded extremely well and handle every edge case that a user might miss.

In the end, using STL ensure that the developer's code is neat, short and works as expected even in cases that the developer might not have been aware of. Also, multiple STL algorithms can be combined to perform very complicated instructions with relative ease.

@vicennial
Copy link
Owner Author

T. Winters & H. Wright “All Your Tests are Terrible..."

Tests are integral to not only maintaining a project but also for further expanding it. Tests ensure that the development of the project can continue without depending on the original developers.

A test must follow 5 properties to be considered a "good" test:

  • Correctness
  • Readability
  • Completeness
  • Demonstrability
  • Resilience

Correctness

The test must verify that the system requirements are actually met. Tests that depend on known bugs or faults in the system must not be included.

Readability

Tests should not contain excessive boilerplate code. It must be easy to read and understand for a user.
The test should be optimised for readability even if it takes more time to do so.
Furthermore, the goal of the test is not to display the advanced features of the testing framework. Keep it simple and straight to the point.

Completeness

Tests should not be created with the intention of them passing.
An optimal suite of tests include:

  • Common inputs
  • Corner/edge cases
  • Outlandish cases

Furthermore, completeness does not imply test every API call. They must only test the API which they are working on.

Demonstrability

While the developers have a deeper understanding of the code than common users, they must not use shortcuts or complicated examples as tests. A test is useless if a user cannot understand what it is trying to do.
Tests should be more like a demonstration which can show the user various different ways to use the API.

Resilience

A test failure must imply a problem in the system and should not be due to unrelated causes. We need to ensure that our tests are free from outside effects and can stand strong on their own.
Some examples of bad tests are:

  • Flaky Tests: These tests might give a different verdict every time they are run. For example, if one is calculating response time from a server and the server happens to be overloaded.
  • Brittle Tests: These tests might fail due to a completely unrelated code change.
  • Execution Order Dependent - These tests depend on the order of execution of other tests or some sequence in the program.
  • Non-Hermetic - These tests might give a different verdict if multiple users run the same test at the same time. These tests usually require access to a database to read/write some values.

In the end, a project can quickly spiral down if it lacks effective tests.
Tests, even bad tests, are always better than no tests.

@vicennial
Copy link
Owner Author

Kate Gregory “Stop Teaching C"

The premise of this talk is not about whether C is good language or not, but rather "Stop Teaching C When You Want to Teach C++".

It is a common misconception that C is a pre-requisite to C++. In fact, C-Style coding is a bad practice in C++.
A few reasons why learning C as a pre-requisite is not optimal:

  • Resource Acquisition is Initialisation is not present in C whereas it is a widely used paradigm in C++.
  • It makes C++ look hard and does not encourage modern C++ coding standards.
  • It obscures the learners from other modern languages as well.
  • Early usage of pointers only confuses learners and provides a false complexity to C++.

It is better to teach core C++ concepts directly rather than at the end.
Some advantages of this strategy are:

  • Beginners can think at a higher level of abstraction
  • STL containers can be used to think of a collection as a "thing" to enable easier understanding.
  • References can be used instead of pointers for polymorphism
  • When learning about scope, Resource Acquisition is Initialisation follows naturally.
  • Easier introduction to pointers through shared_ptr<> and unique_ptr<>.

With C++ taught first, beginners need not go through the complicated usage of printf, char* [], memory handling etc to understand the core concepts.

@vicennial
Copy link
Owner Author

Bjarne Stroustrup “Writing Good C++14”

Modern C++ 11/14/17 is almost completely different than the old C++. It is:

  • Easier to write and maintain.
  • Faster.
  • Able to express more with less code.

However, even with many new advanced features, many people still prefer using C++ in archaic styles or get lost in the details of the language.

One way to prevent this is to have 'rules' on writing code.
However, some drawbacks of typical 'rules' set by some organisations are:

  • They generally relate to layout or restrictions on language features and not about programming principles.
  • They are usually outdated and full of bad advice.
  • They are not understood by the users.
  • They are not supported by the tools.
  • They do not provide guidance.

Therefore, we need to specify "good" rules. A structure of a good rule would be:

  1. Reference - Unique number for each rule.
  2. Reason - Why does this rule exist?
  3. Example - How do you apply this rule?
  4. "Don't-do" alternatives - What to avoid?
  5. Exceptions
  6. Enforcement - How is this rule enforced?
  7. See also - Related materials
  8. Note
  9. Discussion - To discuss matters about the rule.

Some properties of a good rule set are:

  • Common users can imitate experienced programmers under the guidance of the rules.
  • Eliminate whole classes of problems such as type/resource safety, dangling pointers, out-of-access issues etc.
  • Simplify code through the use of consistent style and discouragement of exotic coding techniques.

However, a rule set cannot be too large because people will not be able to apply all of them. Therefore, a set of "core" rules should exist.
These core rules could focus on making sure that there are no resource leaks, dangling pointers or any type violations.

ISO C++ has these core rules that one should follow for writing good C++.
With these rules followed, there would be:

  • Type & resource safety
  • No dangling pointers
  • No ownership leaks
  • Eliminate range errors
  • Eliminate null pointers

Since there are quite a lot of rules, a person cannot be expected to remember all of them. Instead, tools should be able to know these rules and should be able to point them out.
For ISOC++, clang-tidy is one such tool.

It is not optimal to try building a new language out of these rules since competing with C++ is extremely hard and could split the CPP community.
Instead, it is advised to follow these rules in normal C++.

Therefore, good C++ code can be written by following the guidelines of ISO C++.

@vicennial
Copy link
Owner Author

Herb Sutter "Back to the Basics! Essentials of Modern C++ Style"

To have a good understanding of Modern C++, the speaker recommends the book " A Tour of C++ " By Bjarne Stroustrup.

Some aspects of the modern C++ coding style:

  • Clear code should be prioritized over the optimal code.
  • However, write efficient code by default.
  • Do not overthink.

Loops

Range-based for loops exist in modern C++ and should be used when iterating over collections rather than a typical iteration-based loop. However, if early termination is required, an iteration-based loop is still a good choice.

Pointers & References

In modern C++, there is a tendency to overuse smart pointers rather than the raw * and &. However, this is not always the best practice since there is a performance cost to using smart pointers.

Some guidelines for using pointers are:

  • Use make_unique by default and make_shared when required instead of new + delete.
  • Avoid using owning raw pointers except for low-level data structures.
  • Do use non-owning raw pointers and references especially for parameters.
  • Do not copy or assign ref-counted smart pointers.
  • Use unique_ptr where ever possible because it is very lightweight and safe to use.

Variable Declaration

It is good practice to use the auto keyword where ever possible.
The advantages of doing this are"

  • Correctness & Maintainability: We do not have to worry about the variable type when we use auto. This is especially useful when we want to store the result of a function call.
  • No implicit conversion is made: Due to this, the compiler can optimise the code better.
  • Usability & Convenience: The auto keyword is very easy and convenient to use when dealing with lambdas or any objects which have long and complex variable declarations.

It is also to be noted that it is very rare that the usage of auto results in a performance drop.
One reason could be that auto x = a; is the same as writing auto x(a);. Thus, the object is not made and copied as the former code block indicates.

Parameter Passing

In old C++, return-by-value was rarely used due to costly copy operations.
However, in recent years Return Value Optimisations takes care of the issue and in Modern C++, it is better to use return by value and let the compiler handle the optimisation.

In short, the defaults of C++ work better than we expect and we should try to adopt them more in our code.

@vicennial
Copy link
Owner Author

Bjarne Stroustrup - The Essence of C++

When C++ was created, it had the goals of :

  • Increase type safety
  • Avoid resource leaks through resource safety
  • Performance-based
  • Readable by both humans and machines.
  • Teachable to both novices and experts.

It was created by combining the concepts borrowed from C and Simula.
The direct mapping to hardware was taken from C and the zero-overhead abstraction (classes, inheritance, generic programming) were taken from Simula.

Modern C++ places a large amount of value on resource management.
For this, it has shared_ptr which implements garbage collection to prevent the misuse of pointers. It also has the unique_ptr implementation for non-shared pointers.
To prevent the old style of returning large data from functions (using pointers), it has move semantics.
Rather than copying the data on return, it instead just returns the handle to the data. This is done through move constructors in classes.

Template Metaprogramming has also become a part of C++ due to its popularity. In C++ 11, constexpr was implemented to replace the misuse of template metaprogramming to generate constant values at compile time.
Furthermore, C++14 improved upon this and provides better interfaces to templates.
Thus, Modern C++ can handle generic programming with ease.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant