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

feat: add concept exercise for headers #741

Merged
merged 3 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,8 @@
"cinttypes": "cpp",
"typeinfo": "cpp",
"variant": "cpp"
}
},
"cSpell.words": [
"stationprogress"
]
}
30 changes: 15 additions & 15 deletions concepts/headers/about.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# About

In C++ declarations are often separated from definitions.
Declarations are grouped into so-called header files and the respective implementation is placed in source files.
In C++, declarations are often separated from definitions.
Declarations are grouped into so-called header files, with the respective implementations placed in source files.
You can think of the header files as an API.
The header file will tell you what a codebase has to offer without going into the details of how.
The header file will tell you _what_ a codebase has to offer without going into the details of _how_.

## Header and Source

The most common file extension for header files is `.h`.
Some projects use `.hpp` or skip the extension completely.

The definitions are located in a separate `.cpp` file.
To reunite both parts, this source file starts by including the respective header file.
To reunite the parts, the source file starts by _including_ the respective header file.

If you want to write a library called "quick_math", that offers a function "super_root" that returns your favorite number, the files would look like this:
If you want to write a library called "quick_math" that offers a function "super_root" that you want to use often, the files would look like this:

```cpp
// A file named quick_math.h
Expand All @@ -33,19 +33,19 @@ double quick_math::super_root(double x, int n) {
}
```

If you need to include another header, that is only needed by the implementation, the respective `#include` line is only needed in the source file.
Everything that is included in the header, is also available in the `.cpp` file, like the `string` library in the example below.
Attention: the `;` is needed after the declaration in the header file, but not after the definition in the source file.
If you need to include a header that is only required by the implementation, the respective `#include` line is only needed in the source file.
Everything that is included in the header is also available in the `.cpp` file, like the `string` library in the example below.
**Attention**: the `;` is needed after the declaration in the header file, but not after the definition in the source file.

~~~~exercism/note
Many C++ exercises on Exercism start with two almost empty files: header and source.
You have to check the `*_test.cpp` file to see the names and namespaces of the expected functions to solve the exercise.
You have to check the `*_test.cpp` file to see the names and namespaces of the expected functions in order to solve the exercise.
~~~~

## Classes and Headers

Classes can become very complex and their relation to the header / source partition might be confusing.
One possible layout is to keep all the implementation details in the source file and the declarations and member variables in the header:
One possible layout is to keep all the implementation details in the source file and all the declarations and member variables in the header:

```cpp
// A file named robot_flower.h
Expand Down Expand Up @@ -82,7 +82,7 @@ When the header is used as an API overview, that is where a person would look fo
The `size` parameter's default of the constructor is therefore handled in the header and not in the implementation.
The definitions in the source file are prefixed with the namespace `robots` and the class type `Flower`.

Another option is a _header only_ library, that does not have a `.cpp` file at all:
Another layout option is a _header only_ library, that does not have a `.cpp` file at all:

```cpp
// A file named robot_flower.h
Expand All @@ -104,13 +104,13 @@ namespace robots {
}
```

Projects might use combinations of those layouts and there is a lot of discussion what might be the best fit for each use case.
Projects might use combinations of these layouts and there is a lot of discussion as to what might be the best fit for each use case.

## Include Guards via pragma once
## Include Guards

You may have noticed the `#pragma once` line in the example header file above.
This include guard ensures that the content of the file is included only once during the compilation to avoid errors.
There is another, more complex variation that starts with `#ifndef` which serves the same purpose, that is detailed below.
This is called an include guard - and it ensures that the content of the file is included only once during the compilation to avoid errors.
There is another, more complex variation of an include guard that starts with `#ifndef` and ends with `#endif`, that is detailed below.


~~~~exercism/advanced
Expand Down
28 changes: 14 additions & 14 deletions concepts/headers/introduction.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# Introduction

In C++ declarations are often separated from definitions.
Declarations are grouped into so-called header files and the respective implementation is placed in source files.
In C++, declarations are often separated from definitions.
Declarations are grouped into so-called header files, with the respective implementations placed in source files.
You can think of the header files as an API.
The header file will tell you what a codebase has to offer without going into the details of how.
The header file will tell you _what_ a codebase has to offer without going into the details of _how_.

## Header and Source

The most common file extension for header files is `.h`.
Some projects use `.hpp` or skip the extension completely.

The definitions are located in a separate `.cpp` file.
To reunite both parts, this source file starts by including the respective header file.
To reunite the parts, the source file starts by _including_ the respective header file.

If you want to write a library called "quick_math", that offers a function "super_root" that you want to use often, the files would look like this:
If you want to write a library called "quick_math" that offers a function "super_root" that you want to use often, the files would look like this:

```cpp
// A file named quick_math.h
Expand All @@ -33,19 +33,19 @@ double quick_math::super_root(double x, int n) {
}
```

If you need to include another header, that is only needed by the implementation, the respective `#include` line is only needed in the source file.
Everything that is included in the header, is also available in the `.cpp` file, like the `string` library in the example below.
Attention: the `;` is needed after the declaration in the header file, but not after the definition in the source file.
If you need to include a header that is only required by the implementation, the respective `#include` line is only needed in the source file.
Everything that is included in the header is also available in the `.cpp` file, like the `string` library in the example below.
**Attention**: the `;` is needed after the declaration in the header file, but not after the definition in the source file.

~~~~exercism/note
Many C++ exercises on Exercism start with two almost empty files: header and source.
You have to check the `*_test.cpp` file to see the names and namespaces of the expected functions to solve the exercise.
You have to check the `*_test.cpp` file to see the names and namespaces of the expected functions in order to solve the exercise.
~~~~

## Classes and Headers

Classes can become very complex and their relation to the header / source partition might be confusing.
One possible layout is to keep all the implementation details in the source file and the declarations and member variables in the header:
One possible layout is to keep all the implementation details in the source file and all the declarations and member variables in the header:

```cpp
// A file named robot_flower.h
Expand Down Expand Up @@ -82,7 +82,7 @@ When the header is used as an API overview, that is where a person would look fo
The `size` parameter's default of the constructor is therefore handled in the header and not in the implementation.
The definitions in the source file are prefixed with the namespace `robots` and the class type `Flower`.

Another option is a _header only_ library, that does not have a `.cpp` file at all:
Another layout option is a _header only_ library, that does not have a `.cpp` file at all:

```cpp
// A file named robot_flower.h
Expand All @@ -104,11 +104,11 @@ namespace robots {
}
```

Projects might use combinations of those layouts and there is a lot of discussion what might be the best fit for each use case.
Projects might use combinations of these layouts and there is a lot of discussion as to what might be the best fit for each use case.

## Include Guards

You may have noticed the `#pragma once` line in the example header file above.
This include guard ensures that the content of the file is included only once during the compilation to avoid errors.
There is another, more complex variation that starts with `#ifndef` and ends with `#endif`.
This is called an include guard - and it ensures that the content of the file is included only once during the compilation to avoid errors.
There is another, more complex variation of an include guard that starts with `#ifndef` and ends with `#endif`.
It serves the same purpose and its usage is shown in the `Flower` class example above.
12 changes: 12 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,18 @@
"classes",
"comparisons"
]
},
{
"slug": "doctor-data",
"name": "Doctor Data",
"uuid": "2d2efdad-4f5d-4a1d-9626-381b37e72e3f",
"concepts": [
"headers"
],
"prerequisites": [
"classes",
"enums"
]
}
],
"practice": [
Expand Down
24 changes: 24 additions & 0 deletions exercises/concept/doctor-data/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Hints

## General

- Pay attention to any enumerators you come across.
- The tests can only be run when you have implemented every task.
To see the test results you only need a rough skeleton of the functions and classes.
- Every function, class, and enumeration that is used by the tests must be declared in the header file.

## 1. Try to extract some first clues

- The class and the enum are in different namespaces.
- Use a `class enum` for `System`
- You can either have two constructors, or one constructor with a default argument.

## 2. Find more details

- The first `REQUIRE` line checks for the default `System`.
- `generation` seems to increase by one in a cloned vessel.
- The `System` seems to be untouched by the cloning process.

## 3. Look into the inner workings

- The `shoot_buster` member function seems to be depenedent on an internal counter.
107 changes: 107 additions & 0 deletions exercises/concept/doctor-data/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Instructions

You start your first day at an Australian company called "Doctor Data", which specializes in information recovery.
You aced your job interview through your knowledge of C++ and [von Neumann probes][van-neumann-probes].
As you have seen a lot of test files, your new boss wants you to recreate the respective source and header files from some test code the company has recently recovered.

In this exercise, you are going to recreate lost files.
vaeng marked this conversation as resolved.
Show resolved Hide resolved

~~~~exercism/note
The workflow of this concept exercise is very similar to the structure of Exercism's practice exercises.
The exercise introduction text is only one part of the specification.
The test file is your definitive guide to solving a given problem.
Due to the way C++ compilation works, the test results might not show up in the web interface until you have implemented a minimal version of every class, function and enumeration that is required by the tests.
If you receive compilation errors, they might disappear once you have addressed all tasks and tests.
~~~~

You have four tasks, all related to recovering the lost code in the header and source files.

## 1. Try to extract some first clues

You look at two recovered lines from the test files:

```cpp
heaven::Vessel bob{"Robert Johansson", 1};
heaven::Vessel will{"Riker", 2, star_map::System::BetaHydri};
```

Your sharp eye instantly recognized a namespace `heaven`.
You also see that there must be a class called `Vessel`.
The constructor can apparently be called with two or three arguments.
The first argument seems to be of type `string`, the second one is a number.
It is possible to initialize the `Vessel` class with a third argument.
The third argument comes from a `star_map` namespace.
It is an enumerator of type `System`.
You even got one of the enumerations: `BetaHydri`.

Prepare the source and header files with your discovered information.
You need two namespaces: `heaven` and `star_map`.
The `heaven` namespace has a class `Vessel`, which can be called with two or three arguments.
The `System` enum is to be placed in the `star_map` namespace and has an `EpsilonEridani` enumeration.
Keep an eye out for other enumerations, so that you can constantly update the `System` enum.

## 2. Find more details

You uncover more lines from the test:

```cpp
heaven::Vessel bob{"Robert Johansson", 1};
REQUIRE(bob.current_system == star_map::System::Sol);
REQUIRE(bob.generation == 1);
heaven::Vessel bob5 = bob.replicate("Mario");
REQUIRE(bob5.current_system == star_map::System::Sol);
REQUIRE(bob5.generation == 2);
```

The newly found test lines uncover another member function of the `Vessel` class: `replicate`.
This function receives a string and returns another `Vessel` instance.
You get an idea about the default value of the third argument of the constructor from the previous task.
You also see two public member variables of the `Vessel` class and the specification of the `replicate` member function.

Add the `replicate` function and the public member variables `current_system` and `generation` to the header and source files.

## 3. Look into the inner workings

You find some more interesting lines in the recovered test files:

```cpp
heaven::Vessel bob6{"Homer", 3, star_map::System::EpsilonEridani};
REQUIRE(bob6.busters == 0);
bob6.make_buster();
REQUIRE(bob6.busters == 1);
bool success = bob6.shoot_buster();
REQUIRE(success);
REQUIRE(bob6.busters == 0);
success = bob6.shoot_buster();
REQUIRE_FALSE(success);
```

Apparently, the `Vessel` class has a member variable `busters`, that can be changed with the two class member functions `make_buster` and `shoot_buster`.
Until other information surfaces, you take a guess that the `make_buster` function returns `void`.
As there is a test for the return value of `shoot_buster`, you assume that the function returns a `bool`.

Add the two functions and the member variable to the `Vessel` class.
Keep looking for other `System` enumerators.

## 4. Complete the picture

During your scan of the test files you find only two uncovered sections of the code:

```cpp
heaven::Vessel bob1{"Bob", 1, star_map::System::AlphaCentauri};
heaven::Vessel marv{"Marvin", 2, star_map::System::DeltaEridani};
heaven::Vessel milo{"Milo", 3, star_map::System::DeltaEridani};
heaven::Vessel howie{"Howard", 4, star_map::System::Omicron2Eridani};

REQUIRE("Bob" == heaven::get_older_bob(bob1, marv));
REQUIRE(heaven::in_the_same_system(marv, milo));
REQUIRE_FALSE(heaven::in_the_same_system(marv, howie));
```

You see two functions, that are not members of the `Vessel` class, as they are not called with an instance.
`get_older_bob` compares two `Vessel`instances and returns a `string`.
`in_the_same_system` compares two `Vessel`instances and returns a `bool`.

Implement the last missing functions from the recovered lines above.

[van-neumann-probes]: https://en.wikipedia.org/wiki/Self-replicating_spacecraft
Loading