mirror of
https://codeberg.org/andyscott/exercism.git
synced 2024-11-09 13:20:48 -05:00
C++: completed Log Levels
This commit is contained in:
parent
88b3c48a36
commit
1b76fd05a3
10 changed files with 18355 additions and 0 deletions
21
cpp/log-levels/.exercism/config.json
Normal file
21
cpp/log-levels/.exercism/config.json
Normal 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."
|
||||||
|
}
|
1
cpp/log-levels/.exercism/metadata.json
Normal file
1
cpp/log-levels/.exercism/metadata.json
Normal 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}
|
66
cpp/log-levels/CMakeLists.txt
Normal file
66
cpp/log-levels/CMakeLists.txt
Normal 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
61
cpp/log-levels/HELP.md
Normal 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
22
cpp/log-levels/HINTS.md
Normal 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
146
cpp/log-levels/README.md
Normal 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
|
16
cpp/log-levels/log_levels.cpp
Normal file
16
cpp/log-levels/log_levels.cpp
Normal 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
|
83
cpp/log-levels/log_levels_test.cpp
Normal file
83
cpp/log-levels/log_levels_test.cpp
Normal 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
17937
cpp/log-levels/test/catch.hpp
Normal file
File diff suppressed because it is too large
Load diff
2
cpp/log-levels/test/tests-main.cpp
Normal file
2
cpp/log-levels/test/tests-main.cpp
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include "catch.hpp"
|
Loading…
Reference in a new issue