//Autor: Marcin Kowalczyk

#include<cassert>

// Coraz większą przykłada się uwagę do utrzymwalności kodu oraz jego niezawodności.
//Dlatego w do kolejnych standardów C++ dodawane są nowe atrybuty pomagające kompilować
// kompilatorom oraz takie które pomagają wyeliminować popularne pomyłki i sprawiają, że
// utrzymanie kodu jest łatwiejsze. Dlatego chciałbym omówić 3 nowe atrybuty dodane do
// standardu C++17: maybe_unused, fallthrough, nodiscard

// UWAGA: wszystkie przykłady są kompilowane za pomocą GCC z ustawionymi flagami:
// g++ –std=c++17 -Werror -Wall -Wextra -Wimplicit-fallthrough

// Nowo dodane atrybuty są bardzo przydatne jeżeli chcemy pisać zrozumiały i czysty kod,
// który kompiluję się bez błędów. Podane atrybuty istniały nie oficjalnie od dawna z
// własnymi nazwami wprowadzonymi przez poszczególne kompilatory. Dlatego można stwierdzić, że
// faktycznie istniała potrzeba wprowadzenia tych atrybutów. Od wersji C++ 17 zostały one
// ustandardyzowane i wprowadzone do wersji oficjalnej.

void f() {}
void g() {}
void k() {}

//maybe_unused

//struktura która zwóróci błąd jeżeli została tylko zainicjowana, nie użyta
struct structur_unused_warning
{
    int a;
    int b;
};

//struktura która nie zwróci błędu jężeli została tylko zainicjowana a nie użyta - ze względu na atrybut
struct [[maybe_unused]] structur_unused_no_warning
{
    int a;
    int b;
};

//statyczna funkcja która zwraca błąd jeżeli nie została ani razu użyta w programie
static void function_unused_warning() { }

//funckja która nie zwraca błędu, nawet jeżeli została nie użyta w programie
[[maybe_unused]] static void function_unused_no_warning() { }

//błąd przy kompilacji ponieważ zmienna a została jedynie zainicjowana wartością bez poźniejszego użycia (przy kompilacji z flagą -DNDEBUG)
void unused_variable_warning()
{
    int a = 0;
    assert(a==0);
}

//brak błędu przy kompilacji przy kompilacji z flagą -DNDEBUG)
void unused_variable_no_warning()
{
    [[maybe_unused]] int a = 0;
    assert(a==0);
}

//fallthrough

void switch_fallthrough(int n)
{
    switch(n)
    {
        case 1:
        {
            f();
            break;
        }
        case 2:
        {
            g();
            [[fallthrough]];
        }
        case 3:
        {
            k();
            //brak świadmości o przejściu do następnego bloku case dlatego compilator zwróci błąd
        }
        case 4:
        {
            //poprawne zakończenie atrybutem fallthrough ponieważ wszystkie możliwe scieżki są opatrzone atrybutem
            if (n > 0)
            {
                n--;
                [[fallthrough]];
            }
            else
                [[fallthrough]];

        }
        default:
        {
            f(); g();
        }
    }
}

void switch_fallthrough_ill_formed(int n)
{
    switch(n)
    {
        case 1:
        {
            //nie poprawne ponieważ kompilator wykrywa że w przypadku n<=0 nie ma atrybutu fallthrough
            // nie istotny jest fakt że taka sytuacja nigdy nie może mieć miejsca
            f();
            if(n>0)
            {
                k();
                [[fallthrough]];
            }
        }
        case 2:
        {
            //niepoprawne ponieważ nigdy fallthrough nie jest bezpośrednio przed przejściem do następnego bloku
            k();
            for(int i=0; i<3; i++)
            {
                n--;
                [[fallthrough]];
            }
        }
        default:
        {
            k();
        }
    }
}

//nodiscard

//funckja kompilująca się bez błędu przy nie rozpatrzeniu zwracanej wartości (domyślne zachowanie kompilatora)
int function_discard_no_warning()
{
    return 0;
}

//funkcja kompilująca się z błędem, jeżeli nie rozpatrzymy zwracanej wartości
[[nodiscard]] int function_discard_warning()
{
    return 0;
}

//musimy rozpatrzyć wartość zwracaną przez każdą funkcje która zwraca ten typ aby skompilować program bez błędu
struct [[nodiscard]] server_warning
{
    int a;
};


server_warning start()
{
    server_warning s = {1};
    return s;
}


int main()
{
    //maybe_unused
    structur_unused_no_warning s_no_warning;
    structur_unused_warning s_warning;

    unused_variable_warning();
    unused_variable_no_warning();

    //fallthrough

    //nodiscard
    function_discard_no_warning();
    function_discard_warning();

    start();

    return 0;
}
