Propozycje do standardu kodowania w C++

1. Zapotrzebowanie na propozycje.

W tej chwili potrzebne są przede wszystkim propozycje dotyczące konwencji nazewniczych oraz formatowania kodu (klamry, wcięcia itp.).


2. Propozycje aktualne.

Wskazówka: Zasady wcięć tabulacyjnych

dziedzina: Estetyka kodu

Wcięcia tabulacyjne powinno się stosować hierarchicznie. Gdy rozpoczynamy młodszy hierarchią blok kodu, powinien być on zawsze proporcjonalnie starszy wcięciami tabulacyjnymi od poprzedzającego bloku/elementu kodu. Powyższe zasady najlepiej przedstawi przykład:

#include <stdio.h>
#include <stdlib.h>
 
// ^ Każde includy, #defin'y są hierarchicznie najstarsze Nie stosujemy wcięć
 
int main()  //Funkcja główna jest hierarchicznie równa z powyższymi elementami
{
    int i; //Zmienne są hierarchicznie młodsze, co owocuje jednym wcięciem
    int j;    
 
    for (i=0;i<15000;i++) //jak widać, for stoi na równi z deklaracją zmiennych, a kolejne bloki są wcięte o kolejne tabulacje.
        {
            printf("%i przyczyna, dla której algorytm plecakowy jest po prostu lepszy.\n",i);
        }
 
    return 0;
}

I zapętlamy. Czyli im młodziej wchodzimy w hierarchię, tym głębiej wcinamy. Każdy poziom przedstawiamy za pomocą jednej tabulacji.

komentarz jabola:
Według mnie wygodniejsze jest czytanie kodu, w którym klamry pętli (warunku, funkcji) i podobnych konstrukcji są w tym samym pionie co sama deklaracja pętli (warunku, funkcji). Jednoznacznie określa to które klamry określają ciało której pętli.
Innymi słowy signore Arkadio, funkcja main bardzo mi się podoba, ale to co zrobiłeś w forze nie za bardzo.

przypis Arunia :P :
Ta jest, masz rację. Skupiłem się za bardzo na blokach i popełniłem błąd na klamerkach. Jak słusznie zauważyłeś, klamerki mają być tak samo, jak w mainie. W każdym razie dzięki za poprawkę. Oczywiście zgadzam się z Tobą :)

int main()  //Funkcja główna jest hierarchicznie równa z powyższymi elementami
{
    int i; //Zmienne są hierarchicznie młodsze, co owocuje jednym wcięciem
    int j;    
 
    for (i=0;i<15000;i++) //jak widać, for stoi na równi z deklaracją zmiennych, a kolejne bloki są wcięte o kolejne tabulacje.
    { //tylko tu jest istotna różnica, ponieważ same klamry nie stanowią "kolejnego bloku"
        printf("%i przyczyna, dla której algorytm plecakowy jest po prostu lepszy.\n",i);
    }
 
    return 0;
}

komentarz astrala:
Z tego co widziałem, bywają też różne podejścia do słówek public, protected i private w ciałach klas. Mój sposób pisania można zauważyć w jednej z propozycji poniżej, chociaż niektórzy (co mnie trochę zdziwiło) traktują te słowa w specjalny sposób i piszą tak:

class MojaKlasa
{
public:
    //składniki publiczne
protected:
    //składniki chronione
private:
    //składniki prywatne
};

zatem słowa public, protected i private nie są w ogóle wcinane. Z tego powodu, że istnieją tu rozbieżności, prosiłbym o uzupełnienie propozycji o przykładową deklarację jakiejś klasy.

omikron:
Powinniśmy stosować wcięcia konsekwentnie dla wszystkich elementów. Czyli powyższa klasa wyglądałaby tak:

class MojaKlasa
{
    public:
        int metodaPublic(void);
    protected:
        void metodaProtected(int);
    private:
        int metodaPrivate(int);
};

Większa czytelność kosztem trochę dłuższych linii. Wybieram bramkę numer 1 ;>

magicrc:
Hmm, też muszę tutaj wtrącić swoje 3 grosze ;P i sprobować przekonać szanownych kolegów do JCC (Java Code Convention) odnośnie klamer bloków kodu. Proponuje klamrę otwierającą blok kodu umieszczać w tej samej lini co ewentualne słowo kluczowe w odległości jednej spacji, np tak:

//zmodyfikowany kod archiosa
int main() { 
    int i; 
    int j;    
 
    for (i = 0; i < 15000; i++) {
        printf("%i przyczyna, dla której algorytm plecakowy jest po prostu lepszy.\n",i);
    }
    return 0;
}

Jak widać klamra zamykająca pozostaje bez zmian. Btw. już słysze sprzeciw astrala :P
Na priv prośbę astrala podaje także przykładowe 'oklamrowanie' klas

class JakasTamKlasa {                              // normalna klasa
    int jakiesWaznePole;
 
    class KlasaWewnetrzna {                        // klasa wewnętrzna
        KlasaWewnetrzna metoda() {
            return new KlasaWewnetrzna {           // klasa anonimowa
 
            }
        }
    }
}

jaki widać zawsze jest tak samo SPACE i klamra otwierająca

astral:
magicrc, z Twojego komentarza wynika, że miałeś próbować nas przekonać do JCC. Zatem do dzieła:).

magicrc:
przygotwania do zwięzłej prezentacji JCC trwają ;], zbieram siły bo jest dużo do ogarnięcia ;P

jabol:
najlepiej byłoby posłuchać opinii wszystkich, ja na przykład sądzę, że lepszym rozwiązaniem jest stawianie klamry linijkę niżej z tego względu, że wtedy zawsze wiadomo, która klamra jest parą dla której, jeżeli napiszemy klamrę otwierającą za funkcją to wprawdzie jest pewien porządek, bo funkcja jest w tym samym pionie co klamra zamykająca, ale klamrę otwierającą na początku nie zawsze się widzi jeżeli jest ona na końcu linijki, gdzieś po funkcji o długiej nazwie i wielu parametrach.

Archios:
Mimo, że kod magic'a niejednokrotnie miałem przyjemność czytać i jest on wg mnie zawsze jansny jak słońce, to pozostanę przy propozycji klamerki niżej. Zgodzę się z komentarzem jabola. Klamerka niżej poprawia czytelność. Ja np gubię się, gdy otwierająca klamra jest na koniec linijki. Poza tym stosowanie klamerki niżej pozwala na czyste "podświetlenie" obu klamr (zamykającej i otwierającej) w wielu edytorach wspomagających kodowanie.


Wskazówka (ważna): Wcięcia powinno się zachowywać niezależnie od stosowanego edytora

dziedzina: Estetyka kodu

Wcięcia w funkcjach, instrukcjach warunkowych, pętlach itd. powinno się stosować aby poprawić czytelność kodu. Niezależnie od stosowanego edytora wcięcia powinny być na każdym poziomie równe i powinny różnić się między poziomami o to samo wcięcie. Zaleca się stosowanie głównie wcięć znakiem tabulacji.

magicrc:
Wydaje mi, że końcowa (releaseowa ;>) postać kodu zamiast tabulacji powinna mieć spacje, np. 1 x TAB = 4 x SPACE. Powodem tego jest to, że nie ma jednego standardu interpretowania tabulacji przez środowiska programistyczne i inne dziwne edytory ;P


Wskazówka: Standard można złamać, jeśli są ku temu naprawdę silne powody.

dziedzina: Ustalenia wstępne

Nie jesteśmy niewolnikami standardu - standard promuje rozwiązania, które są zazwyczaj słuszne, zazwyczaj poprawiają czytelność i zazwyczaj są bezpieczniejsze. Jeśli ktoś uważa, że w którymś momencie standard przeszkadza mu osiągnąć któryś z tych celów - może go złamać, jednak dobrze jest wtedy zamieścić komentarz wyjaśniający, że jest to świadome i uzasadnione działanie.


Reguła: Wszystkie nazwy będą w języku angielskim.

dziedzina: Konwencje nazewnicze.

Język angielski jest standardem w kodowaniu międzynarodowym. Poza tym zastosowanie angielskich nazw ułatwia np. przedstawienie swojego problemu na zagranicznym forum, gdy zamieszczenie fragmentu kodu pozwoliłoby innym lepiej zrozumieć nasz problem.


Reguła: Nazwa zmiennej referencyjnej będzie zakończona suffiksem Ref.

dziedzina: Konwencje nazewnicze.

Wspomaga czytelność - wystarczy spojrzeć na kod, żeby wiedzieć, która zmienna jest referencją.
Przykłady: reorganizerRef, dispatcherRef, accessObjectRef.


Reguła: Nazwa zmiennej wskaźnikowej będzie zakończona suffiksem Ptr.

dziedzina: Konwencje nazewnicze.

Wspomaga czytelność - wystarczy spojrzeć na kod, żeby wiedzieć, która zmienna jest wskaźnikiem.
Przykłady: observerPtr, currentDataPtr, mementoPtr.


Reguła: Zmienne iteracyjne będą oznaczane i, j, k

dziedzina: Konwencje nazewnicze.

Jest to najczęstsza praktyka stosowana w kodowaniu w C++.


Wskazówka Reguła: Zalecany styl komentowania to wspólny styl Doxygena i Javadoc. Pliki będą komentowane w wspólnym stylu Doxygena i Javadoc.

dziedzina: Komentarze

Ten styl komentowania umożliwia generowanie gotowej dokumentacji z kodu źródłowego. Doxygen obsługuje kilka styli komentowania, ale powinien być stosowany ten zgodny z Javadoc, ponieważ będzie można stosować te same przyzwyczajenia w komentowaniu zarówno do kodu C++ jak i Javy.

Więcej informacji:
http://www.stack.nl/~dimitri/doxygen/manual.html
http://java.sun.com/j2se/javadoc/writingdoccomments/index.html


Wskazówka: Nie staraj się pisać zbyt "sprytnie".

dziedzina: Styl kodowania.

Kod powinien być tak napisany, żeby ktoś mógł zrozumieć co on robi, po prostu go czytając. Jak to ktoś gdzieś napisał (TODO: znaleźć kto i gdzie): "Nie jest sztuką napisać kod zrozumiały dla maszyny. Prawdziwa sztuka to pisać kod zrozumiały dla człowieka".

komentarz jabola:
nie zgadzam sie, czasem lepiej napisac sprytnie i odpowiednio skomentowac niz pisac jak pierwszy lepszy idiota i robic 10-krotnie wieksze pliki
komentarz astrala:
Choćbym nie wiem jak się wysilał, nie widzę przewagi krótszego kodu nad czytelniejszym kodem. Krótszy kod nie daje żadnych korzyści, natomiast czytelniejszy kod daje ich całe mnóstwo. Jeśli rozmiar kodu to jedyny argument, to nawet nie mamy o czym rozmawiać:). Jeszcze jedno - piszmy jak idioci - wtedy nawet idioci nas zrozumieją;).
komentarz jabola:
dobra, zrobilem za duzy skrot myslowy, nie chodzilo mi stricte o wielkosc kodu, czy rozmiary plikow, wiemy przeciez ze czesto programistom placi sie od kLOCa, chodzi mi o to ze czasem pewne rzeczy mozna zastapic konstrukcja o wiele prostsza, szybsza w wykonaniu, wydajniejsza i nie wprowadzajaca dodatkowych bledow, chociaz nie kazdy musi od razu zrozumiec "co autor mial przez to na mysli".
komentarz astrala:
Moim (i wielu innych - poczytaj na necie) zdaniem zbyt sprytny kod jest dużo bardziej podatny na błędy, choćby z tego powodu, że nie każdy od razu rozumie, co autor miał na myśli. Tak czy inaczej jest to propozycja wskazówki a nie reguły - chyba nie ma co sie gorączkować.


Reguła: Kiedy tylko się da, pliki nagłówkowe powinny być włączane w plikach źródłowych, a nie w nagłówkowych.

dziedzina: ???

dopisać uzasadnienie


3. Propozycje zawieszone (czekające na lepsze czasy).

Reguła: W C++ 0 będzie stosowane zamiast NULL.

dziedzina: Elementy języka -> wskaźniki.

W różnych implementacjach bibliotek NULL jest różnie definiowany. Nie jest to problemem w języku C, natomiast w jezyku C++, który ma silniejszą kontrolę typów, może to doprowadzić do wygenerowania przez kompilator ostrzeżenia. Biorąc pod uwagę, że dobrym zwyczajem pod koniec (lub nawet w trakcie) tworzenia programu jest czyszczenie wszystkich ostrzeżeń, konieczne byłoby zrobienie wyjątku dla NULLa, co mogłoby doprowadzić do syndromu "wybitej szyby". Dlatego lepiej stosować 0, a czytelność uzyskiwać poprzez specjalne oznaczanie zmiennych wskaźnikowych.

komentarz jabola:
nie sadze aby taka zasada wprowadzila wiele dobrego, ze wzgledu na wprowadzenie w tym semestrze takze nauczania javy, w ktorej istnieje roznica miedzy 0 a NULL

komentarz berhalaka:
przeciez w c++ NULL to jest 0, i zadne bibilioteki c++ tego nie przedefiniuja, jedynie naglowki z c moga bo tam jest (void*)0 (ale i tak wiekszosc makrem ladnie to opakowuje zeby bylo dla c++ 0)


Reguła: Każdy tworzony wskaźnik będzie inicjowany wartością 0.

dziedzina: Elementy języka -> wskaźniki.

Inicjowanie wskaźników watością 0 umożliwia bardzo łatwe sprawdzenie, czy wskaźnik wskazuje na zaalokowany obszar pamięci, czy też nie. Zachowanie tej praktyki można zapewnić, tworząc klasę opakowującą wskaźnik, która przy tworzeniu i zwalnianiu wskazywanego obszaru będzie nadawać mu wartość 0.

komentarz jabola:
odnosnie powyzszego: wartoscia NULL


4. Propozycje odrzucone.

Reguła: Każdy wskaźnik będzie inicjowany wartością 666 na cześć szatana.

dziedzina: wskaźniki
Pompuj rower dla szatana.

O ile nie zaznaczono inaczej, treść tej strony objęta jest licencją Creative Commons Attribution-Share Alike 2.5 License.