Skip to content

Conversation

@boruno
Copy link
Collaborator

@boruno boruno commented Dec 11, 2023

Two tasks done, 8 in WIP state, will be ready soon.

@boruno boruno requested a review from eupp December 27, 2023 10:46
Copy link
Collaborator

@eupp eupp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked approx. half of the prepared tasks. I will have a look at the remaining tasks later.
Overall good job, the text of the theory materials improved significantly compared to the module 3!

@boruno
Copy link
Collaborator Author

boruno commented Mar 4, 2024

A few thoughts:

  1. It seems to me that we need to introduce auto keyword somewhere in this course, because using iterators without it is painful, furthermore CLion and Clang-tidy warn that using pure iterator declarations is not a great practice. Right now I’ve inserted a brief explanation to «Iterator Introduction» task, but we can expand this topic in next module.
  2. I’ve tried to create a task for I/O lesson that will use file I/O for a long time, but we have a problem with .txt file visibility for compiler. I haven’t succeded adding properties to CMake, and two options I’ve came with are:
    1. Writing absolute path into solution, which breaks assumption that we have unified, executable on every machine master-solution.
    2. Changing run configurations, which is also not transferable to other machine. 
Are there any other I’ve missed? None of options above seem viable to me, so right now task lacks file I/O.
  3. I moved first iterator lesson to the beginning of the module, does the current order look good?

@boruno boruno requested a review from eupp March 4, 2024 12:59
@boruno
Copy link
Collaborator Author

boruno commented Apr 17, 2024

Last task of Templates module may be a bit controversial in terms of covered information, since I’ve included a brief explanation of type traits and variadic templates to it. I haven’t came up with anything else that could be presented in this lesson, other than basic template specialization idea. I don't mind deleting this part, but I thought it may be a nice sneak peek to metaprogramming and other high-level stuff.

@boruno boruno requested a review from eupp April 17, 2024 01:13
@@ -0,0 +1,9 @@
In this part of the course, you will learn about such a feature of the C++ language as templates.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In this part of the course, you will learn about such a feature of the C++ language as templates.
In this part of the course, you will learn about the templates in the C++ language.

In essence, templates are a means to write generic code that can work with varying types. They allow you to write a single function or class that can adapt to different types, without the need to write separate functions or classes for each type.

The following topics will be covered:
* Definition of templates for functions and classes
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Definition of templates for functions and classes
* Template functions and classes

* Definition of templates for functions and classes
* Type-inference, `auto` and `decltype` keywords, template argument deduction
* Type aliases: `using` and `typedef`
* Template specialization
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Type inference: auto and decltype
  • Template argument deduction
  • Type aliases: using and typedef
  • Template specialization

@@ -0,0 +1,75 @@
Imagine that you have a function for calculating the sum of two numbers. If you want to calculate the sum of two integers, you can write a function that takes two `int` numbers as arguments and returns their sum. However, suppose you also want to have such a function for `double`, `long long`, `float`, and a custom type such as `Fraction`. You will have to write a separate function for each type. This becomes inconvenient, as you end up writing multiple functions that essentially do the same thing but with different data types.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Imagine that you have a function for calculating the sum of two numbers. If you want to calculate the sum of two integers, you can write a function that takes two `int` numbers as arguments and returns their sum. However, suppose you also want to have such a function for `double`, `long long`, `float`, and a custom type such as `Fraction`. You will have to write a separate function for each type. This becomes inconvenient, as you end up writing multiple functions that essentially do the same thing but with different data types.
Imagine that you have a function for calculating the sum of two numbers. If you want to calculate the sum of two integers, you can write a function that takes two `int` numbers as arguments and returns their sum. However, suppose you also want to have such a function for `double`, `long long`, `float`, or some custom data type. You will have to write a separate function for each type. This becomes inconvenient, as you end up writing multiple functions that essentially do the same thing but with different data types.


Templates are designed to address this exact issue. They allow you to write a single function that can work with different types. For instance, you can write a template function that takes two arguments of any type and returns their sum as a variable of the same type.

The syntax for creating a template function is simple: you start with the `template` keyword, followed by the `typename` keyword, and then include the type name you want to use as a template parameter. Then, you can use this type name in the function as if it were a regular type:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The syntax for creating a template function is simple: you start with the `template` keyword, followed by the `typename` keyword, and then include the type name you want to use as a template parameter. Then, you can use this type name in the function as if it were a regular type:
The syntax for creating a template function is simple: you start with the `template` keyword, followed by the `typename` keyword, and then include the type name you want to use as a template parameter. Then, you can use this type name in the function as if it was a regular type:

When the compiler encounters any call to a template function, it first checks for a specialized version of the function for the argument's type. If such a version exists, it will employ it; otherwise, it will use the general version.
There is a technique called metaprogramming, which allows you to write code that generates other code using templates and template specialization. Since it is a complex topic, we will not cover it in this course, but you can find more information about it in the [C++ documentation](https://en.cppreference.com/w/cpp/meta). However, there is a feature called "type traits" that can be helpful in some cases. Type traits are a set of classes that provide information about types at compile time. You can use them to check if a type has a specific property, like being a pointer or reference. You can find more information about type traits in the [C++ documentation](https://en.cppreference.com/w/cpp/header/type_traits).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not mention "type traits". It is a complicated feature, which can only confuse novice programmers. This feature deserves its own series of tasks and should be properly explained.

There is a technique called metaprogramming, which allows you to write code that generates other code using templates and template specialization. Since it is a complex topic, we will not cover it in this course, but you can find more information about it in the [C++ documentation](https://en.cppreference.com/w/cpp/meta). However, there is a feature called "type traits" that can be helpful in some cases. Type traits are a set of classes that provide information about types at compile time. You can use them to check if a type has a specific property, like being a pointer or reference. You can find more information about type traits in the [C++ documentation](https://en.cppreference.com/w/cpp/header/type_traits).
Since each specialization requires a separate implementation, it may not always be the best solution. In many cases, you can try to find any common type property and use it's checker, like the `std::is_pointer` function to check if the type is a pointer and handle it within the general function. This way, you can avoid code duplication and make the code more readable.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this paragraph and example too.

* `erase(value)` - removes the element `value` from the container
* `find(value)` - returns an iterator to the element `value` if it is found, otherwise, it returns `end()`

To be fair, all these methods have **amortized** `O(1)` complexity, since sometimes hashes of different keys can be the same. This is called a [collision](https://en.wikipedia.org/wiki/Hash_table#Collision_resolution). In such situations, the whole table needs to be rehashed, which is the reason why the methods of an unordered container can perform in `O(n)` time in the worst-case scenario.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this paragraph after the next two paragraphs (about hash table and std::hash), and put it under a hint.

}
```

You need to implement a [cache](https://en.wikipedia.org/wiki/Cache_(computing)) system with an [LRU](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)) (Least Recently Used) policy. Its member functions are defined in the `/include/Cache.h` file. Since cache is a very fast type of storage, you will need to use hashing to implement it. The provided solution uses `std::list` and `std::unordered_map`, but you can choose any container from the STL for your own implementation. At first, define a structure inside `/include/Cache.h` file that will store key-value pairs. Then, define another structure that will store the key and an iterator to the corresponding element in the list. After defining these structures, you can implement the cache operations on `task.cpp`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided solution uses std::list and std::unordered_map, but you can choose any container from the STL for your own implementation.

Maybe let's remove this suggestion? I think it can only confuse the students.
Using just std::list and std::unordered_map is fine for this task.

In addition to all that we have learned so far, the STL also provides a number of algorithms that can be used on a range of elements. These algorithms are implemented as functions that usually utilize iterators. In this task, we will look at some of the most commonly used algorithms. There are many more algorithms available in the STL, and you can find a complete list [here](https://en.cppreference.com/w/cpp/algorithm).
We highly encourage you to explore the STL algorithms by yourself and try to use them in your code! They are useful and can save you a lot of time and effort.

#### 1. `std::sort()`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is still relevant (there is still no mentioned of iterators categories expected by the algorithms).


# Running learner side code
# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly.
add_executable(lesson-run ${SRC})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a problem with this.
First, "lesson-run" name is not unique (it is easy to give the same name to another task by mistake).
Second, as the comment above mentions, for plugin integration to work properly, the name has to be dependent on the $PROJECT_NAME variable.

So I suggest to revert it back to ${PROJECT_NAME}-run and instead change the name of the project. So replace:

project(StandardLibraryEssentials-StandardTemplateLibrary-SequentialContainerAdaptors)

to just

project(StandardLibraryEssentials-SequentialContainerAdaptors)

or even:

project(SequentialContainerAdaptors)

Same applies to lesson-test target.

@eupp eupp changed the base branch from main to develop June 17, 2024 08:55
@eupp eupp merged commit 18f1300 into develop Jun 17, 2024
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

Successfully merging this pull request may close these issues.

4 participants