From 9dfe66460532621403310c0c1407a687991aeb42 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Tue, 27 Apr 2021 15:16:48 +0200 Subject: [PATCH 01/35] Initial version, using C tutorial as template --- content/languages/cpp/authoring/index.md | 292 +++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 content/languages/cpp/authoring/index.md diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md new file mode 100644 index 00000000..da87c57d --- /dev/null +++ b/content/languages/cpp/authoring/index.md @@ -0,0 +1,292 @@ +--- +title: Authoring C++ Content +kind: tutorial +sidebar_position: 0 +slug: /languages/cpp/authoring +--- + +This article is meant for kata authors and translators who would like to create new content in C++. It attempts to explain how to create and organize things in a way conforming to [authoring guidelines](/authoring/guidelines/), shows the most common pitfalls and how to avoid them. + +This article is not a standalone tutorial on creating kata or translations. It's meant to be a complementary, C++-specific part of a more general set of HOWTOs and guidelines related to [content authoring](/authoring/). If you are going to create a C++ translation, or a new C++ kata from scratch, please make yourself familiar with the aforementioned documents related to authoring in general first. + +## General info + +Any technical information related to the C++ setup on Codewars can be found on the [C++ reference](/languages/cpp/) page (language versions, available libraries, and setup of the code runner). + + +## Description + +C++ code blocks can be inserted with C++-specific part in [sequential code blocks](/references/markdown/extensions/#sequential-code-blocks): + +~~~ +```cpp + +...your code here... + +``` +~~~ + +C++-specific paragraphs can be inserted with [language conditional rendering](/references/markdown/extensions/#conditional-rendering): + +``` +~~~if:cpp + +...text visible only for C++ description... + +~~~ + +~~~if-not:cpp + +...text not visible in C++ description... + +~~~ +``` + + \ No newline at end of file From a4eb3855d825f4893d3dc6dc3e0d0eabb75dfefa Mon Sep 17 00:00:00 2001 From: hobovsky Date: Tue, 27 Apr 2021 15:19:29 +0200 Subject: [PATCH 02/35] Add TODO --- content/languages/cpp/authoring/index.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index da87c57d..07a90c5c 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -42,6 +42,18 @@ C++-specific paragraphs can be inserted with [language conditional rendering](/r ~~~ ``` + +_TODO:_ + +- includes (missing includes, C includes) +- random utilities +- stringizers +- custom assertion messages (modifications to the testing framework: https://github.com/codewars/snowhouse/pull/3 ) +- compilation warnings +- input and output values: const ref vs value, replacements for arrays and `std::vector` +- avoid C: strings, arrays +- input modification, changing the signature of solution function + \ No newline at end of file +--> From a60756bef10cf5db295058bad17b8f6abf18d0c5 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Wed, 28 Apr 2021 19:02:09 +0200 Subject: [PATCH 10/35] Some wording --- content/languages/cpp/authoring/index.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 052dbea2..c11570c8 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -64,10 +64,10 @@ One consequence of using the template is that signature of solution function can C++ programmers have many sets of naming conventions or code style guides. Some of them can be found for example [here](https://isocpp.org/wiki/faq/coding-standards), or [here](https://google.github.io/styleguide/cppguide.html). Codewars does not strictly enforce any of them, just use whatever set of guidelines you like, but when you do, use it consistently. There's a few C++ coding guidelines which are violated by kata authors and translatiors particularly often: -- Non-trivial arguments should be passed by const reference, and not by value. -- Appropriate containers should be used: +- Arguments which are not cheap to copy should be passed by const reference, and not by value. +- Appropriate containers should be used, _especially_ when used as input arguments or return values: - C-style raw arrays and pointers to arrays need to be avoided - - `std::array`, `std::pair`, or `std::tuple` should be used for data of known size + - `std::array`, `std::pair`, or `std::tuple` should be used for data of known size. Avoid representing 2D points or pairs with `std::vector`. - `std::vector` should be used for arrays of varying size - `std::string` should be used instead of C-strings @@ -88,12 +88,12 @@ Compiler options related to warnings used by the C++ runner are somewhat strict Sometimes authors consider C++ just "a C, but with classes". While C and C++ are still compatible in many ways, such perception is wrong and incorrect use of C features in C++ code leads to bad code at best, to undefined behavior and difficult to diagnose errors in more extreme cases. Features and idioms from C language should be replaced with their equivalents from modern C++: -- C features must not be used where they do not have well-defined behavior in C++. For example, memory management must not be done with `malloc`/`free` or similar, or VLAs should be replaced with `std::array` or `std::vector`. +- C features must not be used where they do not have well-defined behavior in C++. For example, memory management generally should not be done with `malloc`/`free` or similar, or VLAs should be replaced with `std::array` or `std::vector`. - C++ features should be preferred to ones inherited from C, for example: - C-style casts should be replaced with their C++ equivalents. - Functions originating from C should be replaced with related C++ functionalities: `` instead of `rand`, `` instead of `stdio.h`, etc. - C++ header files should not be confused with their C equivalents. For example, `cmath` or `cctype` should be used instead of `math.h` or `ctype.h`. -- Proper C++ data types should be used instead of their C "equivalents". C-strings should be replaced with `std::string`, raw C-style arrays should be replaced with `std::array`, `std::vector`, or other containers, etc. +- Proper C++ data types should be used instead of their C "equivalents", especially when used as elements of solution interface (input parameters and return value). C-strings should be replaced with `std::string` (or `std::string_view`), raw C-style arrays should be replaced with `std::array`, `std::vector`, or other containers, C++ pointer wrappers should be considered instead of raw pointers, etc. ## Tests From e4e104b0ac661c0136b6bf1a87d6fab09d0a1221 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Wed, 28 Apr 2021 23:25:45 +0200 Subject: [PATCH 11/35] Apply suggestions from code review Co-authored-by: awesome A.D. <59703337+awesomeAD1@users.noreply.github.com> --- content/languages/cpp/authoring/index.md | 46 ++++++++++++------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index c11570c8..64ee8c14 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -16,7 +16,7 @@ Any technical information related to the C++ setup on Codewars can be found on t ## Description -C++ code blocks can be inserted with C++-specific part in [sequential code blocks](/references/markdown/extensions/#sequential-code-blocks): +C++-specific code can be inserted with the `cpp` flag in [sequential code blocks](/references/markdown/extensions/#sequential-code-blocks): ~~~ ```cpp @@ -45,16 +45,16 @@ C++-specific paragraphs can be inserted with [language conditional rendering](/r ## Tasks and Requirements Some concepts don't always translate well to or from C++. C++ allows for a variety of paradigms and techniques, and many of them are not widely spread across other languages. Because of this, some constructs should be avoided and some translations just shouldn't be done. -- C++ is statically typed, so any task that depends on dynamic typing can be difficult to translate into C++, and attempts of forcing a C++ kata to reflect a dynamically typed interface can lead to ideas that enforce a really bad design. -- There are just a few additional libraries available for the C++ runner, so kata that take advantage of additional, specialized packages installed for other languages become much more difficult in C++. +- C++ is statically typed, so any task that depends on dynamic typing can be difficult to translate into C++, and attempts to force a C++ kata to reflect a dynamically typed interface can lead to ideas that enforce a really bad design. +- There are only a few additional libraries available to the C++ runner, so kata that take advantage of specialized packages installed for other languages become much more difficult to implement in C++. - Many features of C++ do not have direct equivalents in other popular languages: mixture of allowed paradigms, template meta-programming, unmanaged memory backed by RAII, native access to the platform and runtime, and many others. C++ kata which rely on them can be difficult to translate to other languages. ## Kata snippets and template -Due to complicated compilation model of C++ code, paired with the fact that Codewars strictly enforces names of source files, their amount, and meaning, code runner uses a `[template]()` (TODO: document C++ runner template) to concatenate all kata snippets into a single translation unit. This fact has various consequences on the code, some positive, and some negative: namespace pollution, symbols introduced by one snippet being visible in other snippets, etc. However, in the majority of cases, it does not affect kata in any way, and whenever possible, this behavior should be treated as implementation detail of C++ code runner. Kata snippets should be treated as separate source files, if possible. +Due to the complicated compilation model of C++ code, paired with the fact that Codewars strictly enforces the names, amount, and meaning of source files, the code runner uses a `[template]()` (TODO: document C++ runner template) to concatenate all kata snippets into a single translation unit. This fact has various consequences on the code, some positive, and some negative: namespace pollution, symbols introduced by one snippet being visible in other snippets, etc. However, in the majority of cases, it does not affect kata in any way, and whenever possible, this behavior should be treated as an implementation detail of the C++ code runner. Kata snippets should be treated as separate source files, if possible. -One consequence of using the template is that signature of solution function can be modified by the user in a way which can affect tests. To avoid possibility of users tampering with prototype of solution function, it's recommended to re-declare it in the tests snippet. +One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. To avoid the possibility of users tampering with the prototype of a solution function, it's recommended to re-declare it in the tests snippet. ## Coding @@ -63,7 +63,7 @@ One consequence of using the template is that signature of solution function can C++ programmers have many sets of naming conventions or code style guides. Some of them can be found for example [here](https://isocpp.org/wiki/faq/coding-standards), or [here](https://google.github.io/styleguide/cppguide.html). Codewars does not strictly enforce any of them, just use whatever set of guidelines you like, but when you do, use it consistently. -There's a few C++ coding guidelines which are violated by kata authors and translatiors particularly often: +There are a few C++ coding guidelines which are violated by kata authors and translators particularly often: - Arguments which are not cheap to copy should be passed by const reference, and not by value. - Appropriate containers should be used, _especially_ when used as input arguments or return values: - C-style raw arrays and pointers to arrays need to be avoided @@ -75,8 +75,8 @@ There's a few C++ coding guidelines which are violated by kata authors and trans ### Header files C++ authors often forget to include required header files, or just leave them out deliberately because "it works" even when some files are not included. It happens mostly due to the following reasons: -- C++ setup used by Codewars runner uses somewhat specific way to prepare and run kata snippets (TODO: describe C++ template). The structure of concatenated file which is compiled by the runner causes that some header files are included always, or that some files included in one kata snippet are automatically available in some other kata snippet. However, this behavior should not be relied on if not necessary, and every snippet should include all header files required by code it contains. -- Some header files include other header files indirectly, for example, file `foo.h` contains line `#include `, which might appear to make the explicit include for `bar.h` unnecessary. It's not true though, because the file `foo.h` might change one day, or might depend on some compiler settings or command line options, and after some changes to the configuration of the C++ runner, the `bar.h` might be not included there anymore. +- C++ setup used by the Codewars runner uses a somewhat specific way to prepare and run kata snippets (TODO: describe C++ template). The structure of concatenated files which the runner compiles causes some header files to always be included, or makes some files included in one kata snippet automatically available to some other kata snippet. However, this behavior should not be relied upon if not necessary, and every snippet should include all header files required by the code it contains. +- Some header files include other header files indirectly; for example, if file `foo.h` contains line `#include `, that might appear to make the explicit include for `bar.h` unnecessary. This is not true though, because the file `foo.h` might change one day, or might depend on some compiler settings or command line options, and after some changes to the configuration of the C++ runner, the file `bar.h` might be not included there anymore. ### Compilation warnings @@ -86,25 +86,25 @@ Compiler options related to warnings used by the C++ runner are somewhat strict ### Avoiding C -Sometimes authors consider C++ just "a C, but with classes". While C and C++ are still compatible in many ways, such perception is wrong and incorrect use of C features in C++ code leads to bad code at best, to undefined behavior and difficult to diagnose errors in more extreme cases. Features and idioms from C language should be replaced with their equivalents from modern C++: +Sometimes authors consider C++ just "C, but with classes". While C and C++ are compatible in many ways, that kind of perception is wrong: incorrect use of C features in C++ code leads to bad code at best, or to undefined behavior and difficult to diagnose errors in more extreme cases. Features and idioms from C language should be replaced with their equivalents from modern C++: -- C features must not be used where they do not have well-defined behavior in C++. For example, memory management generally should not be done with `malloc`/`free` or similar, or VLAs should be replaced with `std::array` or `std::vector`. -- C++ features should be preferred to ones inherited from C, for example: +- C features must not be used where they do not have well-defined behavior in C++. For example, memory management generally should not be done with `malloc`/`free` or similar, and VLAs should be replaced with `std::array` or `std::vector`. +- C++ features should be preferred over those inherited from C, for example: - C-style casts should be replaced with their C++ equivalents. - Functions originating from C should be replaced with related C++ functionalities: `` instead of `rand`, `` instead of `stdio.h`, etc. - C++ header files should not be confused with their C equivalents. For example, `cmath` or `cctype` should be used instead of `math.h` or `ctype.h`. -- Proper C++ data types should be used instead of their C "equivalents", especially when used as elements of solution interface (input parameters and return value). C-strings should be replaced with `std::string` (or `std::string_view`), raw C-style arrays should be replaced with `std::array`, `std::vector`, or other containers, C++ pointer wrappers should be considered instead of raw pointers, etc. +- Proper C++ data types should be used instead of their C "equivalents", especially when used as elements of a solution interface (input parameters and return value). C-strings should be replaced with `std::string` (or `std::string_view`), raw C-style arrays should be replaced with `std::array`, `std::vector`, or other containers, C++ pointer wrappers should be considered instead of raw pointers, etc. ## Tests ### Testing framework -C++ kata use the [modified version](https://github.com/codewars/igloo) of [Igloo](https://github.com/joakimkarlsson/igloo) testing framework, along with [modified version](https://github.com/codewars/snowhouse) of [Snowhouse](https://github.com/banditcpp/snowhouse) assertion library. Codewars modified both libraries to adapt them to code runner environment and make them more useful for kata authors. +C++ kata use the [modified version](https://github.com/codewars/igloo) of the [Igloo](https://github.com/joakimkarlsson/igloo) testing framework, along with a [modified version](https://github.com/codewars/snowhouse) of the [Snowhouse](https://github.com/banditcpp/snowhouse) assertion library. Codewars modified both libraries to adapt them to the code runner environment and make them more useful for kata authors. #### Custom assertion messages -Drawback of the original version of Snowhouse assertion library is that neither its `AssertThat` macro, nor `Assert::That` function, accepts a custom assertion message which could be used by authors to provide detailed information on the cause of failure. To support authors with possibility to provide useful feedback, Codewars provides a set of overloads for `Assert::That`, which accept additional message supplier: +A drawback of the original version of the Snowhouse assertion library is that neither its `AssertThat` macro nor `Assert::That` function accept a custom assertion message which could be used by authors to provide detailed information on the cause of failure. To support authors with possibility to provide useful feedback, Codewars provides a set of overloads for `Assert::That`, which accept an additional message supplier: ```cpp template @@ -122,11 +122,11 @@ static void That(const char* actual, const ExpressionType& expression, const Mes `message_supplier` is a callable compatible with a function accepting no arguments and returning `std::string`. It can be a function, a functor, a lambda expression, or an instance of any type supporting above requirements. -For more details and examples of custom assertion messages, see `[Example test suite](#example_test_suite)` below, or `[Snowhouse reference](/languages/cpp/snowhouse/)` page. +For more details and examples of custom assertion messages, see `[Example test suite](#example_test_suite)` below, or visit the `[Snowhouse reference](/languages/cpp/snowhouse/)` page. #### Stringizers -In its default configuration, Snowhouse can produce confusing assertion messages, when `expected` and `actual` values are of type it cannot stringify: +In its default configuration, Snowhouse can produce confusing assertion messages when `expected` and `actual` values are of a type it cannot stringify: ```text does_not_pretty_print_type_without_stringizer @@ -134,12 +134,12 @@ In its default configuration, Snowhouse can produce confusing assertion messages Actual: [ [unsupported type], [unsupported type] ] ``` -To rectify such issue in your tests, you can make such types suitable for stringification in a way described in dedicated documentation article on [custom Snowhouse stringizers](/languages/cpp/igloo/stringizers/). +To rectify this issue in your tests, you can make such types suitable for stringification in a way described in the dedicated documentation article on [custom Snowhouse stringizers](/languages/cpp/igloo/stringizers/). ### Random utilities -Until C++11, the most common way of producing random values was `rand` function. However, it has a set of problems: it needs to be properly seeded, and it's difficult to produce values outside `0...RAND_MAX` range. Since C++11, standard library offers a set of functionalities which are designed to produce random values of different types, from various ranges, and with better distribution when compared to `rand`. Unfortunately, API presented in `random` header seems to be confusing and difficult to use and accompanied by amount of misconceptions, and authors either use it incorrectly, or resort to (not) good, old `rand`. However, working with `random` turns out to be not that difficult: +Until C++11, the most common way of producing random values was the `rand` function. However, it has a set of problems: it needs to be properly seeded, and it is difficult to produce values outside of `0...RAND_MAX` range. Since C++11, the standard library offers a set of functionalities which are designed to produce random values of different types, from various ranges, and with better distribution when compared to `rand`. Unfortunately, the API presented in the `random` header seems to be confusing and difficult to use and accompanied by a few misconceptions, and authors either use it incorrectly or resort to (not) good, old `rand`. However, working with `random` turns out to be not that difficult: ```cpp //use random_device only as a seed @@ -178,14 +178,14 @@ _TODO: `sample`, `shuffle`_ ### Reference solution -If the test suite happens to use a reference solution to calculate expected values (which [should be avoided](/authoring/guidelines/submission-tests/#reference-solution) when possible), or some kind of reference data like precalculated arrays, etc., it must not be possible for the user to call it, redefine, overwrite, or directly access its contents. To prevent this, reference data can be made a private member of `Describe` structure. Reference solution can be either a private member of `Describe`, or a lambda-initialized local variable of an `It` block. See `[Example test suite](#example_test_suite)` for some examples. +If the test suite happens to use a reference solution to calculate expected values (which [should be avoided](/authoring/guidelines/submission-tests/#reference-solution) when possible), or some kind of reference data like precalculated arrays, etc., it must not be possible for the user to call it, redefine, overwrite, or directly access its contents. To prevent this, reference data can be made a private member of the `Describe` structure. A reference solution can be either a private member of `Describe`, or a lambda-initialized local variable of an `It` block. See `[Example test suite](#example_test_suite)` for some examples. The reference solution or data ___must not___ be defined in the [Preloaded code](/authoring/guidelines/preloaded/). ### Input mutation -General guidelines for submission tests contain a section related to [input mutation](/authoring/guidelines/submission-tests/#input-mutation) and how to prevent users from abusing it to work around kata requirements. It's usually not a problem in "real world" C++ programming, but on Codewars, users can take advantage of vulnerable test suites and modify their behavior. Constness of a function argument can be forcefully cast away by a user, or they can change the function signature and remove `const` qualifier from input parameter(s). After calling a user solution, tests should not rely on the state of such values, and they should consider them as potentially modified by a user. +General guidelines for submission tests contain a section related to [input mutation](/authoring/guidelines/submission-tests/#input-mutation) and how to prevent users from abusing it to work around kata requirements. It's usually not a problem in "real world" C++ programming, but on Codewars, users can take advantage of vulnerable test suites and modify their behavior. Constness of a function argument can be forcefully cast away by a user, or they can change the function signature and remove the `const` qualifier from input parameter(s). After calling a user solution, tests should not rely on the state of such values, and they should consider them as potentially modified by the user. ### Calling assertions @@ -200,8 +200,8 @@ To avoid the above problems, calls to assertion functions should respect the fol - Appropriate assertion functions should be used for each given test. `AssertEquals` is not the only option, and Snowhouse provides a selection of constraints and expressions suitable for many scenarios: `EqualsWithDelta` for floating point comparisons, `EqualsContainer ` to compare containers with a predicate, etc. - `Assert::That(bool)` should not be used, because it generates poor feedback on failure. The overload `Assert::That(bool_value, Equals(expected_value), message_supplier)` should be used instead. - Overloads of `Assert::That` which accept `message_supplier` should be preferred. Assertion message should provide meaningful details on the cause of failure, like test input, etc. -- Some additional attention should be paid to the order of parameters passed to `Assert::That`. It differs between various assertion libraries, and it happens to be quite often confused by authors, mixing up `actual` and `expected` in assertion messages. For the C++ testing framework, the order is `(actual, SomeConstraint(expected))`. -- To avoid unexpected crashes in tests, it's recommended to perform some additional assertions before assuming that the answer returned by the user solution has some particular form, or size. For example, if the solution returns a pointer, an explicit assertion should be added to check whether the returned pointer is valid, and not, for example, `nullptr`; the size of the returned container should be verified before accessing an element which could turn out to be located out of its bounds. +- Some additional attention should be paid to the order of parameters passed to `Assert::That`. It differs between various assertion libraries, and authors quite often happen to confuse `actual` and `expected` in assertion messages. For the C++ testing framework, the order is `(actual, SomeConstraint(expected))`. +- To avoid unexpected crashes in tests, it's recommended to perform some additional assertions before assuming that the answer returned by the user solution has some particular form or size. For example, if the solution returns a pointer, an explicit assertion should be added to check whether the returned pointer is valid, and not, for example, `nullptr`; the size of the returned container should be verified before accessing an element which could turn out to be located out of its bounds. ### Testability @@ -211,7 +211,7 @@ In C++, not everything can be easily tested. It's not possible to reliably verif ## Preloaded -C++ sometimes requires some boilerplate code to implement non-trivial tests, checks, and assertions. It can be tempting to put some code that would be common to sample tests and submission tests in the Preloaded snippet, but this approach sometimes proves to be problematic (see [here](/authoring/guidelines/preloaded/#accessibility-of-preloaded-code) why), and can cause some headaches for users who are interested in training on the kata locally, or checking how the user solution is called, etc. It's strongly discouraged to use preloaded code to make the code common for test snippets if it would hide some information or implementation details interesting to the user. +C++ sometimes requires some boilerplate code to implement non-trivial tests, checks, and assertions. It can be tempting to insert some code that would be common to sample tests and submission tests in the Preloaded snippet, but this approach sometimes proves to be problematic (see [here](/authoring/guidelines/preloaded/#accessibility-of-preloaded-code) why), and can cause some headaches for users who are interested in training on the kata locally, or checking how the user solution is called, etc. It's strongly discouraged to use preloaded code to make the code common for test snippets if it would hide some information or implementation details interesting to the user. ## Example test suite From d0367b7ab82ff4e82f172b6c784bc670a527a340 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Wed, 28 Apr 2021 23:58:04 +0200 Subject: [PATCH 12/35] Apply suggestions from code review --- content/languages/cpp/authoring/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 64ee8c14..8f424365 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -44,7 +44,7 @@ C++-specific paragraphs can be inserted with [language conditional rendering](/r ## Tasks and Requirements -Some concepts don't always translate well to or from C++. C++ allows for a variety of paradigms and techniques, and many of them are not widely spread across other languages. Because of this, some constructs should be avoided and some translations just shouldn't be done. +Some concepts don't always translate well to or from C++. C++ allows for a variety of paradigms and techniques, many of which are not necessarily suited to other languages. For this reason, certain constructs should be avoided and some kata just should not be translated to C++. - C++ is statically typed, so any task that depends on dynamic typing can be difficult to translate into C++, and attempts to force a C++ kata to reflect a dynamically typed interface can lead to ideas that enforce a really bad design. - There are only a few additional libraries available to the C++ runner, so kata that take advantage of specialized packages installed for other languages become much more difficult to implement in C++. - Many features of C++ do not have direct equivalents in other popular languages: mixture of allowed paradigms, template meta-programming, unmanaged memory backed by RAII, native access to the platform and runtime, and many others. C++ kata which rely on them can be difficult to translate to other languages. From c871a0ac09707c6ea4d29a41b70e369f12a6e83b Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 29 Apr 2021 02:52:13 +0200 Subject: [PATCH 13/35] Example test suite - initial version --- content/languages/cpp/authoring/index.md | 212 ++++++++++++----------- 1 file changed, 111 insertions(+), 101 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 8f424365..b7c3cda2 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -142,12 +142,12 @@ To rectify this issue in your tests, you can make such types suitable for string Until C++11, the most common way of producing random values was the `rand` function. However, it has a set of problems: it needs to be properly seeded, and it is difficult to produce values outside of `0...RAND_MAX` range. Since C++11, the standard library offers a set of functionalities which are designed to produce random values of different types, from various ranges, and with better distribution when compared to `rand`. Unfortunately, the API presented in the `random` header seems to be confusing and difficult to use and accompanied by a few misconceptions, and authors either use it incorrectly or resort to (not) good, old `rand`. However, working with `random` turns out to be not that difficult: ```cpp -//use random_device only as a seed -std::random_device seed; +//use random_device only as a seeder +std::random_device seeder; //create one PRNG which will be used to pick values //from (potentially many) distributions -std::mt19937 engine{ seed() }; +std::mt19937 engine{ seeder() }; //a set of distributions for every type or range you are going to need in your tests std::uniform_int_distribution< int> rand_number { 1, 100 }; @@ -218,119 +218,101 @@ C++ sometimes requires some boilerplate code to implement non-trivial tests, che Below you can find an example test suite that covers most of the common scenarios mentioned in this article. Note that it does not present all possible techniques, so actual test suites can use a different structure, as long as they keep to established conventions and do not violate authoring guidelines. -_TODO: create example test suite_ - - +/* + + +//reference solution defined as static +static void square_every_item_ref(double items[], size_t size) +{ + for(size_t i = 0; i Date: Thu, 29 Apr 2021 03:29:42 +0200 Subject: [PATCH 14/35] Example test suite --- content/languages/cpp/authoring/index.md | 84 +++++++----------------- 1 file changed, 23 insertions(+), 61 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index b7c3cda2..bfc4c122 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -190,17 +190,16 @@ General guidelines for submission tests contain a section related to [input muta ### Calling assertions -Criterion provides a set of useful [assertions](/languages/c/criterion/#assertions), but when used incorrectly, they can cause a series of problems: -- Stacktraces of a crashing user solution can reveal details that should not be visible, +Snowhouse provides a set of useful `[assertions (TODO: link to reference)](/languages/c/criterion/#assertions)`, but when used incorrectly, they can cause a series of problems: - Use of an assertion not suitable for the given case may lead to incorrect test results, - Incorrectly used assertions may produce confusing or unhelpful messages. To avoid the above problems, calls to assertion functions should respect the following rules: -- The expected value should be calculated _before_ invoking an assertion. The `expected` parameter passed to the assertion should not be a function call expression, but a value calculated directly beforehand. +- The expected value should be calculated _before_ invoking an assertion. The `expected` parameter passed to the assertion should not be a function call expression, but a value calculated directly beforehand. _(? does this point hold for C++ kata?)_ - Appropriate assertion functions should be used for each given test. `AssertEquals` is not the only option, and Snowhouse provides a selection of constraints and expressions suitable for many scenarios: `EqualsWithDelta` for floating point comparisons, `EqualsContainer ` to compare containers with a predicate, etc. -- `Assert::That(bool)` should not be used, because it generates poor feedback on failure. The overload `Assert::That(bool_value, Equals(expected_value), message_supplier)` should be used instead. - Overloads of `Assert::That` which accept `message_supplier` should be preferred. Assertion message should provide meaningful details on the cause of failure, like test input, etc. -- Some additional attention should be paid to the order of parameters passed to `Assert::That`. It differs between various assertion libraries, and authors quite often happen to confuse `actual` and `expected` in assertion messages. For the C++ testing framework, the order is `(actual, SomeConstraint(expected))`. +- `Assert::That(bool)` should not be used, because it generates poor feedback on failure. The overload `Assert::That(bool_value, Equals(expected_value), message_supplier)` should be used instead. +- Some additional attention should be paid to the order of parameters passed to `Assert::That`. Authors quite often happen to confuse `actual` and `expected` in assertion messages. For the C++ testing framework, the order is `(actual, SomeConstraint(expected))`. - To avoid unexpected crashes in tests, it's recommended to perform some additional assertions before assuming that the answer returned by the user solution has some particular form or size. For example, if the solution returns a pointer, an explicit assertion should be added to check whether the returned pointer is valid, and not, for example, `nullptr`; the size of the returned container should be verified before accessing an element which could turn out to be located out of its bounds. @@ -218,8 +217,6 @@ C++ sometimes requires some boilerplate code to implement non-trivial tests, che Below you can find an example test suite that covers most of the common scenarios mentioned in this article. Note that it does not present all possible techniques, so actual test suites can use a different structure, as long as they keep to established conventions and do not violate authoring guidelines. -_TODO: finish example test suite_ - ```cpp //include all required headers #include @@ -266,7 +263,7 @@ private: std::mt19937 engine{ std::random_device{}() }; std::function gen_number = std::bind(std::uniform_int_distribution{ 1, 100 }, engine); std::function gen_small_size = std::bind(std::uniform_int_distribution{ 2, 10 }, engine); - std::function gen_large_size = std::bind(std::uniform_int_distribution{ 20, 100 }, engine); + std::function gen_large_size = std::bind(std::uniform_int_distribution{ 80, 100 }, engine); //random test case generator std::vector generate_random_input(size_t size) { @@ -309,61 +306,26 @@ public: Assert::That(actual, EqualsContainer(expected), ExtraMessage(fmt::format("Input: {}", stringify_input(original)))); } } -}; -/* - - -//a set of large random tests, with not so detailed debugging messages -Test(random_tests, large_arrays) { - - double array[1000]; //small enough to be allocated on the stack, - double reference[1000]; //but you can use dynamic memory if necessary. - - for(int i=0; i<10; ++i) { + //a set of large random tests, with not so detailed debugging messages + It(LargeArrays) { - //generate test cases - size_t array_size = rand() % 200 + 801; - fill_random_array(array, array_size); - - //since original array is not used after tests, it's enough to create only one copy - memcpy(reference, array, sizeof(double) * array_size); - - square_every_item_ref(reference, array_size); - square_every_item(array, array_size); - - //assertion uses custom message - cr_assert_arr_eq_cmp(array, reference, array_size, cmp_double_fuzzy_equal, "Invalid answer for arrays of size %zu", array_size); - } -} - -/* - - -//reference solution defined as static -static void square_every_item_ref(double items[], size_t size) -{ - for(size_t i = 0; i Date: Thu, 29 Apr 2021 13:52:27 +0200 Subject: [PATCH 15/35] Some refinements on Igloo and Snowhouse --- content/languages/cpp/authoring/index.md | 42 ++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index bfc4c122..4306b0cf 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -98,10 +98,43 @@ Sometimes authors consider C++ just "C, but with classes". While C and C++ are c ## Tests -### Testing framework - C++ kata use the [modified version](https://github.com/codewars/igloo) of the [Igloo](https://github.com/joakimkarlsson/igloo) testing framework, along with a [modified version](https://github.com/codewars/snowhouse) of the [Snowhouse](https://github.com/banditcpp/snowhouse) assertion library. Codewars modified both libraries to adapt them to the code runner environment and make them more useful for kata authors. +### Igloo + +General structure of tests created with Igloo can be presented as: + +```cpp +Describe(TestGroup_1) { + + It(TestCase_1) { + //... + } + + It(TestCase_2) { + //... + } +}; + +Describe(TestGroup_2) { + + It(TestCase_1) { + //... + } +}; +``` + +`Describe` blocks are expanded into C++ `struct`, and they can contain anything a structure can, in particular `public` and `private` access specifiers, member fields and functions, etc. `Describe` blocks can be also nested, with one remark: nested `Describe` sections compile and get registered for execution correctly, but test output panel does not report them hierarchically: all of them are flattened into one-level test report. + +### Snowhouse + +`Snowhouse` provides `Assert::That` function, which can accept either a constraint which has to be fulfilled by an asserted value (`Assert::That(actual, Equals(expected))`), or a fluent expression accepting actual value as input and returning a boolean value indicating a success or failure (`Assert::That(actual, Is().Equal(expected))`). Both types of assertions are equivalent to each other and authors can choose whichever suits them better. For clarity, this document uses only constraints as examples, but each of them has corresponding expression. Most useful ones are: +- `Equals` +- `EqualsWithDelta` +- `EqualsContainer` +- ... what else? + + #### Custom assertion messages A drawback of the original version of the Snowhouse assertion library is that neither its `AssertThat` macro nor `Assert::That` function accept a custom assertion message which could be used by authors to provide detailed information on the cause of failure. To support authors with possibility to provide useful feedback, Codewars provides a set of overloads for `Assert::That`, which accept an additional message supplier: @@ -172,9 +205,12 @@ for(int i=0; i Date: Thu, 29 Apr 2021 15:48:19 +0200 Subject: [PATCH 16/35] usings --- content/languages/cpp/authoring/index.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 4306b0cf..994577f4 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -56,6 +56,8 @@ Due to the complicated compilation model of C++ code, paired with the fact that One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. To avoid the possibility of users tampering with the prototype of a solution function, it's recommended to re-declare it in the tests snippet. +Another issue caused by the use of the template is excessive namespace pollution. See paagraph on [namespaces](#namespaces) for more details. + ## Coding @@ -93,8 +95,14 @@ Sometimes authors consider C++ just "C, but with classes". While C and C++ are c - C-style casts should be replaced with their C++ equivalents. - Functions originating from C should be replaced with related C++ functionalities: `` instead of `rand`, `` instead of `stdio.h`, etc. - C++ header files should not be confused with their C equivalents. For example, `cmath` or `cctype` should be used instead of `math.h` or `ctype.h`. -- Proper C++ data types should be used instead of their C "equivalents", especially when used as elements of a solution interface (input parameters and return value). C-strings should be replaced with `std::string` (or `std::string_view`), raw C-style arrays should be replaced with `std::array`, `std::vector`, or other containers, C++ pointer wrappers should be considered instead of raw pointers, etc. +- Proper C++ data types should be used instead of their C "equivalents", especially when used as elements of a solution interface (input parameters and return value). C-strings should be replaced with `std::string` (or `std::string_view`), raw C-style arrays should be replaced with `std::array`, `std::vector`, or other containers, smart pointers should be considered instead of raw pointers, etc. + +### `using` directives + +To avoid problems with namespace pollution, C++ snippets should not contain `using` directives (and especially `using namespace std;`) anywhere in the global namespace. Incorrect use of `using` can sometimes cause difficult to diagnose compilation problems. For example, `using namespace std;` placed incorrectly in "Complete solution" or in "Initial solution" snippet can result in failed compilation for _some_ users. To minimize such risks, authors should stick to following guidelines: +- `using` directives should not be present in global scope of "Preloaded", "Complete solution", and "Initial solution" snippets. They are heavily discouraged in global scope of "Sample tests" and "Submission tests" snippets. This rule applies not only to `using namespace std;`, but also to other namespaces and namespace-qualified names. +- `using` directives can be perfectly used in function scope: inside of an "Initial solution" function, in test helpers, or in `It` blocks. ## Tests @@ -273,8 +281,11 @@ Describe(FixedTests) { //a test case of fixed_tests suite for primary scenario It(ExampleArray) { - std::vector items = { 0, 1, 2, 3, 4 }; - std::vector expected = { 0, 1, 4, 9, 16 }; + //using directive limited to non-global scope + using namespace std; + + vector items = { 0, 1, 2, 3, 4 }; + vector expected = { 0, 1, 4, 9, 16 }; auto actual = square_every_item(items); From 50c77008668ca3636a4796109fe27bc2872a1e30 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Thu, 29 Apr 2021 18:20:32 +0200 Subject: [PATCH 17/35] Apply suggestions from code review Co-authored-by: awesome A.D. <59703337+awesomeAD1@users.noreply.github.com> --- content/languages/cpp/authoring/index.md | 40 ++++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 994577f4..83b6d4a7 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -100,9 +100,9 @@ Sometimes authors consider C++ just "C, but with classes". While C and C++ are c ### `using` directives -To avoid problems with namespace pollution, C++ snippets should not contain `using` directives (and especially `using namespace std;`) anywhere in the global namespace. Incorrect use of `using` can sometimes cause difficult to diagnose compilation problems. For example, `using namespace std;` placed incorrectly in "Complete solution" or in "Initial solution" snippet can result in failed compilation for _some_ users. To minimize such risks, authors should stick to following guidelines: -- `using` directives should not be present in global scope of "Preloaded", "Complete solution", and "Initial solution" snippets. They are heavily discouraged in global scope of "Sample tests" and "Submission tests" snippets. This rule applies not only to `using namespace std;`, but also to other namespaces and namespace-qualified names. -- `using` directives can be perfectly used in function scope: inside of an "Initial solution" function, in test helpers, or in `It` blocks. +To avoid problems with namespace pollution, C++ snippets should not contain `using` directives (particularly `using namespace std;`) anywhere in the global namespace. Incorrect use of `using` can sometimes cause compilation problems that are difficult to diagnose. For example, `using namespace std;` placed incorrectly in the "Complete solution" or "Initial solution" snippet can result in failed compilation for _some_ users. To minimize such risks, authors should stick to following guidelines: +- `using` directives should not be present in the global scope of "Preloaded", "Complete solution", and "Initial solution" snippets. They are heavily discouraged in the global scope of "Sample tests" and "Submission tests" snippets. This rule applies not only to `using namespace std;`, but also to other namespaces and namespace-qualified names. +- `using` directives are perfectly fine inside the scope of an "Initial solution" function, test helpers, or `It` blocks. ## Tests @@ -132,11 +132,11 @@ Describe(TestGroup_2) { }; ``` -`Describe` blocks are expanded into C++ `struct`, and they can contain anything a structure can, in particular `public` and `private` access specifiers, member fields and functions, etc. `Describe` blocks can be also nested, with one remark: nested `Describe` sections compile and get registered for execution correctly, but test output panel does not report them hierarchically: all of them are flattened into one-level test report. +`Describe` blocks are expanded into C++ `struct`s, and they can contain anything a `struct` can, in particular `public` and `private` access modifiers, member fields and functions, etc. `Describe` blocks can also be nested, with one caveat: while nested `Describe` sections are correctly compiled and registered for execution, the test output panel does not report them hierarchically; they are all flattened into a single-level test report. ### Snowhouse -`Snowhouse` provides `Assert::That` function, which can accept either a constraint which has to be fulfilled by an asserted value (`Assert::That(actual, Equals(expected))`), or a fluent expression accepting actual value as input and returning a boolean value indicating a success or failure (`Assert::That(actual, Is().Equal(expected))`). Both types of assertions are equivalent to each other and authors can choose whichever suits them better. For clarity, this document uses only constraints as examples, but each of them has corresponding expression. Most useful ones are: +`Snowhouse` provides the function `Assert::That`, which can accept either a constraint which has to be fulfilled by an asserted value (`Assert::That(actual, Equals(expected))`), or a fluent expression accepting actual value as input and returning a boolean value indicating success or failure (`Assert::That(actual, Is().Equal(expected))`). Both types of assertions are equivalent to each other and authors can choose whichever suits them better. For clarity, this document uses only constraints as examples, but each of them have corresponding expressions. The most useful ones are: - `Equals` - `EqualsWithDelta` - `EqualsContainer` @@ -213,7 +213,7 @@ for(int i=0; i square_every_item(const std::vector& ages); Describe(FixedTests) { - //a test case of fixed_tests suite for primary scenario + // a test case of fixed_tests suite for a primary scenario It(ExampleArray) { - //using directive limited to non-global scope + // using directive limited to non-global scope using namespace std; vector items = { 0, 1, 2, 3, 4 }; @@ -289,11 +289,11 @@ Describe(FixedTests) { auto actual = square_every_item(items); - //Assertion constraint checking for container equality + // Assertion constraint checking for container equality Assert::That(actual, EqualsContainer(expected), ExtraMessage("Invalid answer for { 0, 1, 2, 3, 4 }")); } - //a test case of fixed_tests suite for potential edge case + // a test case of fixed_tests suite for a potential edge case It(EmptyArray) { std::vector empty; @@ -312,7 +312,7 @@ private: std::function gen_small_size = std::bind(std::uniform_int_distribution{ 2, 10 }, engine); std::function gen_large_size = std::bind(std::uniform_int_distribution{ 80, 100 }, engine); - //random test case generator + // random test case generator std::vector generate_random_input(size_t size) { std::vector generated; std::generate_n(std::back_inserter(generated), size, gen_number); @@ -332,41 +332,41 @@ private: public: - //a set of small random tests, with verbose debugging messages + // a set of small random tests, with verbose debugging messages It(SmallArrays) { for(int i=0; i<10; ++i) { - //generate test case + // generate test case size_t input_size = gen_small_size(); auto input = generate_random_input(input_size); - //tests need to copy the input vector, because - //it is used after calling user and reference solution + // tests need to copy the input vector, because + // it is used after calling the user and reference solution auto original = input; auto expected = square_every_item_ref(input); auto actual = square_every_item(input); - //assertion uses custom message to avoid confusing test output - //it also uses data from original, non-mutated input array + // assertion uses a custom message to avoid confusing test output. + // it also uses data from original, non-mutated input array Assert::That(actual, EqualsContainer(expected), ExtraMessage(fmt::format("Input: {}", stringify_input(original)))); } } - //a set of large random tests, with not so detailed debugging messages + // a set of large random tests, with less detailed debugging messages It(LargeArrays) { for(int i=0; i<10; ++i) { - //generate test cases + // generate test cases size_t input_size = gen_large_size(); auto input = generate_random_input(input_size); auto expected = square_every_item_ref(input); auto actual = square_every_item(input); - //assertion uses custom message supplier + // assertion uses custom message supplier Assert::That(actual, EqualsContainer(expected), [&]() { auto [act, exp] = std::mismatch(actual.cbegin(), actual.cend(), expected.cbegin()); auto idx = act - actual.cbegin(); From 549ea89e6c52a2ee84b32be5ee150262a7c23d2e Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 30 Apr 2021 01:41:23 +0200 Subject: [PATCH 18/35] Apply suggestions from code review --- content/languages/cpp/authoring/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 83b6d4a7..756ef013 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -70,8 +70,8 @@ There are a few C++ coding guidelines which are violated by kata authors and tra - Appropriate containers should be used, _especially_ when used as input arguments or return values: - C-style raw arrays and pointers to arrays need to be avoided - `std::array`, `std::pair`, or `std::tuple` should be used for data of known size. Avoid representing 2D points or pairs with `std::vector`. - - `std::vector` should be used for arrays of varying size - - `std::string` should be used instead of C-strings + - `std::vector` is not the only available option, and can be replaced by another container (for example, `std::set`), a set of iterators, a range, or any other construct suitable for the task. + - `std::string` or `std::string_view` should be used instead of C-strings ### Header files @@ -205,7 +205,7 @@ auto gen_letter = std::bind(rand_letter, engine); std::string input; //input string generator: string of random length, composed of random letters size_t input_length = gen_length(); //use length generator -for(int i=0; i Date: Fri, 30 Apr 2021 01:56:13 +0200 Subject: [PATCH 19/35] Apply suggestions from code review --- content/languages/cpp/authoring/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 756ef013..0004031e 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -90,7 +90,7 @@ Compiler options related to warnings used by the C++ runner are somewhat strict Sometimes authors consider C++ just "C, but with classes". While C and C++ are compatible in many ways, that kind of perception is wrong: incorrect use of C features in C++ code leads to bad code at best, or to undefined behavior and difficult to diagnose errors in more extreme cases. Features and idioms from C language should be replaced with their equivalents from modern C++: -- C features must not be used where they do not have well-defined behavior in C++. For example, memory management generally should not be done with `malloc`/`free` or similar, and VLAs should be replaced with `std::array` or `std::vector`. +- C features must not be used where they do not have well-defined behavior in C++. For example, memory management generally should not be done with `malloc`/`free` or similar, and VLAs should be replaced with `std::vector`. - C++ features should be preferred over those inherited from C, for example: - C-style casts should be replaced with their C++ equivalents. - Functions originating from C should be replaced with related C++ functionalities: `` instead of `rand`, `` instead of `stdio.h`, etc. From 842f0a20215f113b154a54eb0e6ef850d0b37cea Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 30 Apr 2021 02:23:14 +0200 Subject: [PATCH 20/35] Sidebar --- content/languages/cpp/authoring/index.md | 6 ++---- sidebars.js | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 0004031e..97963716 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -1,8 +1,6 @@ --- -title: Authoring C++ Content -kind: tutorial -sidebar_position: 0 -slug: /languages/cpp/authoring +title: Authoring JavaScript Content +sidebar_label: Authoring --- This article is meant for kata authors and translators who would like to create new content in C++. It attempts to explain how to create and organize things in a way conforming to [authoring guidelines](/authoring/guidelines/), shows the most common pitfalls and how to avoid them. diff --git a/sidebars.js b/sidebars.js index 360e12f8..3a0db7ed 100644 --- a/sidebars.js +++ b/sidebars.js @@ -164,6 +164,7 @@ module.exports = { label: "C++", items: [ "languages/cpp/index", + "languages/cpp/authoring/index", { type: "category", label: "igloo", From ffafbdc773e7535b8417cddaad624dde5559f4ed Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 30 Apr 2021 02:28:00 +0200 Subject: [PATCH 21/35] Fix frontmatter --- content/languages/cpp/authoring/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 97963716..e780ab58 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -1,5 +1,5 @@ --- -title: Authoring JavaScript Content +title: Authoring C++ Content sidebar_label: Authoring --- From 603d3dd70e5d5f2944ed482118d927fe42ed860a Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 30 Apr 2021 09:56:41 +0200 Subject: [PATCH 22/35] Suggestions from code review --- content/languages/cpp/authoring/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index e780ab58..d86e1d1f 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -52,7 +52,7 @@ Some concepts don't always translate well to or from C++. C++ allows for a varie Due to the complicated compilation model of C++ code, paired with the fact that Codewars strictly enforces the names, amount, and meaning of source files, the code runner uses a `[template]()` (TODO: document C++ runner template) to concatenate all kata snippets into a single translation unit. This fact has various consequences on the code, some positive, and some negative: namespace pollution, symbols introduced by one snippet being visible in other snippets, etc. However, in the majority of cases, it does not affect kata in any way, and whenever possible, this behavior should be treated as an implementation detail of the C++ code runner. Kata snippets should be treated as separate source files, if possible. -One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. To avoid the possibility of users tampering with the prototype of a solution function, it's recommended to re-declare it in the tests snippet. +One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. Make sure that tests are not possible to work around by users tampering with the prototype of the solution function. One possible way to ensure this is to re-declare the solution function in the tests snippet. Another issue caused by the use of the template is excessive namespace pollution. See paagraph on [namespaces](#namespaces) for more details. From a567943a8094f2b4d5901d0cbddb85c829068e9a Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 30 Apr 2021 10:12:24 +0200 Subject: [PATCH 23/35] Update links; add some remarks on contents of preloaded --- content/languages/cpp/authoring/index.md | 28 ++++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index d86e1d1f..1419550d 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -54,7 +54,7 @@ Due to the complicated compilation model of C++ code, paired with the fact that One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. Make sure that tests are not possible to work around by users tampering with the prototype of the solution function. One possible way to ensure this is to re-declare the solution function in the tests snippet. -Another issue caused by the use of the template is excessive namespace pollution. See paagraph on [namespaces](#namespaces) for more details. +Another issue caused by the use of the template is excessive namespace pollution. See paagraph on [`using` directives`](#using-directives) for more details. ## Coding @@ -75,9 +75,11 @@ There are a few C++ coding guidelines which are violated by kata authors and tra ### Header files C++ authors often forget to include required header files, or just leave them out deliberately because "it works" even when some files are not included. It happens mostly due to the following reasons: -- C++ setup used by the Codewars runner uses a somewhat specific way to prepare and run kata snippets (TODO: describe C++ template). The structure of concatenated files which the runner compiles causes some header files to always be included, or makes some files included in one kata snippet automatically available to some other kata snippet. However, this behavior should not be relied upon if not necessary, and every snippet should include all header files required by the code it contains. +- C++ setup used by the Codewars runner uses a somewhat specific way to prepare and run kata snippets (TODO: describe C++ template). The structure of concatenated files which the runner compiles causes some header files to always be included, or makes some files included in one kata snippet automatically available to some other kata snippet. However, this behavior should not be relied upon if not necessary. - Some header files include other header files indirectly; for example, if file `foo.h` contains line `#include `, that might appear to make the explicit include for `bar.h` unnecessary. This is not true though, because the file `foo.h` might change one day, or might depend on some compiler settings or command line options, and after some changes to the configuration of the C++ runner, the file `bar.h` might be not included there anymore. +Every snippet should include all header files required by the code it contains. + ### Compilation warnings @@ -88,12 +90,9 @@ Compiler options related to warnings used by the C++ runner are somewhat strict Sometimes authors consider C++ just "C, but with classes". While C and C++ are compatible in many ways, that kind of perception is wrong: incorrect use of C features in C++ code leads to bad code at best, or to undefined behavior and difficult to diagnose errors in more extreme cases. Features and idioms from C language should be replaced with their equivalents from modern C++: -- C features must not be used where they do not have well-defined behavior in C++. For example, memory management generally should not be done with `malloc`/`free` or similar, and VLAs should be replaced with `std::vector`. -- C++ features should be preferred over those inherited from C, for example: - - C-style casts should be replaced with their C++ equivalents. - - Functions originating from C should be replaced with related C++ functionalities: `` instead of `rand`, `` instead of `stdio.h`, etc. -- C++ header files should not be confused with their C equivalents. For example, `cmath` or `cctype` should be used instead of `math.h` or `ctype.h`. - Proper C++ data types should be used instead of their C "equivalents", especially when used as elements of a solution interface (input parameters and return value). C-strings should be replaced with `std::string` (or `std::string_view`), raw C-style arrays should be replaced with `std::array`, `std::vector`, or other containers, smart pointers should be considered instead of raw pointers, etc. +- C++ header files should not be confused with their C equivalents. For example, `cmath` or `cctype` should be used instead of `math.h` or `ctype.h`. +- C features must not be used where they do not have well-defined behavior in C++. For example, memory management generally should not be done with `malloc`/`free` or similar, and VLAs should be replaced with `std::vector`. ### `using` directives @@ -102,6 +101,7 @@ To avoid problems with namespace pollution, C++ snippets should not contain `usi - `using` directives should not be present in the global scope of "Preloaded", "Complete solution", and "Initial solution" snippets. They are heavily discouraged in the global scope of "Sample tests" and "Submission tests" snippets. This rule applies not only to `using namespace std;`, but also to other namespaces and namespace-qualified names. - `using` directives are perfectly fine inside the scope of an "Initial solution" function, test helpers, or `It` blocks. + ## Tests C++ kata use the [modified version](https://github.com/codewars/igloo) of the [Igloo](https://github.com/joakimkarlsson/igloo) testing framework, along with a [modified version](https://github.com/codewars/snowhouse) of the [Snowhouse](https://github.com/banditcpp/snowhouse) assertion library. Codewars modified both libraries to adapt them to the code runner environment and make them more useful for kata authors. @@ -161,7 +161,7 @@ static void That(const char* actual, const ExpressionType& expression, const Mes `message_supplier` is a callable compatible with a function accepting no arguments and returning `std::string`. It can be a function, a functor, a lambda expression, or an instance of any type supporting above requirements. -For more details and examples of custom assertion messages, see `[Example test suite](#example_test_suite)` below, or visit the `[Snowhouse reference](/languages/cpp/snowhouse/)` page. +For more details and examples of custom assertion messages, see `[Example test suite](#example-test-suite)` below, or visit the `[Snowhouse reference](/languages/cpp/snowhouse/)` page. #### Stringizers @@ -220,7 +220,7 @@ Note that `std::random_shuffle` is now considered obsolete and has been supersed ### Reference solution -If the test suite happens to use a reference solution to calculate expected values (which [should be avoided](/authoring/guidelines/submission-tests/#reference-solution) when possible), or some kind of reference data like precalculated arrays, etc., it must not be possible for the user to call it, redefine, overwrite, or directly access its contents. To prevent this, reference data can be made a private member of the `Describe` structure. A reference solution can be either a private member of `Describe`, or a lambda-initialized local variable of an `It` block. See `[Example test suite](#example_test_suite)` for some examples. +If the test suite happens to use a reference solution to calculate expected values (which [should be avoided](/authoring/guidelines/submission-tests/#reference-solution) when possible), or some kind of reference data like precalculated arrays, etc., it must not be possible for the user to call it, redefine, overwrite, or directly access its contents. To prevent this, reference data can be made a private member of the `Describe` structure. A reference solution can be either a private member of `Describe`, or a lambda-initialized local variable of an `It` block. See `[Example test suite](#example-test-suite)` for some examples. The reference solution or data ___must not___ be defined in the [Preloaded code](/authoring/guidelines/preloaded/). @@ -254,6 +254,16 @@ In C++, not everything can be easily tested. It's not possible to reliably verif C++ sometimes requires some boilerplate code to implement non-trivial tests, checks, and assertions. It can be tempting to insert some code that would be common to sample tests and submission tests in the Preloaded snippet, but this approach sometimes proves to be problematic (see [here](/authoring/guidelines/preloaded/#accessibility-of-preloaded-code) why), and can cause some headaches for users who are interested in training on the kata locally, or checking how the user solution is called, etc. It's strongly discouraged to use preloaded code to make the code common for test snippets if it would hide some information or implementation details interesting to the user. +Preloaded should not contain `#include` directives and `using` directives just to makie some names available to other snippets (see [`using` directives](#using-directives) and [header files](#header-files)). Preloaded of following form is bad, and should not be used: + +```cpp +// include them here, because all snippets use them +#include +#include + +using namespace std; +``` + ## Example test suite From 0e7d325e762fe0a484d8a1de30d73be3bb8d9a1f Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 30 Apr 2021 10:39:17 +0200 Subject: [PATCH 24/35] Sidebar --- sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sidebars.js b/sidebars.js index 3a0db7ed..59081b33 100644 --- a/sidebars.js +++ b/sidebars.js @@ -167,7 +167,7 @@ module.exports = { "languages/cpp/authoring/index", { type: "category", - label: "igloo", + label: "Testing Framework", items: ["languages/cpp/igloo", "languages/cpp/igloo/stringizers"], }, ], From cc22ec1d35e01f563d3f6be900f5f8efe0f9f867 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 30 Apr 2021 12:17:51 +0200 Subject: [PATCH 25/35] Solution template --- content/languages/cpp/solution_template.md | 32 ++++++++++++++++++++++ sidebars.js | 1 + 2 files changed, 33 insertions(+) create mode 100644 content/languages/cpp/solution_template.md diff --git a/content/languages/cpp/solution_template.md b/content/languages/cpp/solution_template.md new file mode 100644 index 00000000..1e1d26fc --- /dev/null +++ b/content/languages/cpp/solution_template.md @@ -0,0 +1,32 @@ +--- +title: Solution Setup +sidebar_label: Solution Setup +--- + +# C++ Solution Setup + +_TODO: add details_ + + +## Solution template + +```cpp +#include +#include + +using namespace igloo; + +// #include "preloaded.h" if preloaded + +// [solution] + +// [tests] + +int main(int, const char *[]) { + NullTestResultsOutput output; + TestRunner runner(output); + CodewarsTestListener listener; + runner.AddListener(&listener); + runner.Run(); +} +``` \ No newline at end of file diff --git a/sidebars.js b/sidebars.js index 59081b33..c66ce2bf 100644 --- a/sidebars.js +++ b/sidebars.js @@ -165,6 +165,7 @@ module.exports = { items: [ "languages/cpp/index", "languages/cpp/authoring/index", + "languages/cpp/solution_template", { type: "category", label: "Testing Framework", From bfebca326f7bff8468b4fea1fb54d9f9677724b4 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 30 Apr 2021 12:28:25 +0200 Subject: [PATCH 26/35] Fill TODO links; Additional libraries; --- content/languages/cpp/authoring/index.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 1419550d..f34f9e5d 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -50,7 +50,7 @@ Some concepts don't always translate well to or from C++. C++ allows for a varie ## Kata snippets and template -Due to the complicated compilation model of C++ code, paired with the fact that Codewars strictly enforces the names, amount, and meaning of source files, the code runner uses a `[template]()` (TODO: document C++ runner template) to concatenate all kata snippets into a single translation unit. This fact has various consequences on the code, some positive, and some negative: namespace pollution, symbols introduced by one snippet being visible in other snippets, etc. However, in the majority of cases, it does not affect kata in any way, and whenever possible, this behavior should be treated as an implementation detail of the C++ code runner. Kata snippets should be treated as separate source files, if possible. +Due to the complicated compilation model of C++ code, paired with the fact that Codewars strictly enforces the names, amount, and meaning of source files, the code runner uses a [template](/languages/cpp/solution_template) to concatenate all kata snippets into a single translation unit. This fact has various consequences on the code, some positive, and some negative: namespace pollution, symbols introduced by one snippet being visible in other snippets, etc. However, in the majority of cases, it does not affect kata in any way, and whenever possible, this behavior should be treated as an implementation detail of the C++ code runner. Kata snippets should be treated as separate source files, if possible. One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. Make sure that tests are not possible to work around by users tampering with the prototype of the solution function. One possible way to ensure this is to re-declare the solution function in the tests snippet. @@ -75,7 +75,7 @@ There are a few C++ coding guidelines which are violated by kata authors and tra ### Header files C++ authors often forget to include required header files, or just leave them out deliberately because "it works" even when some files are not included. It happens mostly due to the following reasons: -- C++ setup used by the Codewars runner uses a somewhat specific way to prepare and run kata snippets (TODO: describe C++ template). The structure of concatenated files which the runner compiles causes some header files to always be included, or makes some files included in one kata snippet automatically available to some other kata snippet. However, this behavior should not be relied upon if not necessary. +- C++ setup used by the Codewars runner uses a somewhat specific way to prepare and run kata snippets (see [Kata snippets and template](#kata-snippets-and-template)). The structure of concatenated files which the runner compiles causes some header files to always be included, or makes some files included in one kata snippet automatically available to some other kata snippet. However, this behavior should not be relied upon if not necessary. - Some header files include other header files indirectly; for example, if file `foo.h` contains line `#include `, that might appear to make the explicit include for `bar.h` unnecessary. This is not true though, because the file `foo.h` might change one day, or might depend on some compiler settings or command line options, and after some changes to the configuration of the C++ runner, the file `bar.h` might be not included there anymore. Every snippet should include all header files required by the code it contains. @@ -245,6 +245,11 @@ To avoid the above problems, calls to assertion functions should respect the fol - To avoid unexpected crashes in tests, it's recommended to perform some additional assertions before assuming that the answer returned by the user solution has some particular form or size. For example, if the solution returns a pointer, an explicit assertion should be added to check whether the returned pointer is valid, and not, for example, `nullptr`; the size of the returned container should be verified before accessing an element which could turn out to be located out of its bounds. +### Additional libraries + +Currently, C++ setup provides just a few [additional libraries](/languages/cpp/#packages), but they can come helpful for authors to create better kata. For example, `{fmt}` library can be used to compose assertion messages and provide better feedback from tests. + + ### Testability In C++, not everything can be easily tested. It's not possible to reliably verify the validity of a returned pointer. It's difficult to test for conditions which result in a crash or undefined behavior. Sometimes the only way is to skip some checks or crash the tests. From 695623f140b8116ec7845bd88c70caad0672525d Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 30 Apr 2021 12:39:39 +0200 Subject: [PATCH 27/35] Link to testing framework reference --- content/languages/cpp/authoring/index.md | 2 +- content/languages/cpp/igloo.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index f34f9e5d..ee0b55fb 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -232,7 +232,7 @@ General guidelines for submission tests contain a section related to [input muta ### Calling assertions -Snowhouse provides a set of useful `[assertions (TODO: link to reference)](/languages/c/criterion/#assertions)`, but when used incorrectly, they can cause a series of problems: +Snowhouse provides a set of useful [assertions](/languages/cpp/igloo#snowhouse-assertions)`, but when used incorrectly, they can cause a series of problems: - Use of an assertion not suitable for the given case may lead to incorrect test results, - Incorrectly used assertions may produce confusing or unhelpful messages. diff --git a/content/languages/cpp/igloo.md b/content/languages/cpp/igloo.md index 83e030cf..d8eb6449 100644 --- a/content/languages/cpp/igloo.md +++ b/content/languages/cpp/igloo.md @@ -18,3 +18,7 @@ We use patched Igloo. [changes](https://github.com/joakimkarlsson/igloo/compare/ Assertion Library [snowhouse v3.1.1](https://github.com/banditcpp/snowhouse/releases/tag/v3.1.1). Igloo's website is no longer available, use https://web.archive.org/web/20161217124718/http://igloo-testing.org/ --> + + +## Snowhouse assertions + From e6768cfa6a1b83a5a19ba1799eaef43b95f59adb Mon Sep 17 00:00:00 2001 From: hobovsky Date: Fri, 30 Apr 2021 21:54:08 +0200 Subject: [PATCH 28/35] Remove a note on redeclaration of user solution. --- content/languages/cpp/authoring/index.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index ee0b55fb..d2e49e38 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -52,7 +52,7 @@ Some concepts don't always translate well to or from C++. C++ allows for a varie Due to the complicated compilation model of C++ code, paired with the fact that Codewars strictly enforces the names, amount, and meaning of source files, the code runner uses a [template](/languages/cpp/solution_template) to concatenate all kata snippets into a single translation unit. This fact has various consequences on the code, some positive, and some negative: namespace pollution, symbols introduced by one snippet being visible in other snippets, etc. However, in the majority of cases, it does not affect kata in any way, and whenever possible, this behavior should be treated as an implementation detail of the C++ code runner. Kata snippets should be treated as separate source files, if possible. -One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. Make sure that tests are not possible to work around by users tampering with the prototype of the solution function. One possible way to ensure this is to re-declare the solution function in the tests snippet. +One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. Make sure that tests are not possible to work around by users tampering with the prototype of the solution function. Another issue caused by the use of the template is excessive namespace pollution. See paagraph on [`using` directives`](#using-directives) for more details. @@ -285,10 +285,6 @@ Below you can find an example test suite that covers most of the common scenario #include #include -//redeclare the user solution -std::vector square_every_item(const std::vector& ages); - - Describe(FixedTests) { // a test case of fixed_tests suite for a primary scenario From d7828022f8ccca62728d45fed4a8df5bbf8bcc12 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Sat, 1 May 2021 00:52:45 +0200 Subject: [PATCH 29/35] Update content/languages/cpp/authoring/index.md Co-authored-by: kazk --- content/languages/cpp/authoring/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index d2e49e38..05998edc 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -1,6 +1,7 @@ --- title: Authoring C++ Content sidebar_label: Authoring +slug: /languages/cpp/authoring --- This article is meant for kata authors and translators who would like to create new content in C++. It attempts to explain how to create and organize things in a way conforming to [authoring guidelines](/authoring/guidelines/), shows the most common pitfalls and how to avoid them. From e0f04a6d163615d773a274d9058df68a28d71d62 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Sat, 1 May 2021 02:48:25 +0200 Subject: [PATCH 30/35] Remarks from code review --- content/languages/cpp/authoring/index.md | 29 +++++++++---------- .../cpp/{igloo.md => igloo/index.md} | 9 ++---- ...ution_template.md => solution-template.md} | 0 sidebars.js | 4 +-- 4 files changed, 19 insertions(+), 23 deletions(-) rename content/languages/cpp/{igloo.md => igloo/index.md} (83%) rename content/languages/cpp/{solution_template.md => solution-template.md} (100%) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 05998edc..9aa6bf1f 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -45,13 +45,12 @@ C++-specific paragraphs can be inserted with [language conditional rendering](/r Some concepts don't always translate well to or from C++. C++ allows for a variety of paradigms and techniques, many of which are not necessarily suited to other languages. For this reason, certain constructs should be avoided and some kata just should not be translated to C++. - C++ is statically typed, so any task that depends on dynamic typing can be difficult to translate into C++, and attempts to force a C++ kata to reflect a dynamically typed interface can lead to ideas that enforce a really bad design. -- There are only a few additional libraries available to the C++ runner, so kata that take advantage of specialized packages installed for other languages become much more difficult to implement in C++. - Many features of C++ do not have direct equivalents in other popular languages: mixture of allowed paradigms, template meta-programming, unmanaged memory backed by RAII, native access to the platform and runtime, and many others. C++ kata which rely on them can be difficult to translate to other languages. ## Kata snippets and template -Due to the complicated compilation model of C++ code, paired with the fact that Codewars strictly enforces the names, amount, and meaning of source files, the code runner uses a [template](/languages/cpp/solution_template) to concatenate all kata snippets into a single translation unit. This fact has various consequences on the code, some positive, and some negative: namespace pollution, symbols introduced by one snippet being visible in other snippets, etc. However, in the majority of cases, it does not affect kata in any way, and whenever possible, this behavior should be treated as an implementation detail of the C++ code runner. Kata snippets should be treated as separate source files, if possible. +Due to the complicated compilation model of C++ code, paired with the fact that Codewars strictly enforces the names, amount, and meaning of source files, the code runner uses a [template](/languages/cpp/solution-template) to concatenate all kata snippets into a single translation unit. This fact has various consequences on the code, some positive, and some negative: namespace pollution, symbols introduced by one snippet being visible in other snippets, etc. However, in the majority of cases, it does not affect kata in any way, and whenever possible, this behavior should be treated as an implementation detail of the C++ code runner. Kata snippets should be treated as separate source files, if possible. One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. Make sure that tests are not possible to work around by users tampering with the prototype of the solution function. @@ -62,9 +61,9 @@ Another issue caused by the use of the template is excessive namespace pollution ### Code style -C++ programmers have many sets of naming conventions or code style guides. Some of them can be found for example [here](https://isocpp.org/wiki/faq/coding-standards), or [here](https://google.github.io/styleguide/cppguide.html). Codewars does not strictly enforce any of them, just use whatever set of guidelines you like, but when you do, use it consistently. +C++ programmers have many sets of naming conventions or code style guides. Some of them can be found for example in [ISO C++ FAQ](https://isocpp.org/wiki/faq/coding-standards), or [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html). Codewars does not strictly enforce any of them, just use whatever code style you like, but when you do, use it consistently. -There are a few C++ coding guidelines which are violated by kata authors and translators particularly often: +There are a few C++ language guidelines which are violated by kata authors and translators particularly often: - Arguments which are not cheap to copy should be passed by const reference, and not by value. - Appropriate containers should be used, _especially_ when used as input arguments or return values: - C-style raw arrays and pointers to arrays need to be avoided @@ -182,34 +181,34 @@ To rectify this issue in your tests, you can make such types suitable for string Until C++11, the most common way of producing random values was the `rand` function. However, it has a set of problems: it needs to be properly seeded, and it is difficult to produce values outside of `0...RAND_MAX` range. Since C++11, the standard library offers a set of functionalities which are designed to produce random values of different types, from various ranges, and with better distribution when compared to `rand`. Unfortunately, the API presented in the `random` header seems to be confusing and difficult to use and accompanied by a few misconceptions, and authors either use it incorrectly or resort to (not) good, old `rand`. However, working with `random` turns out to be not that difficult: ```cpp -//use random_device only as a seeder +// use random_device only as a seeder std::random_device seeder; -//create one PRNG which will be used to pick values -//from (potentially many) distributions +// create one PRNG which will be used to pick values +// from (potentially many) distributions std::mt19937 engine{ seeder() }; -//a set of distributions for every type or range you are going to need in your tests +// a set of distributions for every type or range you are going to need in your tests std::uniform_int_distribution< int> rand_number { 1, 100 }; std::uniform_int_distribution< size_t> rand_length { 20, 100 }; std::uniform_real_distribution rand_coefficient{ -1, 1 }; std::uniform_int_distribution< char> rand_letter { 'a', 'z' }; std::uniform_int_distribution< char> rand_bool { 0, 1 }; -//etc. +// etc. -//for convenience, distributions can be bound with PRNG +// for convenience, distributions can be bound with PRNG auto gen_length = std::bind(rand_length, engine); auto gen_letter = std::bind(rand_letter, engine); std::string input; -//input string generator: string of random length, composed of random letters -size_t input_length = gen_length(); //use length generator +// input string generator: string of random length, composed of random letters +size_t input_length = gen_length(); // use length generator for(size_t i=0; i Date: Sat, 1 May 2021 02:50:29 +0200 Subject: [PATCH 31/35] Fix sidebar --- sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sidebars.js b/sidebars.js index b96fc0b5..aa6613fc 100644 --- a/sidebars.js +++ b/sidebars.js @@ -169,7 +169,7 @@ module.exports = { { type: "category", label: "Igloo", - items: ["languages/cpp/igloo", "languages/cpp/igloo/stringizers"], + items: ["languages/cpp/igloo/index", "languages/cpp/igloo/stringizers"], }, ], }, From 69f46fafc5a9b5000fc99843569deca39adf0c5f Mon Sep 17 00:00:00 2001 From: hobovsky Date: Sat, 1 May 2021 12:04:44 +0200 Subject: [PATCH 32/35] Apply suggestions from code review Co-authored-by: error256 --- content/languages/cpp/authoring/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 9aa6bf1f..5ff4dd31 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -232,13 +232,13 @@ General guidelines for submission tests contain a section related to [input muta ### Calling assertions -Snowhouse provides a set of useful [assertions](/languages/cpp/igloo#snowhouse-assertions)`, but when used incorrectly, they can cause a series of problems: +Snowhouse provides a set of useful [assertions](/languages/cpp/igloo#snowhouse-assertions), but when used incorrectly, they can cause a series of problems: - Use of an assertion not suitable for the given case may lead to incorrect test results, - Incorrectly used assertions may produce confusing or unhelpful messages. To avoid the above problems, calls to assertion functions should respect the following rules: - The expected value should be calculated _before_ invoking an assertion. The `expected` parameter passed to the assertion should not be a function call expression, but a value calculated directly beforehand. _(? does this point hold for C++ kata?)_ -- Appropriate assertion functions should be used for each given test. `AssertEquals` is not the only option, and Snowhouse provides a selection of constraints and expressions suitable for many scenarios: `EqualsWithDelta` for floating point comparisons, `EqualsContainer ` to compare containers with a predicate, etc. +- Appropriate assertion functions should be used for each given test. `AssertEquals` is not the only option, and Snowhouse provides a selection of constraints and expressions suitable for many scenarios: `EqualsWithDelta` for floating point comparisons, `EqualsContainer` to compare containers with a predicate, etc. - Overloads of `Assert::That` which accept `message_supplier` should be preferred. Assertion message should provide meaningful details on the cause of failure, like test input, etc. - `Assert::That(bool)` should not be used, because it generates poor feedback on failure. The overload `Assert::That(bool_value, Equals(expected_value), message_supplier)` should be used instead. - Some additional attention should be paid to the order of parameters passed to `Assert::That`. Authors quite often happen to confuse `actual` and `expected` in assertion messages. For the C++ testing framework, the order is `(actual, SomeConstraint(expected))`. From dc3a1046c6fc1132a4b319c1bc2da05bd3c18d55 Mon Sep 17 00:00:00 2001 From: error256 Date: Sat, 1 May 2021 13:57:30 +0300 Subject: [PATCH 33/35] Update content/languages/cpp/authoring/index.md Co-authored-by: hobovsky --- content/languages/cpp/authoring/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 5ff4dd31..6648e0ec 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -237,7 +237,6 @@ Snowhouse provides a set of useful [assertions](/languages/cpp/igloo#snowhouse-a - Incorrectly used assertions may produce confusing or unhelpful messages. To avoid the above problems, calls to assertion functions should respect the following rules: -- The expected value should be calculated _before_ invoking an assertion. The `expected` parameter passed to the assertion should not be a function call expression, but a value calculated directly beforehand. _(? does this point hold for C++ kata?)_ - Appropriate assertion functions should be used for each given test. `AssertEquals` is not the only option, and Snowhouse provides a selection of constraints and expressions suitable for many scenarios: `EqualsWithDelta` for floating point comparisons, `EqualsContainer` to compare containers with a predicate, etc. - Overloads of `Assert::That` which accept `message_supplier` should be preferred. Assertion message should provide meaningful details on the cause of failure, like test input, etc. - `Assert::That(bool)` should not be used, because it generates poor feedback on failure. The overload `Assert::That(bool_value, Equals(expected_value), message_supplier)` should be used instead. From ead836c3fa8f4e51e3ab745c7a4775a4aa6ad6f5 Mon Sep 17 00:00:00 2001 From: hobovsky Date: Sun, 2 May 2021 12:22:42 +0200 Subject: [PATCH 34/35] Remove details on solution template --- content/languages/cpp/authoring/index.md | 2 +- content/languages/cpp/solution-template.md | 32 ---------------------- sidebars.js | 1 - 3 files changed, 1 insertion(+), 34 deletions(-) delete mode 100644 content/languages/cpp/solution-template.md diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index 6648e0ec..ecb93e74 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -50,7 +50,7 @@ Some concepts don't always translate well to or from C++. C++ allows for a varie ## Kata snippets and template -Due to the complicated compilation model of C++ code, paired with the fact that Codewars strictly enforces the names, amount, and meaning of source files, the code runner uses a [template](/languages/cpp/solution-template) to concatenate all kata snippets into a single translation unit. This fact has various consequences on the code, some positive, and some negative: namespace pollution, symbols introduced by one snippet being visible in other snippets, etc. However, in the majority of cases, it does not affect kata in any way, and whenever possible, this behavior should be treated as an implementation detail of the C++ code runner. Kata snippets should be treated as separate source files, if possible. +Due to the complicated compilation model of C++ code, paired with the fact that Codewars strictly enforces the names, amount, and meaning of source files, the code runner uses a template to concatenate all kata snippets into a single translation unit. This fact has various consequences on the code, some positive, and some negative: namespace pollution, symbols introduced by one snippet being visible in other snippets, etc. However, in the majority of cases, it does not affect kata in any way, and whenever possible, this behavior should be treated as an implementation detail of the C++ code runner. Kata snippets should be treated as separate source files, if possible. One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. Make sure that tests are not possible to work around by users tampering with the prototype of the solution function. diff --git a/content/languages/cpp/solution-template.md b/content/languages/cpp/solution-template.md deleted file mode 100644 index 1e1d26fc..00000000 --- a/content/languages/cpp/solution-template.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Solution Setup -sidebar_label: Solution Setup ---- - -# C++ Solution Setup - -_TODO: add details_ - - -## Solution template - -```cpp -#include -#include - -using namespace igloo; - -// #include "preloaded.h" if preloaded - -// [solution] - -// [tests] - -int main(int, const char *[]) { - NullTestResultsOutput output; - TestRunner runner(output); - CodewarsTestListener listener; - runner.AddListener(&listener); - runner.Run(); -} -``` \ No newline at end of file diff --git a/sidebars.js b/sidebars.js index aa6613fc..dc04e114 100644 --- a/sidebars.js +++ b/sidebars.js @@ -165,7 +165,6 @@ module.exports = { items: [ "languages/cpp/index", "languages/cpp/authoring/index", - "languages/cpp/solution-template", { type: "category", label: "Igloo", From 941d303bfc47feb9c3a06b7ba36a688d7892ec9f Mon Sep 17 00:00:00 2001 From: kazk Date: Sun, 2 May 2021 20:05:19 -0700 Subject: [PATCH 35/35] Apply suggestions from code review --- content/languages/cpp/authoring/index.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/languages/cpp/authoring/index.md b/content/languages/cpp/authoring/index.md index ecb93e74..b50401bd 100644 --- a/content/languages/cpp/authoring/index.md +++ b/content/languages/cpp/authoring/index.md @@ -54,7 +54,7 @@ Due to the complicated compilation model of C++ code, paired with the fact that One consequence of using a template is that the signature of a solution function can be modified by the user in a way which can affect tests. Make sure that tests are not possible to work around by users tampering with the prototype of the solution function. -Another issue caused by the use of the template is excessive namespace pollution. See paagraph on [`using` directives`](#using-directives) for more details. +Another issue caused by the use of the template is excessive namespace pollution. See paragraph on [`using` directives`](#using-directives) for more details. ## Coding @@ -138,7 +138,6 @@ Describe(TestGroup_2) { - `Equals` - `EqualsWithDelta` - `EqualsContainer` -- ... what else? #### Custom assertion messages