TINY: Jednohubky

00 – Formátovaný zápis do proudu

V této jednohubce budete mít za úkol naimplementovat jednoduchou funkci, write_stats, která bere dva argumenty, sadu dat a výstupní proud, a do výstupního proudu zapíše 3 statistické údaje o datech: minimum, maximum a průměr.

Údaje musí být zapsány ve specifickém formátu, například pro vstupní data “1.23, 5.44, -23” by výstup vypadal takto:

min: -23.00
max: 5.44
mean: -5.44

Všimněte si, že čísla jsou vždy vypsaná se dvěma desetinnými čísly.

Projekt s testy najdete zde.

Do Brute odevzdávejte pouze Váš soubor tiny-00.cpp s vaší implementací funkce write_stats.


Rady

Formátovací manipulátory, které budete potřebovat, najdete v jedné ze dvou hlaviček:

Zároveň vás může zajímat šablona numeric_limits, přes kterou můžete získat informace o největší možné hodnotě typu a další podobné informace.

01 – Kopie a reference

V této jednohubce dostanete rozpracovaný kód implementující dvě přetížení funkce pluralize, specificky

  • std::string pluralize(std::string const& str);
  • std::vector<std::string> pluralize(std::vector<std::string> const& str);

Jak název napovídá, úkol této funkce je převést předané slovo, případně vektor slov, na jejich množné číslo. V kódu který máte je ovšem chyba, takže část testů neprochází, a vaším úkolem je změnit kód který dostanete tak, aby všechny testy procházely.

Projekt s testy najdete zde.

Do Brute odevzdávejte pouze zazipovaný soubor tiny-01.cpp s vašimi změnami.


Rady

Tento úkol si můžete udělat velmi těžký, nebo velmi jednoduchý. Proto vám doporučujeme si před hledáním chyby v kódu pustit testy a rozmyslet si, co vidíte na výstupu.

02 – Práce s dynamicky alokovanou pamětí

V této jednohubce znovu dostanete již hotový kód, ale na rozdíl od minulé jednohubky všechny testy procházejí. To ovšem neznamená, že je s ním vše v pořádku – jak název napovídá, obsahuje chybu práce s dynamicky alokovanou pamětí.

Projekt s implementací a testy najdete zde.

Poznámka k implementaci: funkcionalita funkce summarize_data je popsána uvnitř hlavičky tiny-02.hpp anglicky. Pro jistotu ji zde uvádíme i česky:

  • Funkce summarize_data vrací statistiky pro každý validní řádek uvnitř předaného proudu. Nevalidní řádky jsou přeskočeny. Samotný řádek začíná číslem n, které určuje kolik čísel na řádku následuje. Následná čísla musí být z rozsahu [0, 255].


Rady

Pokud jste na Linuxu, již mnohokrát zmiňovaný Valgrind vám řekne k jaké chybě dochází a kde. Pokud jste na Windows, doporučujeme Dr. Memory, který funguje zhruba stejně jako Valgrind. Pokud používáte aktuální verzi OS X, Valgrind vám bohužel nebude fungovat, budete tedy muset použít o něco komplikovanější nástroj jménem AddressSanitizer.

03 – Život objektů

Poučen problémy v minulé jednohubce se programátor rozhodl opravit kód summarize_data tak, aby ke stejné chybě již nemohlo dojít. Vytvořil si tedy třídu fixed_array, která za alokované pole zodpovídá a vždy ho správně uvolní. Bohužel se to zcela nepovedlo a na vás je, abyste to opravili.

Projekt s implementací a testy najdete zde.

Též trochu změnil formát výstupu, protože si uvědomil, že když se špatné řádky prostě vypustí, tak není možné zpětně zjistit, které řádky nebyly platné. Nyní se tedy pro neplatné řádky vrací statistika, která říká, že na řádku bylo std::numeric_limits<size_t>::max() prvků.


Rady

Pokud se vám kompilátor snaží poradit, je dobrý nápad mu naslouchat.

04 – Kopie a přesuny

V této jednohubce bude vaším úkolem doplnit funkční kopírovací a přesunující operace pro jednoduchý binární vyhledávací strom.

Projekt s implementací a testy najdete zde.


Rady

Připomeňte si obsah odpovídající cvičení.

05 – Operátory

V této jednohubce si vyzkoušíte přetěžování operátorů pro třídu. Specificky se jedná o třídu pjc::complex a matematické operátory +, - a * (pouze v binární variantě). Specificky chceme, aby šly sčítat (respektive odčítat nebo násobit) dvě instance třídy pjc::complex nebo instance třídy pjc::complex s double.

Projekt s implementací a testy najdete zde.

Do Brute tentokrát odevzdejte i vámi změněnou hlavičku tiny-05.hpp.


Rady

Pokud si nepamatujete jak fungují dané operace nad komplexními čísly, wikipedie poradí.

06 – Nevirtuální a virtuální rozhraní

V této jednohubce vás čeká modifikace již existující virtuální hierarchie tříd. Specificky se jedná o hierarchii různých generátorů náhodných čísel a vaším úkolem bude do ní doplnit sběr různých statistik:

  • Nejmenší/největší vygenerovaný int, respektive double
  • Počet vygenerovaných int, double čísel
  • Počet vygenerovaných bool, včetně toho, kolik jich bylo pravdivých a kolik nepravdivých

Projekt s implementací a testy najdete zde.

Do Brute tentokrát odevzdejte i vámi změněnou hlavičku tiny-06.hpp.


Rady

Existují dva způsoby, jak tuto jednohubku vyřešit. Jeden je pracnější, zabere vám delší dobu, ale nemusíte u něj vůbec přemýšlet. Alternativně můžete použít idiom, kterému se říká Nevirtuální rozhraní (NVI – NonVirtual Interface), který umožňuje předkovi (rozhraní) vynutit, aby byly dodrženy invarianty během volání poděděných metod.

NVI je implementace návrhového vzoru “Template Method” (šablona, šablonová metoda), specifická pro C++. Bohužel se nám nepovedlo najít dobrý text vysvětlující specificky NVI v češtině, ale WikiBooks obsahuje dobré vysvětlení v angličtině.

07 – Simulace kovariantních návratových typů s chytrými ukazateli

Jak jsme si ukazovali na cvičení, v C++ nelze kombinovat kovariantní návratové typy s chytrými ukazately, jako je například std::unique_ptr. Nicméně, chytrou kombinací nevirtuálního rozhraní, schovávání, virtuálního rozhraní a boilerplate, se dá kovariance simulovat.

V této jednohubce budete znovu pracovat s hierarchií tříd které generují náhodná čísla a vaším úkolem bude doplnit metodu clone, která vrací příslušně konkretizovaný std::unique_ptr a zároveň se chová polymorfně.

Projekt s implementací a testy najdete zde.

Do Brute odevzdejte i vámi změněnou hlavičku tiny-07.hpp.


Rady

Je jasné, že metoda clone nemůže být virtuální, ale pro její správné chování potřebujete virtuální polymorfismus. Jinak řečeno, budete muset těch metod implementovat více.

08 – Jednoduchá šablonová funkce

V této jednohubce si napíšete jednoduchou šablonovou funkci clamp. Tato funkce se v C++17 stala součástí standardní knihovny, ale protože v tomto předmětu používáme tento semestr stále ještě C++14, budeme ji implementovat my.

Funkce clamp má dvě přetížení

  • T const& clamp(T const& value, T const& low, T const& high)
  • T const& clamp(T const& value, T const& low, T const& high, Compare cmp)

které vrací low pokud value je menší než low, high pokud value je větší než high a jinak value. Jinak řečeno, value je omezeno do rozsahu [low, high]. První overload předpokládá, že T je porovnatelné pomocí operátoru <, druhý overload pak bere vhodný komparátor jako 4. argument.

Projekt s implementací a testy najdete zde.

Do Brute odevzdejte vaši hlavičku tiny-08.hpp.


Rady

Není potřeba implementovat obě funkce stejně, můžete implementovat jednu voláním druhé. K tomu se vám bude hodit obsah hlavičky functional.


Další

K této jednohubce vám navíc nabízíme dvě otázky k zamyšlení:

  • Pokud uživatel předá parametry ve špatném pořadí, může dostat špatný výsledek. Toto se dá opravit, pokud funkci přejmenujeme1). Jaké jméno by to bylo?
  • const int& clamped = clamp(value, 0, 255); může vést k chybě. K jaké? Jde to rozumně opravit?

09 – Jednoduché metaprogramování

V této jednohubce se naposled vrátíme k naší implementaci vector ze cvičení. Poslední, co jsme s ním dělali, byla optimalizace metody push_back(Iterator, Iterator) dle typu iterátoru, kterou dostala. V této jednohubce budeme podobně optimalizovat zvětšování (a kopírování) vektoru v závislosti na typu, pro který je náš vector instanciovaný. Specificky to znamená, že budeme rozlišovat 3 druhy typů,

  • Triviálně kopírovatelné typy
  • Typy, které mají přesunující přiřazení označené jako noexcept a nejsou triviálně kopírovatelné
  • Ostatní typy

a budeme se k nim chtít chovat jinak, když budeme rozšiřovat pole uvnitř vektoru. Specificky, triviálně kopírovatelné typy chceme kopírovat pomocí memcpy, typy, které nejsou triviálně kopírovatelné, ale nemohou vyhodit výjimku při přesunujícím přiřazení, chceme opravdu přesunout a typy, které výjimku vyhodit mohou, chceme prostě zkopírovat.

Projekt s implementací a testy najdete zde.

Do Brute odevzdejte hlavičky array.hpp, vector.hpp a případné jiné hlavičky které jste vytvořili.

K zjištění, jestli je typ triviálně kopírovatelný používejte pjc::is_trivially_constructible z pjc-traits.hpp, a ne std::is_trivially_constructible.


Rady

Pro implementaci tohoto úkolu doporučujeme použít SFINAE (viz přednáška), a ne tag dispatch, který byl použit na cvičení.

Jestli přesunující přiřazení daného typu je noexcept se dá zjistit pomocí traitu is_nothrow_move_assignable ze standardní knihovny.


Další

Triviálně kopírovatelné typy jsou typy s triviálním kopírovacím konstruktorem a přiřazením. Méně kruhově definováno, jsou to typy u kterých je volání kopírovacích operací ekvivalentní se zkopírováním jejich bitové reprezentace2).

Důvod, proč nechceme přesouvat typy, které mohou vyhodit výjimku je poskytnutí tzv. silné záruky. Detaily jsou v přednášce “Exception Safety”, hrubý nástin pak je, že pokud dojde k chybě během zvětšování vektoru, tak se silnou zárukou zůstanou původní prvky zachovány a nepřijdeme o data.

1)
A změníme implementaci tak, aby odpovídala novému jménu
2)
Toto striktně řečeno není pravda pro typ trivially_copyable_tracker, proto jsme zavedli pjc::is_trivially_constructible trait a specializovali ho pro tento typ.
courses/b6b36pcc/ukoly/hwjednohubky.txt · Last modified: 2022/09/14 16:19 by nagyoing