C++: completed Log Levels

This commit is contained in:
Andrew Scott 2024-07-08 18:12:14 -04:00
parent 88b3c48a36
commit 1b76fd05a3
Signed by: a
GPG key ID: 7CD5A5977E4931C1
10 changed files with 18355 additions and 0 deletions

View file

@ -0,0 +1,21 @@
{
"authors": [
"silvanocerza",
"vaeng"
],
"files": {
"solution": [
"log_levels.cpp"
],
"test": [
"log_levels_test.cpp"
],
"exemplar": [
".meta/exemplar.cpp"
]
},
"forked_from": [
"csharp/log-levels"
],
"blurb": "Learn about strings by parsing logs."
}

View file

@ -0,0 +1 @@
{"track":"cpp","exercise":"log-levels","id":"0aa0c4f7fa1c4913b3c208898635ec4f","url":"https://exercism.org/tracks/cpp/exercises/log-levels","handle":"Chomp1295","is_requester":true,"auto_approve":false}

View file

@ -0,0 +1,66 @@
# Basic CMake project
cmake_minimum_required(VERSION 3.5.1)
# Get the exercise name from the current directory
get_filename_component(exercise ${CMAKE_CURRENT_SOURCE_DIR} NAME)
# Name the project after the exercise
project(${exercise} CXX)
# Get a source filename from the exercise name by replacing -'s with _'s
string(REPLACE "-" "_" file ${exercise})
# Implementation could be only a header
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.cpp)
set(exercise_cpp ${file}.cpp)
else()
set(exercise_cpp "")
endif()
# Use the common Catch library?
if(EXERCISM_COMMON_CATCH)
# For Exercism track development only
add_executable(${exercise} ${file}_test.cpp $<TARGET_OBJECTS:catchlib>)
elseif(EXERCISM_TEST_SUITE)
# The Exercism test suite is being run, the Docker image already
# includes a pre-built version of Catch.
find_package(Catch2 REQUIRED)
add_executable(${exercise} ${file}_test.cpp)
target_link_libraries(${exercise} PRIVATE Catch2::Catch2WithMain)
# When Catch is installed system wide we need to include a different
# header, we need this define to use the correct one.
target_compile_definitions(${exercise} PRIVATE EXERCISM_TEST_SUITE)
else()
# Build executable from sources and headers
add_executable(${exercise} ${file}_test.cpp test/tests-main.cpp)
endif()
set_target_properties(${exercise} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED OFF
CXX_EXTENSIONS OFF
)
set(CMAKE_BUILD_TYPE Debug)
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(GNU|Clang)")
set_target_properties(${exercise} PROPERTIES
# added "-Wno-unused-parameter" to remove compiler warnings
# should make it easier for students to run their first real code
COMPILE_FLAGS "-Wall -Wextra -Wpedantic -Werror"
)
endif()
# Configure to run all the tests?
if(${EXERCISM_RUN_ALL_TESTS})
target_compile_definitions(${exercise} PRIVATE EXERCISM_RUN_ALL_TESTS)
endif()
# Tell MSVC not to warn us about unchecked iterators in debug builds
if(${MSVC})
set_target_properties(${exercise} PROPERTIES
COMPILE_DEFINITIONS_DEBUG _SCL_SECURE_NO_WARNINGS)
endif()
# Run the tests on every build
add_custom_target(test_${exercise} ALL DEPENDS ${exercise} COMMAND ${exercise})

61
cpp/log-levels/HELP.md Normal file
View file

@ -0,0 +1,61 @@
# Help
## Running the tests
Running the tests involves running `cmake -G` and then using the build command appropriate for your platform.
Detailed instructions on how to do this can be found on the [Running the Tests][cpp-tests-instructions] page for C++ on exercism.org.
## Passing the Tests
When you start a new exercise locally, the files are configured so that only the first test is performed.
Get that first test compiling, linking and passing by following the [three rules of test-driven development][three-laws-of-tdd].
Create just enough structure by declaring namespaces, functions, classes, etc., to satisfy any compiler errors and get the test to fail.
Then write just enough code to get the test to pass.
Once you've done that, uncomment the next test by moving the line `if defined(EXERCISM_RUN_ALL_TESTS)` past the next test.
See the example below from the Bob exercise (file `bob_test.cpp`, line 15):
```diff
-#if defined(EXERCISM_RUN_ALL_TESTS)
TEST_CASE("shouting")
{
REQUIRE("Whoa, chill out!" == bob::hey("WATCH OUT!"));
}
+#if defined(EXERCISM_RUN_ALL_TESTS)
```
Moving this line past the next test may result in compile errors as new constructs may be invoked that you haven't yet declared or defined.
Again, fix the compile errors minimally to get a failing test, then change the code minimally to pass the test, refactor your implementation for readability and expressiveness and then go on to the next test.
Try to use standard C++17 facilities in preference to writing your own low-level algorithms or facilities by hand.
[cpp-tests-instructions]: https://exercism.org/docs/tracks/cpp/tests
[three-laws-of-tdd]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
## Submitting your solution
You can submit your solution using the `exercism submit log_levels.cpp` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [C++ track's documentation](https://exercism.org/docs/tracks/cpp)
- The [C++ track's programming category on the forum](https://forum.exercism.org/c/programming/cpp)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
To get help if you're having trouble, you can use one of the following resources:
- [`c++-faq` tag on StackOverflow](https://stackoverflow.com/tags/c%2b%2b-faq/info)
- [C++ FAQ from isocpp.com](https://isocpp.org/faq)
- [CppReference](http://en.cppreference.com/) is a wiki reference to the C++ language and standard library
- [C traps and pitfalls](http://www.slideshare.net/LegalizeAdulthood/c-traps-and-pitfalls-for-c-programmers) is useful if you are new to C++, but have programmed in C

22
cpp/log-levels/HINTS.md Normal file
View file

@ -0,0 +1,22 @@
# Hints
## General
- [This tutorial][strings-tutorial] offers a nice introduction to C++ strings.
- The `std::basic_string` class has many useful [built-in methods][cpp-reference-string].
## 1. Get the message from a log line
- The built-in methods offer some ways to [find][cpp-reference-string-find] text in a string.
- The built-in methods can help us [get a portion][cpp-reference-string-substr] of a string
## 2. Get the log level from a log line
There are several ways to concatenate strings, the simplest is using the [`+` operator][cpp-reference-string-concatenation] but there are [more advanced ways][cpp-reference-printf] as well.
[strings-tutorial]: https://www.learncpp.com/cpp-tutorial/4-4b-an-introduction-to-stdstring/
[cpp-reference-string]: https://en.cppreference.com/w/cpp/string/basic_string
[cpp-reference-string-find]: https://en.cppreference.com/w/cpp/string/basic_string/find
[cpp-reference-string-substr]: https://en.cppreference.com/w/cpp/string/basic_string/substr
[cpp-reference-string-concatenation]: https://en.cppreference.com/w/cpp/string/basic_string/operator%2B
[cpp-reference-printf]: https://en.cppreference.com/w/cpp/io/c/fprintf

146
cpp/log-levels/README.md Normal file
View file

@ -0,0 +1,146 @@
# Log Levels
Welcome to Log Levels on Exercism's C++ Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
### Includes
In general, you don't want to reinvent the wheel when it comes to programming.
For many standard algorithms, there will be someone who might have already done your work for you.
### Include directive
To include the content of other files C++ uses the `include` directive.
These includes should be stated at the top of the file.
During compilation, the line with the directive is replaced by the content of the respective file.
Namespaces are kept as they are in the original file after includes.
### The Standard Library
The standard library offers many common functions, algorithms, and data structures.
The standard library uses the `std` namespace.
One example is the C numerics library `cmath`.
It provides many common mathematical operations.
```cpp
#include <cmath>
int cube_me(int a) {
// raise `a` to the third power
return std::pow(a, 3);
}
```
~~~~exercism/note
Standard libraries are included with angled braces `<>` instead of double quotes `"`.
The difference is the location, where the compiler searches for the respective files.
The search in the current project is skipped for the angled braces version, and it directly starts in the system's include directories.
If you want to include local files you would use double quotes: `#include "myfile"`
~~~~
## Strings
A `string` in C++ is a mutable object that represents text as a sequence of characters (letters, digits, punctuation, etc.).
Strings are manipulated by calling the string's methods.
### Strings Library
In C++ the string type and the associated functions have to be included from the strings library before usage.
You can do so by adding `#include <string>` to the top of your file.
They will then populate the `std` namespace.
The fully qualified name of the string type is `std::string`.
The string literal uses the double quote character: `"`.
```cpp
#include <string>
std::string w_berry_quote{"A well-made sentence, I think, is a thing of beauty."};
```
### Common String Operations
You can use the `+` operator to concatenate strings:
```cpp
std::string original_title{"The School of Rock"};
std::string sequel_indicator{"Electric Boogaloo"};
std::string next_movie_title = original_title + " 2: " + sequel_indicator;
```
To use the strings library, you need to know that it is possible to call a function that belongs to an object.
These are called member functions.
Later in the syllabus, you will learn more about member functions and the connected class concept.
```cpp
std::string qualification{"awesome"};
// 1st argument: from the index to the end of the string:
std::string who_is_awesome = qualification.substr(5);
// => "me"
// 2nd optional argument for the length:
std::string material{"haunted books"};
std::string ghost = material.substr(8, 3);
// => "boo"
```
The `find` function is also very useful.
It is called as a member function on the string and takes a string as the argument.
`find` returns the zero-indexed position of the _first_ occurrence in the string.
```cpp
std::string new_release{"apple released a new app!"};
new_release.find("app");
// => 0
new_release.find("e");
// => 4
```
## Instructions
In this exercise, you'll be processing lines from a logged report.
Each log line is a string formatted as follows: `"[<LEVEL>]: <MESSAGE>"`.
There are three different log levels:
- `INFO`
- `WARNING`
- `ERROR`
You have three tasks, each of which will take a log line and ask you to do something with it.
## 1. Get the message from a log line
Implement the `log_line::message` method to return a log line's message:
```cpp
log_line::message("[ERROR]: Invalid operation")
// => "Invalid operation"
```
## 2. Get the log level from a log line
Implement the `log_line::log_level` method to return a log line's log level, which should be returned in uppercase:
```cpp
log_line::log_level("[ERROR]: Invalid operation")
// => "ERROR"
```
## 3. Reformat a log line
Implement the `log_line::reformat` method that reformats the log line, putting the message first and the log level after it in parentheses:
```cpp
log_line::reformat("[INFO]: Operation completed")
// => "Operation completed (INFO)"
```
## Source
### Created by
- @silvanocerza
- @vaeng

View file

@ -0,0 +1,16 @@
#include <string>
namespace log_line {
std::string message(std::string line) {
return line.substr(line.find(" ") + 1);
}
std::string log_level(std::string line) {
return line.substr(1, line.find("]") - 1);
}
std::string reformat(std::string line) {
return message(line) + " (" + log_level(line) + ")";
}
} // namespace log_line

View file

@ -0,0 +1,83 @@
#include "log_levels.cpp"
#ifdef EXERCISM_TEST_SUITE
#include <catch2/catch.hpp>
#else
#include "test/catch.hpp"
#endif
using namespace std;
TEST_CASE("Error message", "[task_1]") {
const string actual = log_line::message("[ERROR]: Stack overflow");
const string expected{"Stack overflow"};
REQUIRE(expected == actual);
}
TEST_CASE("Warning message", "[task_1]") {
const string actual = log_line::message("[WARNING]: Disk almost full");
const string expected{"Disk almost full"};
REQUIRE(actual == expected);
}
TEST_CASE("Info message", "[task_1]") {
const string actual = log_line::message("[INFO]: File moved");
const string expected{"File moved"};
REQUIRE(actual == expected);
}
TEST_CASE("Error log level", "[task_2]") {
const string actual = log_line::log_level("[ERROR]: Disk full");
const string expected{"ERROR"};
REQUIRE(actual == expected);
}
TEST_CASE("Warning log level", "[task_2]") {
const string actual = log_line::log_level("[WARNING]: Unsafe password");
const string expected{"WARNING"};
REQUIRE(actual == expected);
}
TEST_CASE("Info log level", "[task_2]") {
const string actual = log_line::log_level("[INFO]: Timezone changed");
const string expected{"INFO"};
REQUIRE(actual == expected);
}
TEST_CASE("Error reformat", "[task_3]") {
const string actual = log_line::reformat("[ERROR]: Segmentation fault");
const string expected{"Segmentation fault (ERROR)"};
REQUIRE(actual == expected);
}
TEST_CASE("Warning reformat", "[task_3]") {
const string actual = log_line::reformat("[WARNING]: Decreased performance");
const string expected{"Decreased performance (WARNING)"};
REQUIRE(actual == expected);
}
TEST_CASE("Info reformat", "[task_3]") {
const string actual = log_line::reformat("[INFO]: Disk defragmented");
const string expected{"Disk defragmented (INFO)"};
REQUIRE(actual == expected);
}
#if defined(EXERCISM_RUN_ALL_TESTS)
#endif

17937
cpp/log-levels/test/catch.hpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include "catch.hpp"