🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: pl.comp.programming
1 messages
1 total messages Started by Jacek Marcin Jaworski Mon, 05 Aug 2024 19:26
Tworzenie Programów Nieuprzywilejowanych Opartych Na Wtyczkach
#217913
Author: Jacek Marcin Jaworski
Date: Mon, 05 Aug 2024 19:26
1848 lines
67702 bytes
    Tworzenie Programów Nieuprzywilejowanych Opartych Na Wtyczkach

                                                             autor:

    [1]Jacek Marcin Jaworski

                                                         pseudonim:

    [2]Energo Koder Atlant

                                                         utworzono:

    2023-01-27

                                                           miejsce:

    Pruszcz Gd.

                                               wersja: 2308 z dnia:

    2024-08-05

                                                          sys. op.:

    Linux Triskel, Kubuntu

                                                             prog.:

    Libre Office

    Spis treści

    [3]Wprowadzenie 2

    [4]1 Narzędzia programisty 2

    [5]2 Standardowa proc. tworzenia prog. opartego na wtyczkach 3

    [6]3 Analiza zstępująca (funkcjonalna) 3

    [7]4 Analiza wstępująca (techniczna) 4

    [8]5 Arch. prog. 4

    [9]5.1  Arch.  typowego  prog. nieuprzywilejowanego opartego na
    wtyczkach 4

    [10]5.2 Arch. kat. ze źródłami 5

    [11]5.2.1 Model asemblerowy i model C 5

    [12]5.2.2 Główne kat. z kodami żródłowymi 5

    [13]5.2.3 Kat. bibl. narzędziowej 5

    [14]5.2.4 Kat. prog. 5

    [15]5.2.5 Kat. ppr 5

    [16]5.2.6 Kat. wtyczki 6

    [17]5.2.7 Kat. testy-aut 6

    [18]5.3 Arch. pliku z kodem 6

    [19]5.3.1 Sekwencyjna 6

    [20]5.3.2 Funkcyjna 7

    [21]5.3.3 Proceduralna 7

    [22]5.3.4 Obiekty w proceduralnym j. C 8

    [23]5.3.5 Obiektowa 9

    [24]5.4 Wtyczki w proj. 10

    [25]5.5 Prog. konsolowe wywoływane przez prog. 10

    [26]5.6 Przenośność programu 10

    [27]6 Algorytmy w prog. 10

    [28]6.1 Logika w programie 10

    [29]6.2 Kontrola stanu Logiki 11

    [30]6.3 Spr. niezmienników 11

    [31]6.4 Obsługa wyjątków w Logice 11

    [32]6.5 Sposób interakcji z prog. 12

    [33]6.6 Sposób przetwarzania danych w prog. 12

    [34]6.6.1 Przetwarzanie strumieniowe danych tekstowych 12

    [35]6.6.2 Praca z plikami 13

    [36]6.6.3 Przetwarzanie bazodanowe 13

    [37]6.6.4 Przetwarzanie sieciowe 13

    [38]6.6.5 Przetwarzanie wielowątkowe 13

    [39]6.7 Sposób wykrywania błędów 13

    [40]7 Styl prog. 14

    [41]7.1 Izolacja proj. od świata zewnętrznego 14

    [42]7.2    Projektowanie   i   kodowanie   wg   optymistycznego
    scenariusza 14

    [43]7.3 Sposób prezentacji wyników 15

    [44]7.4 Sposób zgłaszania błędów 15

    [45]7.4.1 Wartość zwracana z f. 16

    [46]7.4.2 errno 16

    [47]7.4.3 Wyjątki 17

    [48]7.5 Komentarze 18

    [49]Dodatek 1: Mały sabotaż 19

    [50]Dodatek 2. Wzorce proj. - jak na nie patrzeć? 19

    [51]Wzorce 19

    [52]Antywzorce 20

    [53]Dodatek 2: Zasady używania baz danych SQL 20

    Wprowadzenie

    W informie najważniejsze są 3 sprawy: arch., algorytmy i styl
    programów. Reszta to blichtr.

    W programowaniu najważniejsze są 2 sprawy: programy i skrypty:

    Programy są to języki kompilowane: Asembler, Fortran, C, C++,
    D, Paskal, Delfi, Rust.

    Skrypty są to języki interpretowane: Pyton, Jawa, Jawa Skrypt,
    PHP, C#, Perl. Specjalną odmianą skryptów są powłoki: Bash, sh,
    zsh, csh, command.com, PowerShell.

    Prog. koduję gdy mają one krytyczne znaczenie (albo dla mnie,
    albo dla klienta).

    Skrypty piszę, gdy nie ma znaczenia ani szybkość wykonania ani
    jakość kodu. W praktyce skrypty piszę by ułatwić sobie życie i
    przyspieszyć codzienną pracę.

    Są  tylko  3  rodzaje  programów:  prog.  główny,  programy
    uprzywilejowane i programy nieuprzywilejowane:

    Program główny to rdzeń każdego systemu operacyjnego: zarządza
    on  procesami, wątkami, pamięcią i urządzeniami. Robi to za
    pomocą sterowników;

    Programy uprzywilejowane to głównie demony: demony dzieją się
    na sieciowe i lokalne.

    Demony sieciowe świadczą usługi sieciowe. Ale nie oznacza to,
    że tylko zdalne komputery mogą z nich korzystać. Wiele systemów
    składa się z demonów sieciowych współdziałających z programem
    nieuprzywilejowanym na tym samym komputerze.

    Demony   lokalne   sterują   określonymi  elementami  systemu
    operacyjnego. Demony lokalne to faktycznie wydzielone elementy,
    które   powinny  być  w  programie  głównym,  ale  są  dla
    bezpieczeństwa lub po prostu dla wygody poza nim;

    Program główny i programy uprzywilejowane działają w trybie
    uprzywilejowanym, czyli mają pełne uprawnienia (mówi się, że
    one z komputerem mogą zrobić wszystko).

    Programy   nieuprzywilejowane,   to   programy  używane  przez
    użytkownika:   są   to   polecenia   konsoli,  oraz  programy
    interaktywne działające w trybie tekstowym lub graficznym.

    Programy nieuprzywilejowane działają bez specjalnych uprawnień.
    Jednak nawet te programy mogą być szkodliwe bo standardowo mają
    pełen dostęp do katalogu domowego oraz standardowo mają pełen
    dostęp do globalnej sieci Internet. Skutek tego może być taki,
    że   ściągnięty  z  sieci  programik  po  uruchomieniu  bez
    najmniejszego problemu może wesoło wysyłać w świat listy plików
    lub  nawet całe pliki wybrane przez zdalnego operatora. Można
    się  przed  tym  zabezpieczyć  własnoręcznie  konfigurując
    piaskownicę i zaporę sieciową.

    W tej publikacji będę omawiać tylko architekturę programów
    nieuprzywilejowanych.  Bo  od  programowania  takich "zwykłych
    programów" należy zaczynać karierę programisty.

    Tak jak nie ma jednego przepisu na świetny budynek, tak nie ma
    jednego   przepisu   na   doskonały  prog..  Arch.  programów
    komputerowych  to wieloaspektowy temat, który postaram się tu
    zarysować.

    ZASADY PODAWANE W SZKOLE CZY NA UCZELNI UMOŻLIWIAJĄ ZALICZENIA
    PRZEDMIOTÓW, ALE NIE ZASTĄPIĄ MYŚLENIA W ŻYCIU, BO SĄ JEDYNIE
    WSKAZÓWKĄ JAK RADZIĆ SOBIE Z PROBLEMAMI.

    Podobnie należy myśleć o zasadach tutaj opisanych.

                         1Narzędzia programisty

    Podst. narzędzia programisty, to:
     1. Dok.:  plan  proj.,  dok.  funkcjonalna,  dok.  techniczna,
        sprawozdanie  z  wykonania prototypu, dok. użytych prog. i
        bibl.,     sprawozdanie     z    podsumowaniem    wykonania
        wcześniejszych proj. (oraz wcześniejszych wydań w ramach
        bieżącego proj.);
     2. Polecenia  konsolowe do przetwarzania tekstu oparte na wyr.
        reg. to podstawa skryptów jakie na co dzień trzeba tworzyć
        by pchać proj. na przód.
     3. Kod:  prog.,  bibl.  proj.,  firmowe  i  kupione,  wtyczki,
        konwertery, testy aut.;
     4. Pliki z zasobami prog., dane testowe;
     5. Edytor programisty (ulubiony);
     6. Debuger (do uruchamiania ze śledzeniem);
     7. Profiler  (do  wykrywania  wycieków  pam.  i do wykrywania
        nadmiarowych wywołań f.).

    Zasada  Inżynierska:  Należy dostosowywać sobie narzędzia z
    dostępnym kodem źródłowym.

    Zasada Inżynierska: Wydajność zwiększa się przez automatyzację
    a nie przez pośpiech.

         2Standardowa proc. tworzenia prog. opartego na wtyczkach

    Przygotowania:
     1. Plan realizacji proj.;
     2. Szkolenie zespołu[54]^1 (ze sprawozdaniem);
     3. Dok. funkcjonalna proj. (wynik analizy zstępującej);
     4. Dok. techniczna (wynik analizy wstępującej);
     5. Prototyp (ze sprawozdaniem);
        Prototyp  to  luksus jaki powoduje skok świadomościowy ze
        sfery domniemywań do sfery wiedzy realnej na temat problemu
        i proj.
     6. Korekta dok.

    Realizacja:
     7. Kodowanie konwerterów danych i ich testów aut.;
     8. Kodowanie bibl. proj. i ich testów aut.;
     9. Kodowanie logiki prog.;
    10. Kodowanie okien prog. i ich testy manualne;
    11. Kodowanie  PPR (Publiczny Pakiet Rozwojowy, w j. ang. SDK):
        Powinien on się składać wył. z kl. abs.
        Problemem wtedy są kl. pochodne od QObject (bo one nie mogą
        być abs.). Jest na to proste obejście: wystarczy utworzyć
        kl. pochodną od QObject (lub jej pochodnej) i umieścić w
        niej wymagane dodatkowe f. wirt. - ale puste. Natomiast ich
        implementacja będzie dopiero w kol. kl. dziedziczącej, ale
        umieszczonej  nie  w PPR, tylko w kodzie prog. (o. tych kl.
        będą param. wtyczek z interfejsem z PPR).
    12. Kodowanie wtyczek z interfejsem PPR;
    13. Pods. proj.

                   3Analiza zstępująca (funkcjonalna)

    Analiza zstępująca mówi co ma robić prog.:
     1. Na jakim sprzęcie i pod jakim sys. op. będzie pracował;
     2. Jakie dane będzie przyjmował;
     3. Jakie dane będzie zwracał;
     4. Z czym i jak będzie współpracował;
     5. jakie będą zasady jego poprawnego użycia;
     6. Jakie będą jego najważniejsze opcje;
     7. Jakie będą jego najważniejsze funkcje.
     8. Diagramy przypadków użycia (dostarcza je klient[55]^2);
     9. Diagramy sekwencji (dostarcza je klient);
    10. Makiety  wszystkich  okien  programu  z  opisem  działania
        (dostarcza je projektant).

    Analiza zstępująca nic nie mówi o algorytmach ani implementacji
    programu.

    Wynikiem analizy zstępującej jest "dokumentacja funkcjonalna".

                    4Analiza wstępująca (techniczna)

    Analiza wstępująca mówi jak wewnętrznie ma działać prog.:
     1. Dane wej. (formaty i położenie);
     2. Alg. przetwarzania danych w prog.;
     3. Dane wyj. (formaty i położenie);
     4. Definiuje jakie będą użyte protokoły komunikacyjne;
     5. Definiuje jakie mają być wtyczki (API i położenie);
     6. Definiuje wymagane kl. narzędziowe;
     7. Definiuje kl. logiki prog. i ich maszyny stanów;
     8. Wskazuje jakie mają być użyte wzorce proj.;
     9. Definiuje jak będą wyglądać testy;
    10. Definiuje jaki ma być poziom bezp. i jak go osiągnąć.

    Wynikiem analizy wstępującej jest "dokumentacja techniczna".

                               5Arch. prog.

5.1Arch. typowego prog. nieuprzywilejowanego opartego na wtyczkach

    Aplikacja zawiera kl. narzędzi, logiki i okien, natomiast bibl.
    zawierają  jedynie  dodatkowe kl. narzędzi. Ta arch. została
    wymyślona    przeze    mnie    i    nazwałem    ją   wzorcem
    Narzędzia-Logika-Okna.   Tym   wzorcem   zastępuję  bardziej
    specjalistyczny wzorzec Model-Widok-Kontroler (w j. ang. MVC).

    Na obrazku są strzałki jednokierunkowe. Obrazuje to zasadę, że
    kod z wyższych warstw wywołuje kod z warstw niższych. Natomiast
    warstwy niższe nie wywołują kodu warstw wyższych. Jednak czasem
    warstwa wyższa musi zaczekać na coś co ma dostarczyć warstwa
    niższa. W tym celu korzysta się z mechanizmu f. zwrotnej (w j.
    ang.  callback).  Polega to na tym, że po prostu jawnie podaje
    się  wsk. do f. jaki ma być wywołany gdy w warstwie niższej
    pojawią się dane.

    F.  zwrotna  będzie  wywoływana  przez inny wątek. Dlatego w
    wywołaniach  zwrotnych  konieczna  jest  sych.  muteksami  lub
    semaforami.

    W proj. mamy 3 rodzaje bibl.: dostawcy, firmowe i proj.:
     1. Bibl.  dostawcy:  to  bibl. jakie dostajemy z sys. op. oraz
        jakie kupuje firma;

    Jakość  bibl. dostawcy są odbiciem jego kultury technicznej,
    ekonomicznej i osobistej jego kraju.
     2. Bibl. firmowe: to bibl. jakie zawierają narzędzia własne
        firmy.

    Jakość  bibl.  firmowych  jest  odbiciem kultury technicznej,
    ekonomicznej i osobistej twojej firmy (są to narzędzia własne
    firmy).
    Bibl. firmowe mają też separować proj. od świata zewnętrznego.
        Oto powody:

     1. Rozwijanie   bibl.   firmowych   jest  konieczne  z  powodu
        konieczności  używania  darmowych bibl. czyli gratisów z
        SZAP. A gratis ma za zadanie psuć umysł naiwnego pasożyta.
     2. Otoczenie firmy jest niestabilne - nawet gdy firma płaci za
        narzędzia  programisty.  Dlatego te bibl. przede wszystkim
        przezywają  wszystkie używane w firmie typy danych w celu
        ew. ich podmiany, rozszerzania lub przedefiniowania;
     3. Przestarzałe  zew. bibl. w j. C trzeba opakowywać kl. C++
        tak by można było ich normalnie używać.



    Bibl. proj.: Te bibl. wyodrębnia się by w przyszłości przesunąć
    je do bibl. firmowych.

5.2Arch. kat. ze źródłami

   5.2.1Model asemblerowy i model C

    Obecnie w j. prog. stosuje się 2 modele org. kodu w kat. proj.:
     1. Model  asemblerowy  występuje  w  j.  Asembler i D (oraz w
        skryptach,  ale  nimi  tu  się  nie zajmuję). Jego cechą
        charakterystyczną jest jedność i nierozerwalność deklaracji
        f.  i  jej definicji. To powoduje, że konieczne w nim jest
        udogodnienie w postaci widoczności f. w całym pliku. Z tego
        powodu:

    W  modelu  asemblerowym  wywołanie  f.  może  poprzedzić jej
    deklarację-definicję.  Mimo,  że  jest  to niedopuszczalne z
    logicznego p. widzenia.
     2. Model  C:  Występuje  tylko w j. C i C++. Jego cechą jest
        możliwość rozdzielenia deklaracji i definicji f.: można
        wydzielać  pliki  nagłówkowe  z  deklaracjami f. i pliki
        źródłowe z definicjami f. Użycie f. nie może poprzedzić jej
        deklaracji.

    Powrót w j. D do modelu asemblerowego oznacza brak możliwości
    stosowania tego j. do dużych proj. Wynika to po prostu z braku
    plików  nagłówkowych, co oznacza brak możliwości szybkiego
    wglądu w zawartość plików źródłowych.

    W obu sposobach organizacji kodu istnieje możliwość tworzenia
    f.  wstawianych.  W  j.  Asembler,  C i C++ można je wstawiać
    makrami. Dodatkowo w C, C++ i D wybrane f. można oznaczać jako
    inline  (jednak mimo takiej dyrektywy kompilator wcale nie musi
    wstawić  kodu tej f. w miejscu wywołania – powody tego typu
    zachowania są nieznane).

   5.2.2Główne kat. z kodami żródłowymi

     1. Kod prog.;
     2. PPR: „Pub. Pakiet Rozwojowy” (w j. ang. SDK);
     3. Kod wtyczek prog.;
     4. Skrypty;
     5. Testy aut.

   5.2.3Kat. bibl. narzędziowej

NazwaBibl/CMakeLists.txt
NazwaBibl/h++/NazwaBibl
NazwaBibl/c++
NazwaBibl/zasoby

   5.2.4Kat. prog.

NazwaProg/CMakeLists.txt
NazwaProg/Logika
NazwaProg/Narzedzia
NazwaProg/Okna
Zasoby

   5.2.5Kat. ppr

ppr/wspolne/kontrolki
ppr/wspolne/logika
ppr/wspolne/narzedzia
ppr/wspolne/wspolne.h++
ppr/wtyczki/InterAbsWtyczki1.h++

   5.2.6Kat. wtyczki

wtyczki/NazwaWtyczki1/CmakeLists.txt
wtyczki/NazwaWtyczki1/c++
wtyczki/NazwaWtyczki1/zasoby

   5.2.7Kat. testy-aut

testy-aut/c++
testy-aut/CmakeLists.txt
testy-aut/dane-testowe

5.3Arch. pliku z kodem

    Nowsze j. prog. zwykle wprowadzają nowy styl programowania, ale
    często pozwalają też programować w starszych stylach. Jednak w
    starszych  j.  prog. trudno uzyskać efekty jakie wprowadzono w
    nowych - ogromny wysiłek jaki jest do tego konieczny całkowicie
    niweczy  zysk.  Dlatego nonsensem jest kodowanie w j. C w stylu
    C++.

   5.3.1Sekwencyjna

    Wszystkie j. prog. umożliwiają programowanie sekwencyjne.

    W  arch.  sekwencyjnej  kolejne  linie  prog.  są wykonywane w
    obrębie jednej f.; po kolei z góry na dół; od pierwszej linii
    do ostatniej. Skoki wykonuje się wyłącznie w obrębie lokalnych
    pętli. Tak zaczyna się nauka programowania.

    Przykład prog.[56]^3 sekwencyjnego w j. Asembler[57]^4:
; Obliczenie N-tej wart. ciągu Fibonaciego:
format ELF64 executable
entry main

include 'import64.inc'
interpreter '/lib64/ld-linux-x86-64.so.2'
needed 'libc.so.6'
import printf,exit

segment readable executable
main:
    push rcx
    push rdx
    mov rcx, [gIloscIteracji]

; Obl. wart. ciągu Fibonaciego:
; Param rcx: nr wyr. ciągu.
; Wynik rdx: szukana wart.
    cmp rcx, 1
    jne f1
    mov rdx, 1
    ret
f1:
    cmp rcx, 2
    jne f2
    mov rdx, 2
    ret
f2:
    mov rax, 1
    mov rbx, 1
    dec rcx
    dec rcx
f3:
    mov rdx, rax
    add rdx, rbx
    mov rax, rbx
    mov rbx, rdx
    dec rcx
    jnz f3

; Drukowanie wyniku:
; Param rdx: wrart do wypisania na konsolę.
    xor rax, rax  ; Bez xmm.
    mov rdi, gWartCiaguFibonaciego
    mov rsi, [gIloscIteracji]
    call [printf]

    pop rdx
    pop rcx

; Wyj. z prog.
    xor rax, rax
    call [exit]

segment readable writable
    gIloscIteracji: dq 10
    gWartCiaguFibonaciego: db "Wynik po %d iteracjach wynosi: %d.", 10, 0

   5.3.2Funkcyjna

    J.  programowania funkcyjnego jest Fortran. Natomiast j. C, C++
    i D umożliwiają m. in. programowanie funkcyjne.

    W arch. funkcyjnej z pierwszej f. wyodrębnia się kolejne. Robi
    się  to  w  celu poprawy przejrzystości oraz w celu ponownego
    użycia   f.  Prowadzi  to  do  tworzenia  bibli.  (linkowanych
    statycznie lub dynamicznie).

    Cechą szczególną prog. funkcyjnego jest całkowita eliminacja
    zarządzania  stanem programu. Wynika to z faktu, że wszystkie
    dane są przekazywane jako param. f. i przez swoją kompletność
    stanowią  one  dokładne  określenie  stanu prog. Ten schemat
    powielają  niektóre  (lepsze?) protokoły sieciowe, np. HTTP,
    jednak niegdyś popularny FTP bazuje już na jawnych stanach po
    s. serwera i po s. klienta.

    W j. Fortran wszystkie zmienne alokuje się na stosie, czyli nic
    nie można zaalokować na stercie.

    Przykład prog. funkcyjnego w j. D:
// Obliczenie silni:
import std.stdio;
import std.conv;
import std.format;
import std.string;
import core.stdc.stdlib;

ulong wczytaj()
{
    writeln("Podaj nr wyrazu ciągu silni jaki chcesz poznać [zatw. klaw.
Enter]: ");
    return to!long(readln().strip());
}

ulong oblicz(ulong n)
{
    writeln(format("Obliczam %d wyr. silni.", n));
    ulong lWynik = 1;
    for(ulong i = 1; i <= n; ++i)
       lWynik *= i;
    return lWynik;
}

void drukuj(ulong s)
{
    writeln(format("Silnia wynosi: %d", s));
}

int main(string[] a)
{
    wczytaj().oblicz().drukuj();
    return EXIT_SUCCESS;
}

   5.3.3Proceduralna

    Klasycznymi  j. prog. proceduralnego są j. C i Paskal. Program
    podzielony  jest  na  f.,  a  jego dane są ujęte w struktury.
    Struktury  najczęściej są oparte o typy proste, czyli o typy
    wbudowane  w  j.  (rzadziej  struktury składają się z innych
    struktur).

    Arch.  proceduralna  jest  podobna  do  funkcyjnej,  jednak nie
    przekazuje się do f. kompletu danych definiujących stan prog.
    Zmienna określająca stan prog. może być zm. w strukturze logiki
    aplikacji lub może być po prostu zm. globalną prog.

    Częsty błąd początkujących programistów prog. proceduralnych i
    obiektowych to brak jawnej kontroli stanu w aplikacji (brak zm.
    typu  enum z aktualnym stanem), prowadzi to do porażki proj. w
    raz ze wzrostem jego komplikacji prog.

    Jawnej   kontroli   stanu   na   pewno   wymaga   programowanie
    wielowątkowe  i  sieciowe (wiele spotykanych prot. sieć. jest
    stanowych  mimo,  że  znacznie komplikuje to demony sieciowe i
    programy klienckie).

    Przykład prog. proceduralnego w j. C:
/* Przykład proceduralnego przetwarzania danych: */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Struktury:::::::::::::::::::::::::::::::::::::*/
struct Dane
{
    int cX, cY, cWysokosc;
    double cPaliwo;
    double cLadunek;
    /* Tu zmienne... */
};

/* Funkcje:::::::::::::::::::::::::::::::::::::::*/
struct Dane* wczytajDane()
{
    struct Dane* d = malloc(sizeof(struct Dane));
    memset(d, 0, sizeof(struct Dane));
    /* Tu kod... */
    return d;
}
int sprDane(struct Dane* d)
{
    /* Tu kod... */ return 0;
}
int oblicz(struct Dane* d)
{
    /* Tu kod... */ return 0;
}
int wypiszWyniki(struct Dane* d)
{
    /* Tu kod... */ return 0;
}
int zwolnijPamiecDanych(struct Dane* d)
{
    free(d);
    return 0;
}

int main(int n, char** w)
{
    /* [Pominięte dla czytelności:] Wczytywanie opcji:
          param. lini komend i plików ustawień. */

    struct Dane* lDane = wczytajDane();
    int lWynik = sprDane(lDane);

    if(!lWynik)
       lWynik = oblicz(lDane);

    if(!lWynik)
       lWynik = wypiszWyniki(lDane);

    zwolnijPamiecDanych(lDane);

    if(lWynik)
    {
       fprintf(stderr, "Wystąpił błąd! Nr: %d\n", lWynik);
       exit(lWynik);
    }

    printf("Wykonanie prawidłowe!\n");
    exit(EXIT_SUCCESS);
}

   5.3.4Obiekty w proceduralnym j. C

    W tym miejscu można podpowiedzieć kombinatorom trik pozwalający
    uzyskać efekt dziedziczenia struktur w j. C. Pomysł polega na
    kapsułkowaniu   rodzica   w   potomku.   Jest  to  uproszczone
    dziedziczenie jednobazowe. Działa to tylko dzięki temu, że w j.
    C odwzorowanie struktur w pam. komp. Jest dokładnie takie jak w
    deklaracji struktury – nic nie jest dodawane przez kompilator.
    Schemat  kapsułkowania  w  celu uzyskania efektu dziedziczenia
    pokazuje poniższy kod:
#include <stdio.h>
#include <stdlib.h>

struct StrukturaA
{
    char a;
    int b;
};

struct StrukturaB
{
    struct StrukturaA A;
    long long c;
};

int main(int n, char** p)
{
    struct StrukturaB b;
    b.A.a = 100;
    b.A.b = 101;
    b.c = 102;

    struct StrukturaA* a = (struct StrukturaA*) &b;

    printf("a.a=%d, a.b=%d, b.c=%lld\n", a->a, a->b, b.c);

    return EXIT_SUCCESS;
}

    Trzeba  jednak  pamiętać,  że  C++  pozwala  na  wielobazowe
    dziedziczenie (można dziedziczyć po wielu kl. jednocześnie) i
    wygodne f. wirt (w strukturach C można je emulować używając
    wsk. do f.).

    Ogólnie można powiedzieć, że wszystko co jest możliwe w j. C++
    jest też możliwe w j. C. Można dodać, że pierwsze kompilatory
    C++ były konwerterami kodu do j. C. Z tego co pamiętam tak było
    w przypadku kompilatora FSF GNU g++.

   5.3.5Obiektowa

    Klasycznymi  j.  prog.  obiektowego  są C++ (C z klasami), D i
    Delfi  (Paskal  z  klasami). Arch. obiektowa jest rozwinięciem
    arch. proceduralnej:

    Struktury, zwane klasami mogą mieć f.;

    Kl. czysto abstrakcyjne służą do definiowania interfejsów;

    Kl. można dziedziczyć w celu implementacji interfejsów lub w
    celu rozszerzania lub przedefiniowania zachowania starszych kl.

    W C++ każda kl. powinna być deklarowana w osobnym pliku *.h++ i
    implementowana w osobnym pliku *.c++.

    Uzasadnienie: Czytelność w sensie łatwości analizy proj.

    W odróżnieniu do struktur, kl. często składają się ze zmiennych
    innych klas.

    Przykład prog. obiektowego w j. C++:
// Przykład obiektowego przetwarzania danych:
#include <iostream>
#include <exception>

// Klasy:::::::::::::::::::::::::::::::::::::::::
class Dane                   // Kl. typu struktura.
{
public:
    int cX = 0, cY = 0, cWysokosc = 0;
    double cPaliwo = 0;
    double cPredkosc = 0;
    double cLadunek = 0;
    /* Tu zmienne... */

public:
    void wczytajDane() { /* Tu kod... */ }
};

class PrzetwarzanieDanych    // Kl. typu usługa.
{
public:
    PrzetwarzanieDanych(const Dane& d)
       : cDane(d) {}

public:
    void przetwarzaj()
    {
       sprDane();
       oblicz();
       wypiszWyniki();
    }

protected:
    void sprDane() { /* Tu kod... */ }
    void oblicz() { /* Tu kod... */ }
    void wypiszWyniki() { /* Tu kod... */ }

protected:
    const Dane& cDane;
};

// Funkcje:::::::::::::::::::::::::::::::::::::::
int main(int n, char** w)
{
    try
    {
       // [Pominięte dla czytelności:] Wczytywanie opcji:
       //    param. lini komend i plików ustawień.

       Dane d;
       d.wczytajDane();
       PrzetwarzanieDanych p(d);
       p.przetwarzaj();
    }
    catch(const std::exception& pWyjatek)
    {
       std::cerr << "Wystąpił wyjatek! Treść: "
          << pWyjatek.what() << std::endl;
       exit(EXIT_FAILURE);
    }

    std::cout << "Wykonanie prawidłowe!" << std::endl;
    exit(EXIT_SUCCESS);
}

5.4Wtyczki w proj.

    Rodzaje wtyczek w proj.:
     1. Wtyczki w bibl. narzędzi służą do obsługi różnych formatów
        plików lub do obsługi różnych protokołów;
     2. Wtyczki w Logice prog. służą do obsługi różnych alg. pracy.
        Wtyczki te reagują na zmianę stanu w Logice prog.;
     3. Wtyczki   w  oknach  prog.  standardowo  dodają  nowe  el.
        interfejsu co wzbogaca funkcjonalność prog.

    Zasady tworzenia wtyczek:
     1. Wtyczki w bibl. narzędziowych działają pojedynczo (np. do
        ładowania  pliku w danym formacie). Często te wtyczki są
        ładowane  aut.  na  podstawie  kontekstu  wynikającego  z
        bieżącej pracy prog., np. wybór pliku o określonym roz.;
     2. By użyć równolegle 2 wtyczek z bibl. narzędziowej konieczne
        jest utworzenie dwóch o. kl. narzędziowej. Tak jest np. w
        przypadku  otwarcia  2  różnych  plików  lub w przypadku
        jednoczesnej obsługi 2 różnych protokołów w jednym celu,
        np. do obsługi bazy danych.
     3. Należy jawnie zdefiniować kol. ładowania wtyczek. Ta kol.
        określa też kol. wywołań f. z tych wtyczek. Bez znajomości
        kol. wywołań też nie było by wiadomo co się dzieje w prog.
        Oznacza to, że:
     4. Wewnętrznie  wtyczka  powinna  działać  synchronicznie i
        powinna   powodować   synchroniczne   skutki  w  prog.  To
        zabezpiecza  przed  chaosem który powstaje gdy używa się
        darzeń do oddziaływania na prog.
        Jedyną  asynchroniczną  akcją  jaka  jest dopuszczalna w
        przypadku  wtyczek  to  zdarzenie  w  prog. które po kolei
        wyzwala wtyczki reagujące na tą akcję.

    Można dodać, że coś takiego jak plik json we wtyczkach Qt jest
    kompletnie  nie  na miejscu, bo on służy do określenia jakie
    wtyczki są wymagane przez daną wtyczkę.

5.5Prog. konsolowe wywoływane przez prog.

    Są powody by używać poleceń konsolowych w prog.:
     1. Są łatwiejsze do zrozumienia;
     2. Mają lepszą dokumentację;
     3. Są mniej kłopotliwe;
     4. Są bardziej żywotne;
     5. Łatwo je testować;
     6. Są  b.  szybkie gdy dłużej pracują niż wynosi czas ich
        uruchomienia.

5.6Przenośność programu

    Przenośność też wpływa na arch. prog. Sam kod C, C++ i D jest w
    pełni przenośny. Jednak trzeba go dopasowywać do różnych bibl.
    w różnych sys. op. Oto wskazówki jak dbać o przenośność kodu:
     1. Izolacja  kodu:  przezywanie  typów  i klas oraz tworzenie
        interfejsów   obiektowych  (wzorzec  proj.  konwerter)  do
        interfejsów w czystym C;
     2. Wydzielanie  plików *.uniks.c++, *.mak.c++ i *.okna.c++. W
        tych  plikach  należy  umieszczać wszystkie f. w których
        pojawia  się  nieprzenośny  kod.  Natomiast  skrypt CMake
        warunkowo włącza te pliki w zależności od sys. op. na jaki
        się kompiluje prog.

    Dyrektywy  warunkowe:  #ifdef  Q_OS_UNIX,  #ifdef  Q_OS_MACOS i
    #ifdef Q_OS_WIN są zakazane, bo robią jatkę w programie (a tym
    samym również w głowie programisty)!

                            6Algorytmy w prog.

6.1Logika w programie

    Logika w programie musi:
     1. Obsługiwać  stany  aplikacji  i  na ich podst. kontr. jej
        działanie;
     2. Ładować i zapisywać opcje (pliki INI, linia komend, okno z
        parametrami);
     3. Ładować wtyczki prog.;
     4. Tworzyć i niszczyć okna prog.;
     5. Spr. niezmienniki;
     6. Łapać i obsługiwać wyjątki (rzucane z logik i z narzędzi).

6.2Kontrola stanu Logiki

    Typowo stan prog. można kontrolować na 2 sposoby:
     1. Bezstanowo: ma dwie odmiany:
          1. Prog. bazujące na kontekście: w prostych sekwencyjnych
             prog. z niewielką liczbą zmiennych jest oczywiste co
             się dzieje. Dlatego "jakoś to działa" bez specjalnego
             myślenia o stanach prog.
          2. Prog.  funkcyjne:  Wywoływana  f.  dostaje  wszystkie
             potrzebne dane do jej działania (nie używa ona żadnych
             zmiennych globalnych, ani nie używa zm. kl. do której
             należy).
     2. Stanowo:  Maszyna  stanów  to  po  prostu  enum  z  jawnie
        zdefiniowaną listą możliwych stanów.

    Zasady projektowania maszyny stanów:
     1. Maszyny stanów należy projektować wspierając się programami
        do wizualizacji takimi jak dot z pakietu Graphiz.
     2. W enum nie należy używać heksów do definiowania podstanów.
        Podstany należy definiować jako odrębne maszyny stanów.

    Czyli zamiast:
enum Stan { oczekiwanie = 0, start=1, platnosc=2, wysyłanie=4, pytanieOP
odpis=8,  drukowanieParagonu=16, drukowaniePotwierdzenia=32, ...};

    należy zdefiniować 2 stany:
enum StanTermianala { oczekiwanie, start, platnosc, raportDobowy, ...};

enum StanPlatnosci { oczekiwanie, start, wysyłanie pytanieOPodpis, druko
wanieParagonu, drukowaniePotwierdzenia, ...};
     3. Należy  zakodować  f.  stanDoNapisu()  w calu wypisywania
        czytelnego komunikatu o próbie nielegalnej zmiany stanu (co
        oznacza awaryjne zamknięcie prog.).

    Działanie f. zmiany stanu:
     1. Spr. czy przejście do innego stanu jest dopuszczalne?
     2. Zmienia stan na nowy;
     3. Reaguje na zmianę stanu;
     4. Informuje wtyczki (i inne obiekty) o zmianie stanu.

6.3Spr. niezmienników

    Niezmienniki są to warunki jakie muszą zawsze być spełnione aby
    prog.  działał  prawidłowo.  Za prawidłowe działanie prog.
    odpowiada  jego  logika  więc  tylko  w  logice  należy  spr.
    niezmienniki. Niezmienniki należy spr. na końcu konstruktora i
    na końcu każdej f. publicznej z wyj. destruktora;

6.4Obsługa wyjątków w Logice

    Wyjątki należy łapać w prog. we wszystkich f. pub. kl. logiki.
    Wynika  to  z  faktu, że tylko na poziomie logiki wiadomo co z
    tymi wyjątkami robić.

    W  celu  automatyzacji typowych wyjątków które kończą się
    zamknięciem prog. należy zrobić makro w którym:
     1. Komunikat jest zapisywany do pliku z listą błędów (zawsze);
     2. Komunikat jest wypisywany na konsolę (zawsze);
     3. Komunikat  jest  wyświetlany  w  okienku gdy jest to prog.
        graf. (należy to spr. makrem takim jak #ifdef QT_GUI_LIB).

    Takie  makro  automatyzujące łapanie wyjątków możliwe jest
    jedynie w prog. (a nie w bibl). Jest to argument by logiki były
    tylko i wyłącznie w prog. (a nie w bibl.).

6.5Sposób interakcji z prog.

    Do podstawowych sposobów interakcji z programem należą:
     1. Zadania  wsadowe:  To  typowo  seria poleceń połączonych
        rurkami  w którym pierwsze polecenie przyjmuje dane wej. a
        ostatnie   wypisuje   wynik   na   konsolę.  Interakcja  z
        użytkownikiem  ogranicza  się  do  podania  danych  wej.,
        wywołania i odbierania wyniku w formie tekstowej.

    Prog.  zadań  wsadowych  jest  prostsze niż prog. oparte o f.
    getchar().
     2. F.  getchar():  To  f.  bibl.  j. C. Umożliwia ona prostą
        interakcję w terminalu polegającą na wywołaniu w pętli f.
        getchar()  w  celu  pobrania  znaku  lub  linii  tekstu  od
        użytkownika. Normalnie użycie f. getchar() wstrzymuje pracę
        programu na czas wpisywania znaków.

    Prog.  oparte  o f. getchar() jest prostsze niż prog. oparte o
    pętlę zdarzeń.
     3. Pętla zdarzeń: Szczególnie często jest stosowana w prog.
        graficznych zwanych "programami użytkowymi". Jej działanie
        polega  na monitorowaniu deskryptorów wybranych urządzeń
        (np.  klawiatury,  myszy,  karty  sieciowej,  itd.)  w celu
        pobrania   od  nich  danych.  Wtedy  następuje  propagacja
        zdarzenia  po obiektach mogących je odbierać. W przypadku
        myszy  będzie  to  kontrolka  interaktywna która zostanie
        aktywowana   (dostanie  ognisko  -  w  j.  ang.  focus).  W
        przypadku   klawiatury   będzie   to   aktywna   kontrolka
        interaktywna (mająca ognisko).

    Prog. oparte o pętlę zdarzeń jest prostsze niż prog. oparte o
    pętlę gry.
     4. Pętla gry: W pętli gry oblicza się położenie obiektów na
        ekranie   oraz   fizykę  wirtualnego  świata  (kolizje  i
        uszkodzenia). Mierzy się czas przerysowania każdej klatki w
        celu   przewidzenia   czasu  koniecznego  do  przerysowania
        kolejnej  klatki,  a  to  jest  konieczne by wyliczyć stan
        obiektów  w  grze  w  kolejnej klatce (o ile się ruszyły
        postacie i jakie zaszły zjawiska fizyczne).
        Pętla gry charakteryzuje systemy czasu rzeczywistego, które
        muszą być przewidywalne w sensie czasowym. Pętla zdarzeń,
        nie  spełnia  tych  wymagań, bo nikt nie gwarantuje kiedy
        zdarzenie będzie obsłużone.

    W  źle  napisanych  grach  nawet  wejście w menu oznacza 100%
    obciążenia procesora. Dlaczego tak jest? Bo w grach nawet menu
    jest  rysowane przez silnik gry. I jak komp. Jest zbyt wolny do
    danej gry, to menu też przycina i są slajdy.

    Mimo, że na sys. Linuks i Windows są biblioteki z pętlą gry to
    jednak  te  sys.  op.  nie  są  systemami czasu rzeczywistego.
    Dlatego gry na nich działają wolno (tak samo zresztą wszystkie
    inne  uruchamiane  na  nich  prog.).  Wynika to z faktu pisania
    wolnych  prog. - to dlatego od pocz. XXIw. komputery działają
    tak  samo  wolno.  Szybkie sys. op. i szybkie prog. koduje się
    SZAP w tzw. tajnych czarnych projektach.

    Częstym błędem początkujących programistów próbujących używać
    pętli zdarzeń lub z pętli gry jest sytuacja gdy kontrolka graf.
    jednocześnie służy do wyświetlania i wprowadzania danych. Wtedy
    jest ona jednocześnie kontrolowana przez użytkownika oraz przez
    urządzenie  współpracujące  z  programem - to jest sytuacja
    konfliktowa. Dlatego zawsze należy używać osobnych kontrolek do
    wyświetlania i do ustawiania wart. (ta druga może pojawiać się
    tylko  w razie potrzeby) - mimo, że jest to nieeleganckie, ale
    ma tą zaletę, że działa normalnie.

6.6Sposób przetwarzania danych w prog.

    Niezależnie od sposobu przetwarzania mam nast. zasady:
     1. Używam grup wyspecjalizowanych prog., które współdziałają
        ze sobą w celu realizacji naszych zad.;
     2. Prog.  narzędziowy  można  napisać  od  nowa gdy jest to
        opłacalne, czyli: jest to tanie, albo perspektywiczne, albo
        inaczej się nie da, albo jest to po prostu ciekawe;
     3. Nigdy  nie  tworzymy monstrualnych omnibusów, które mają
        zastąpić wszystkie inne prog. (syndrom Qt Creator).

   6.6.1Przetwarzanie strumieniowe danych tekstowych

    Pobiera się dane z stand. we. (lub z pliku) i przetwarza. Wynik
    zwykle wypisuje się na stand. wy. To przetwarzanie występuje w
    dwóch odmianach:
     1. Linijkowe: przetwarzanie odbywa się linia po linii;
     2. Blokowe: wczytuje się całość i przetwarza na raz (hurtem).
        Ten  model  stosuje  się  przy  wieloliniowych  wyr.  reg.
        Oczywiście, w porównaniu do modelu linijkowego, ten model
        ma dużo większe wymagania pamięciowe. Dlatego należy go
        stosować w ostateczności - najlepiej gdy użytkownik sam o
        tym zadecyduje wybierając taką opcję.

    Zwraca  uwagę  nacisk na przetwarzanie linijkowe w poleceniach
    konsolowych  sys.  Linuks GNU. Jednak jest to tylko uciążliwa
    konwencja, gdyż są polecenia które nie mogą jej wypełniać - np.
    sort - więc również sed i grep mogłyby mieć opcjonalny tryb
    przetwarzania  blokowego  (odpowiednik  multiline  z normalnych
    wyr. reg.).

   6.6.2Praca z plikami

    Są to wszelkiego rodzaju edytory plików w ściśle zdefiniowanych
    formatach: dźwiękowe, obrazy 2D i 3D, filmy, schematy el., lub
    arch. i wiele innych.

    Te prog. charakteryzują się tym, że obrabiany plik w całości
    ładują do pam. RAM.

   6.6.3Przetwarzanie bazodanowe

    Opiera  się  na  pracy  z  bazą  danych  za pośrednictwem f.
    przykrywających silnik bazy danych (który w szczególności może
    być silnikiem SQL, który w szczególności może być na serwerze
    sieciowym). W tym modelu ważne jest by nie dać się ogłupić
    specyficznemu silnikowi bazy danych.

   6.6.4Przetwarzanie sieciowe

    Najczęściej  dotyczy  synchronizacji  danych  w bazach. Takie
    zadania   należy   realizować  w  wątkach  wg  wzorca  proj.
    Producent-Konsument.  Zarówno  po  stronie  aplikacji jak i po
    stronie demona sieciowego.

   6.6.5Przetwarzanie wielowątkowe

    Gdy mowa o wątkach zwykle mówi się o muteksach i semafoarach -
    czyli o tym jak ważne jest chronienie współdzielonych zasobów.
    Jednak można unikać muteksów i semaforów kopiując dla każdego
    wątku   komplet   danych  roboczych.  Jednak  w  programowaniu
    wielowątkowym jest jeszcze coś niebezpieczniejszego co podbija
    stopień trudności:

    Podstawą jakiejkolwiek pracy w wątkach jest maszyna stanów jaka
    definiuje nam gdzie jesteśmy, kiedy i gdzie się udamy.

6.7Sposób wykrywania błędów

    Generalnie można wyróżnić nast. etapy wykrywania błędów przez
    programistę:
     1. Podczas przeglądu kodu:
        Każdą f. zmienianą przez siebie przejrzyj 2x (od góry do
        dołu).
        Każdą f. zmienianą przez innych przejrzyj 3x (od góry do
        dołu).
        Każde włączenie kodu do gałęzi głównej powinny zaakceptować
        co najmniej 2 osoby[58]^5.
     2. Automaty do spr. popr. kodu należy stosować wszędzie gdzie
        to możliwe. Nie musi być to SI!;
     3. W  kl.  logiki  w  f. pub. spr. niezmienniki zgodnie z roz.
        Spr. niezmienników;
     4. W   kl.   przyjmujących   dane   z  zew.  stosujemy  prog.
        kontraktowe.  Są  to  kl.:  opcje (pliki ini, param. linii
        komend),  pobieranie  danych  przez  int. uż., wczytywanie
        danych z dysku oraz pobieranie danych z sieci.

    Niezmienniki kl. różnią się od prog. kontraktowego tym, że
    niezmiennik musi być spełniony w każdej sytuacji (w każdej f.
    danej  kl.)  natomiast testy w prog. kontraktowym związane są
    ściśle z daną f.
     5. Podczas testów jednostkowych:

    TESTY JEDNOSTKOWE DODAJEMY DO PROJ. TAK DŁUGO AŻ WYCZERPIĄ SIĘ
    NAM  POMYSŁY NA NOWE TESTY (lub dopóki nie skończy się czas
    przewidziany na testy automatyczne). TYLKO W TEN SPOSÓB MOŻNA
    MIEĆ PEWNOŚĆ, ŻE PROG. DZIAŁA PRAWIDŁOWO.
     6. Testy manualne: Gdy kodujesz coś nowego i już wszystko gra,
        wymyśl i zrób jeszcze 2 dodatkowe testy manualne.
        Dokładne  przetestowanie okienek i kontrolek zostawiamy do
        przetestowania testerom manualnym.

    Programowanie sterowane przez testy należy odrzucić gdyż jego
    istotą  jest  odwrócenie procesu twórczego i zaczęcie go od
    końca, czyli od testów. Jest to kolejna podpucha z SZAP.

                               7Styl prog.

7.1Izolacja proj. od świata zewnętrznego

    W głowie ojca dyrektora wszystko się może zdarzyć, podobnie jak
    wszystko może się zdarzyć w głowach bankierów co płacą za
    rozwój  darmowego  oprogramowania. Dlatego zgodnie z Ustawą o
    Ochronie Zdrowia Psychicznego trzeba się zabezpieczać przed ich
    sabotażem. W tym celu należy przestrzegać tych zasad:
     1. Zachowanie dyskrecji:
          1. Ze światem zew. komunikuje się wył. kiero. proj.;
          2. Klienta nie wtajemnicza się w szczegóły stosowanych
             rozw. tech.;
          3. O postępach prac wie wyłącznie zespół i klient.
     2. Praca offline:
          1. Zespół pracuje w sieci lokalnej odciętej od Internetu;
          2. W  sieci  lokalnej  jest  repo  z  lustrzanym serwerem
             pakietów używanej dystrybucji (wszyscy muszą pracować
             na tym samym distro w tej samej wer.);
          3. W  razie  potrzeby  zespół  wyszukuje  info  w sieci
             Internet za pomocą sprytnych tel.;
          4. Dane  do sieci Internet wysyła się kopiując dane na
             patyki USB.
     3. Izolacja kodu:
          1. Należy ograniczać il. zew. bibl.;
          2. Wszystkie typy proste należy przezwać własnymi. Są ku
             temu  co  najmniej  2  powody: typy te zmieniają się
             między kompilatorami i sys. op., może zaistnieć kiedyś
             konieczność zmiany typu na inny.
          3. W razie stosowania zew. bibl. kl. należy je przezywać
             aliasami (oczywiście tylko te kl. których się używa).
             Robi się tak by w razie potrzeby można było taki alias
             zastąpić własną, ROZSZERZONĄ klasą bez ruszania jego
             wszystkich wystąpień w kodzie.
          4. W  razie  stosowanie  zew.  bibl. f. W j. C należy je
             opakowywać własnymi kl. w celu uproszczenia ich użycia
             i  możliwości wymiany danej bibl. w j. C (bez zmiany
             reszty kodu).
          5. Należy  się  bronić  przed  j.  SQL  stosując go w
             minimalnym stopniu: do serializacji obiektów i do ich
             wyszukiwania  w  bazie.  Nie  należy stosować SQL do
             żadnego przetwarzania danych, bo to j. skryptowy i ma
             nienormalną składnię (psuje umysł).

7.2Projektowanie i kodowanie wg optymistycznego scenariusza

    Projektowanie prog. (w dok. funkcjonalnej) należy prowadzić wg
    prostej zasady: realizujemy "optymistyczny scenariusz" w którym
    wszystko  się udaje. Proj. wg optymistycznego scenariusza jest
    prosty i naturalny, bo stanowi logiczną ścieżkę od startu do
    zakończenia.

    Jak się okazuje można również kodować wg tej zasady. Ma to tą
    zaletę,  że  kod  jest  wiernym  odbiciem  proj. - czyli jest
    zrozumiały dla każdego kto poznał dok. funkcjonalną.

    Kodowanie wg optymistycznego scenariusza jest trochę niezwykłe:
    należy kodować z minimalną liczbą zagnieżdżeń. Instrukcje "if"
    dotyczą przede wszystkim spr. czy wystąpił błąd. Kluczowe jest
    jednak, żeby nie dodawać bloku "else", bo to "else" to normalny
    przebieg  programu  -  czyli  optymistyczny  scenariusz. Jednak
    mimo,  że  takie  kodowanie  jest  logiczne  z projektowego p.
    widzenia, to jednak wprowadza konieczność znajomości kontekstu
    w jakim występuje dana linia kodu - może to być utrudnieniem
    dla recenzentów i dla następców.

    Programując  optymistyczny  scenariusz  po wykryciu błędu po
    prostu przerywamy optymistyczny scenariusz. W kodzie oznacza to
    zwrócenie kodu błędu (w j. Asembler i C) lub rzucenie wyjątku
    (w j. C++ i D).

    Aby  to  wyjaśnić  przedstawię  2 f. w j. C jedną zgodną z
    optymistycznym  scenariuszem,  a drugą w stylu "byle jak" (kod
    skompilowany   i   uruchomiony   ale   okrojony   dla   potrzeb
    publikacji):
[...]

enum KodBledu
{
    eSukces,
    eDaneWe,
    eZuzyciePaliwa,
    eDrukowanie
};

[...]

int funkcjaOptymistycznyScenariusz(struct Dane* d)
{
    if(sprDaneWe(d) != eSukces)
    {
       fprintf(stderr, "%s", "Błąd danych we.!\n");
       return eDaneWe;
    }

    if(obliczZuzyciePaliwa(d) != eSukces)
    {
       fprintf(stderr, "%s", "Błąd zużycia paliwa!\n");
       return eZuzyciePaliwa;
    }

    if(drukuj(d) != eSukces)
    {
       fprintf(stderr, "%s", "Błąd drukowania!\n");
       return eDrukowanie;
    }

    return eSukces;
}

int f_byle_jak(struct Dane* d)
{
    if(sprDaneWe(d) != eSukces)
    {
       fprintf(stderr, "%s", "Błąd danych we.!\n");
       return eDaneWe;
    }
    else
    {
       if(obliczZuzyciePaliwa(d) != eSukces)
       {
          fprintf(stderr, "%s", "Błąd zużycia paliwa!\n");
          return eZuzyciePaliwa;
       }
       else
       {
          if(drukuj(d) != eSukces)
          {
             fprintf(stderr, "%s", "Błąd drukowania!\n");
             return eDrukowanie;
          }
          else
             return eSukces;
       }
    }
}

[...]

7.3Sposób prezentacji wyników

    Dla porządku wymienię tu sposoby prezentacji danych programu:
     1. Wyjście na konsolę: typowy sposób działania poleceń linii
        komend.  Mimo, że jest to bardzo prosta prezentacja wyniku
        działania, to jest ona nadal bardzo użyteczna. Sprawdza się
        ona  równie  dobrze w codziennej pracy programisty jak i w
        warunkach domowych przy przetwarzaniu plików tekstowych.

    W sys. Windows programy graf. nie mogą pisać na konsolę - przy
    pierwszej próbie pisania na konsolę Windows ubije ten prog. W
    sys. Linuks nie ma tego ograniczenia.
     2. Interfejs    tekstowy:    ma   2   odmiany:   linijkowy   i
        pełnoekranowy.
          1. Interfejs  linijkowy  polega  na  wyświetleniu pyt. i
             oczekiwania na odp. wpisywaną z klawiatury.
          2. Interfejs    pełnoekranowy:   działa   podobnie   do
             interfejsów   graficznych.  Jednak  ma  inne  skróty
             klawiszowe i inną obsługę myszy. Do pełnoekranowych
             interfejsów  tekstowych  często  stosuje  się bibl.
             NCurses (pętla gry).
     3. Interfejs graficzny: To interfejs głównie do klikania. Od
        dziesięcioleci   bezkonkurencyjne   bibl.[59]^6  do  prog.
        interfejsów  graf. jest Qt (pętla zdarzeń) i SDL (pętla
        gry).

7.4Sposób zgłaszania błędów

    Jak się okazuje nawet sposób zwracania błędów przez f. ma wpływ
    na architekturę prog. Są 3 główne sposoby zwracania błędów:

   7.4.1Wartość zwracana z f.

    Po prostu wartość zwracana zawiera kod błędu. W sytuacji gdy
    wszystko gra f. zwraca 0 (tak samo jest w programach powłoki) w
    przeciwnym wypadku zwrócona war. jest kodem błędu.

    Problem ze zwracaniem kodu błędu w wyniku f. jest taki, że nie
    ma możliwości normalnego zwracania jej wyniku (chyba, że przez
    któryś z parametrów tej f.).
/* Zwracanie kodu błędu przez wart. zw.:*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Zmienne globalne::::::::::::::::::::::::::::::*/
enum KodyBledow
{
    eOK,
    eDrukowanie
};

const char gJakisFajnyTekst[] = { "Jakiś fajny tekst!" };

/* Funkcje:::::::::::::::::::::::::::::::::::::::*/
int drukuj(const char* pNapis)
{
    int cosNieTak = (printf("%s\n", pNapis) == 0);

    if(cosNieTak)
       return eDrukowanie;

    return eOK;
}

int main()
{
    int lKodBledu = drukuj(gJakisFajnyTekst);

    if(lKodBledu)
    {
       fprintf(stderr, "Wystąpił błąd! Kod: %d\n", lKodBledu);
       exit(EXIT_FAILURE);
    }

    printf("Wykonanie prawidłowe!\n");
    exit(EXIT_SUCCESS);
}

   7.4.2errno

    Cytat:  "errno  jest  definiowana  przez  standard  ISO  C jako
    modyfikowalna l-wartość typu int, która nie może zostać jawnie
    zadeklarowana;  errno  może  być makrem. Wartość errno jest
    lokalna w obrębie wątku, jej zmiana w jednym wątku nie wpływa
    na wartość w innym."[60]^7

    Koncepcja  enrno  jest  nietrafiona, bo nie jest znany używany
    zakres  wart.  errno. Dlatego by unikać konfliktów do nr kodu
    błędu  konieczna  jest  nazwa bibl. która go zgłasza. Wtedy
    programista mógłby sobie ustawić jako nazwę bibl. nazwę swojej
    aplikacji i samodzielnie zdefiniować jej kody błędów.
/* Przykład użycia errno do zwracania kodów błędów. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/* Zmienne globalne::::::::::::::::::::::::::::::*/
enum KodyBledow
{
    eOK,
    eMalloc,
    eSprntf
};

const char gJakisFajnyTekst[] = { "Jakiś fajny tekst!" };

/* Funkcje:::::::::::::::::::::::::::::::::::::::*/
const char* konwertuj(const char* pNapis)
{
    char* lWynik = malloc(100);

    if(!lWynik)
    {
       errno = eMalloc;
       return pNapis;
    }

    if(sprintf(lWynik, "%s %s\n", pNapis
          , "I jakiś fajny dodatek!") == 0)
    {
       free(lWynik);
       errno = eSprntf;
       return pNapis;
    }

    return lWynik;
}

int main()
{
    printf("%s", konwertuj(gJakisFajnyTekst));

    if(errno)
    {
       fprintf(stderr, "Wystąpił błąd! errno: %d\n", errno);
       exit(EXIT_FAILURE);
    }

    printf("Wykonanie prawidłowe!\n");
    exit(EXIT_SUCCESS);
}

   7.4.3Wyjątki

    W  j.  C++  wprowadzono wyjątki (oczywiście j. D też je ma).
    Wyjątki składają się z dwóch el.: najpierw deklaruje się dwa
    bloki kodu:
try
{}
catch()
{}

    Następnie w bloku try wywołuje się f. która potencjalnie może
    rzucić wyjątek (wyjątek może rzucić f. wywołana z bloku try
    bezpośrednio lub pośrednio). Wyjątek rzuca się dyrektywą throw.
    Wtedy następuje zwinięcie stosu (wyskoczenie z f.) do bloku try
    i przekazanie kontroli do bloku catch.

    W  bloku  obsługi  wyjątków  (blok  catch) normalnie działa
    polimorfizm, tak więc można swobodnie definiować typy wyjątków
    i w dowolnych miejscach można je łapać.

    Złapane wyjątki można dalej propagować. Poniżej pokazałem
    propagację wyjątku z dodaniem dodatkowej informacji.

    Podobnie  jak  w  przypadku  errno  z  j.  C tak samo koncepcja
    wyjątków w C++ jest nietrafiona. Wynika to z faktu, że Komitet
    Centralny  C++  zaleca  tworzenie  nowej kl. dla każdego rodz.
    wyjątku.

    Różnica  jest jedynie taka, że errno nie można poprawić, a
    wyjątki tak. Oto co trzeba w tym celu zrobić:
     1. Należy stworzyć własną kl. Wyjątek której konstruktor
        będzie pobierał nast. param.: nazwę modułu, kod błędu,
        treść błędu, plik, nazwa f. (razem z nazwą kl.), nr linii;
     2. Należy stworzyć makro które będzie pobierało nazwę modułu,
        kod błędu i treść błędu (reszta danych jest pobierana
        makrami typu __FILE__). To makro należy stosować w plikach
        definicji (C++);
     3. Należy stworzyć makro które będzie pobierało nazwę modułu i
        treść błędu (wywołuje on makro z p. 2 z kodem błędu -1). To
        makro  należy  stosować w plikach deklaracji (H++), np. w
        szablonach lub w f. wstawianych/inline.

    Ogólnie  bardzo nieufnie przyjmowano nowinki C++. Brak wiary w
    wyjątki pokutuje do dziś, np. w bibl. Qt wer. 6 z 2020r. nadal
    nie używa się wyjątków.

    Perfidnym rozwiązaniem w bibl. Qt uniemożliwiającym wygodną,
    globalną obsługę wyjątków jest łapanie wyjątków w kodzie
    obsługi pętli zdarzeń w Qt. To jest tym bardziej perfidne, że
    qt.io  oficjalnie  głosi  się  propagandę, że Qt nie używa
    wyjątków. To perfidne rozw. działa tak, że w razie wystąpienia
    takiego  nieprzechwyconego  wyjątku  program  Qt  wypisuje  na
    konsolę komunikat i kończy działanie. Co w normalnym przypadku
    jest całkowitym zaskoczeniem dla użytkownika prog.
// Przykład użycia wyjątków:
#include <exception>
#include <string>
#include <iostream>

using namespace std;

// Zmienne globalne::::::::::::::::::::::::::::::
enum KodyBledow
{
    eOK,
    eDrukowanie
};

char gJakisFajnyTekst[] = { "Jakiś fajny tekst!" };

// Klasy:::::::::::::::::::::::::::::::::::::::::
class Wyjatek : public exception
{
public:
    Wyjatek(string pKomunikat, int pKod)
       : cKomunikat(pKomunikat), cKod(pKod)
    {
    }

    const char* what() const noexcept
    {
       return cKomunikat.c_str();
    }
    int kod() const
    {
       return cKod;
    }

protected:
    const string cKomunikat;
    int cKod;
};

// Funkcje:::::::::::::::::::::::::::::::::::::::
void drukuj(string pNapis)
{
    try
    {
       cout << pNapis << endl;
    }
    catch(const exception& pWyjatek)
    {
       string lTresc = "Coś jest nie tak z cout w f. drukuj()!";
       lTresc += pWyjatek.what();
       throw Wyjatek(lTresc, eDrukowanie);
    }
}

int main(int n, char** w)
{
    try
    {
       drukuj(gJakisFajnyTekst);
    }
    catch(const Wyjatek& pWyjatek)
    {
       cerr << "Wystąpił wyjatek! Treść: "
          << pWyjatek.what()
          << " Kod błędu: " << pWyjatek.kod() << endl;
       exit(EXIT_FAILURE);
    }
    catch(const exception& pWyjatek)
    {
       cerr << "Wystąpił wyjatek! Treść: "
          << pWyjatek.what() << endl;
       exit(EXIT_FAILURE);
    }
    catch(...)
    {
       cerr << "Wystąpił nieznany wyjatek!" << endl;
       exit(EXIT_FAILURE);
    }

    cout << "Wykonanie prawidłowe!" << endl;
    exit(EXIT_SUCCESS);
}

7.5Komentarze

    Komentarze dzielimy na 2 rodz.:
     1. Komentarze   dokumentujące,  z  których  generowana  jest
        dokumentacja dla użytkowników (gł. Doxygen);
     2. Komentarze  techniczne,  czyli  wyjaśniające  szczegóły
        programistom utrzymującym kod.

    Kod powinien być samoopisowy tak jak to tylko możliwe. Kod nie
    powinien wymagać komentarzy. Komentarzy należy unikać po to by
    ułatwiać analizę i utrzymanie kodu.

    Komentarze   powinny  wyłącznie  dokumentować  udostępniane
    interfejsy  API.  Tylko  dlatego, żeby inni programiści mogli
    szybko je poznać i szybko zacząć je używać.

    Komentarze eliminujemy:
     1. Nazwami opisowymi f., zmiennych i makr.
     2. Tworzymy  nową f. pomocniczą z nazwą opisową. Po prostu
        wycinamy ten fragment kodu (który chcieliśmy skomentować) i
        tworzymy  nową  f. W normalnej sytuacji spadek wydajności
        jest  niezauważalny  na  dzisiejszych  prockach (po za tym
        można tą f. zrobić wstawianą/inline co powoduje, że koszt
        wywołania będzie zerowy).
     3. Tworzymy   komunikat  wypisujący  treść  komentarza.  Na
        marginesie: szczególnie często stosuję to w skryptach.

    Okazuje  się, że komentarze należy umieszczać wyłącznie w
    plikach  C++, bo umieszczanie ich w H++ prowadzi do degeneracji
    zalet plików nagłówkowych (przejrzystość konieczną do szybkiego
    ich analizowania).

    Dodatek 1: Mały sabotaż

    Mały sabotaż to nowoczesna odmiana dywersji Szarych Szeregów
    praktykowana    współcześnie   przez   Etycznych   Hakierów
    zatrudnianych przez tajną policję. Mały sabotaż obejmuje m.in.:
     1. Publikowanie opasłych książek i monografii (np. monografie
        prof. Jana Pająka). Zmusza to do pisania własnych skryptów
        i instrukcji (takich jak ta broszura);
     2. Psucie  prog.  konsoli  (brak  potrzebnych opcji, np. sed i
        brak   przetwarzania   blokowego).  Zmusza  to  do  pisania
        własnych   narzędzi   konsoli   (np.   energo-wymiana   z
        przetwarzaniem linijkowym i blokowym);
     3. Brak polskiej dok. (np. większość dok. sys. Ubuntu 20.04).
        Zmusza to do ogłupiającego tłumaczenia w myślach;
     4. Psucie  prog.  okienkowych przez brak przestrzegania zasady
        „zero-conf” czyli zmuszanie do ogłupiającego, wielokrotnego
        powtarzania tych samych czynności (np. Qt Creator). Zmusza
        to do prog. własnych prog. (np. edytor tekstu Tekstprofan);
     5. Psucie składni i organizacji kodu w j. programowania. (np.
        brak   dziedziczenia  operatorów  w  C++).  Zmusza  to  do
        programowania  makr  oraz  parserów  kodu  źródłowego i
        generatorów kodu;
     6. Psucie interfejsów bibl. (API) (np. kl. QFile oraz QProcess
        z   Qt).  Zmusza  to  to  prog.  własnych  normalnych  kl.
        opakowujących (np. bibl. energo-protekcja);
     7. Kasowanie  pam..  (np.  o  tym co b. chciałem zapamiętać
        czytając dany podręcznik). Zmusza to do programowania prog.
        w stylu SuperMemo (np. Turborety).

    Dodatek 2. Wzorce proj. - jak na nie patrzeć?

    Programistyczne  wzorce projektowe to robota tzw. Bandy Czworga
    z 1993r. Można powiedzieć, że to też podpucha z SZAP, bo jak
    się  czyta  zestawienie  tych  wzorców  to  wiele z nich jest
    bliźniaczo podobnych do siebie.

    Podam tu jakie wzorce są zdublowane i jakie na prawdę należy
    sobie przyswoić.

Wzorce

     1. Stan

    Robi:  Przechowuje  aktualny stan logiki, kontroluje przejścia
    stanów  i  informuje  o  zmianie  stanu  zainteresowane  kl. i
    wtyczki.

    Za pomocą: Zwykły enum i zmienna tego typu w kl. logiki.
     2. Prototyp

    Robi:  Jest  to  wirt. f. kopiuj() pełniąca w kl. rolę wirt.
    konstruktora.

    Za pomocą: Zwykła f. wirt.
     3. Singleton

    Robi: Jedyny w aplikacji obiekt świadczący określone usługi.

    Za  pomocą: Jest on tworzony na żądanie tuż przed pierwszym
    użyciem.
     4. Budowniczy

    Zamiast: Fabryka abstrakcyjna, Metoda wytwórcza.

    Robi:  Buduje  obiekty  za  pomocą  f. cząstkowych, lub przez
    łańcuch zobowiązań.

    Za pomocą: Abstrakcyjnego interfejsu.
     5. Kompozyt

    Robi: Dostarcza interfejs abstrakcyjny dla podobnych o.

    Za pomocą: Zwykłe dziedziczenie.
     6. Iterator-Wizytator

    Robi: Umożliwia iterację po kolekcji, której elementy odwiedza
    wizytator.

    Za pomocą: Pary f. lub pary kl.
     7. Wywołanie Zwrotne

    Zamiast: Dekorator, Obserwator

    Robi: Dodaje nowe f. do o. kl. w czasie działania prog.

    Za  pomocą: Rejestruje f. które ma wywoływać w określonych
    sytuacjach.

    W przypadku wywołań zwrotnych często zdarza się, że trzeba je
    synchronizować w celu unikania konfliktu z innymi wątkami.
     8. Producent-Konsument

    Robi: odbiera dane z wątku głównego, buforuje je i wysyła przez
    sieć.

    Za pomocą: 2 dodatkowe wątki do zapisu i do odczytu z gniazda z
    użyciem 2 buforów pierścieniowych (tryb full-duplex).
     9. Polecenie

    Robi: Buforuje napływające rozkazy.

    Za pomocą: Zwykła kl.
    10. Pamiątka

    Robi: Realizuje transakcje w aplikacjach finansowych i historii
    działań we wszelkich edytorach graficznych i tekstowych.

    Za pomocą: Zwykła kl.
    11. Metoda szablonowa

    Robi:  Implementuje  alg.  niezależnie  od  danych na których
    działa.

    Za pomocą: Zwykły szablonu f. lub szablon kl.
    12. Konwerter

    Zamiast: Adapter, Fasada, Most.

    Robi: Udostępnia nowy interfejs dla starej funkcjonalności.

    Za pomocą: Zwykła kl.
    13. Narzędzia-Logika-Okna

    Zamiast: Model-Widok-Kontroler (w j. ang. MVC)

    Robi: Logika steruje zarówno narzędziami jak i oknami. Dane do
    edycji  w oknach dostarcza logika. Kontrola popr. wprowadzanych
    danych powinna się odbywać zarówno na poziomie kontrolek okna
    jak i na poziomie f. pub. w logikach.

    Za pomocą: kl. narzędzi, kl. logiki i kl. okien.

Antywzorce

     1. Pyłek

    Robi: Dostarcza interfejs abstrakcyjny dla podobnych o.

    Za pomocą: Zwykłe dziedziczenie z leniwym niszczeniem o. w celu
    unikania ich ponownego tworzenia.

    Dlaczego bezcelowy: Szczegół optymalizacyjny.
     2. Fabryka abstrakcyjna, Metoda wytwórcza

    Robi: To samo co budowniczy.

    Za pomocą: Zwykła kl.

    Dlaczego bezcelowy: Zbędne powielenie.
     3. Dekorator, Obserwator

    Robi: To samo co wywołanie zwrotne.

    Za pomocą: Zwykłe f.

    Dlaczego bezcelowy: Zbędne powielenie.
     4. Adapter, Fasada, Most

    Robi: To samo co konwerter.

    Za pomocą: Zwykłe kl.

    Dlaczego bezcelowy: Zbędne powielenie.
     5. Interpreter

    Robi: Parsuje dok. w określonych j. opisujących zawartość.

    Za pomocą: o. kl.

    Dlaczego bezcelowy: To program a nie wzorzec.
     6. Model-Widok-Kontroler (w j. ang. MVC)

    Robi: Tworzy spójne trio kl. obsługujących okno prog.

    Za  pomocą: kl. model zawiera dane edytowane w oknie prog, kl.
    widok to okno i jego kontrolki, kontroler weryfikuje dane przed
    wprowadzeniem ich do modelu.

    Dlaczego bezcelowy: Zastępuję go wzorcem Narzędzia-Logika-Okna

    Dodatek 2: Zasady używania baz danych SQL

    Jak  wiadomo  całe  szaleństwo j. SQL wynika jedynie z chęci
    spowolnienia działania komputerów oraz wymuszania ogłupiających
    rozw. Dlatego aby temu przeciwdziałać należy:

W bibl. firmowej należy zakodować kl. BazaSQL z podst. f.

     1. utworzTablice();
     2. zapis();
     3. jestRekord();
     4. odczyt();
     5. usun().

Dla każdej tabeli należy utworzyć odpowiadające jej kl.

NazwaProg/Narzedzia/Baza/Tabela1Dane.h++
NazwaProg/Narzedzia/Baza/Tabela1Dane.c++
NazwaProg/Narzedzia/Baza/Tabela1Funkcje.h++
NazwaProg/Narzedzia/Baza/Tabela1Funkcje.c++
[…]
NazwaProg/Narzedzia/Baza/TabelanDane.h++
NazwaProg/Narzedzia/Baza/TabelanDane.c++
NazwaProg/Narzedzia/Baza/TabelanFunkcje.h++
NazwaProg/Narzedzia/Baza/TabelanFunkcje.c++

    Kl.  Tabela1Dane  ma  zmienne  odpowiadające  polom z Tabela1.
    Natomiast  Tabela1Funkcje  zawiera  proste f. odczytu, zapisu i
    wyszukiwania   danych   w  tej  tabeli  (do  tego  wszelkie  f.
    pomocnicze związane z tą tabelą).

    Zabronione jest strzelanie SELECT do bazy z dowolnego miejsca w
    prog. Wszelkie tego typu zadania wykonuje kl. Tabela1Funkcje.

Należy zakodować f. gNormSort

    Ta  f.  powinna  normalnie  sortować  napisy  i  numery w tych
    napisach łącznie z przypadkami występowania zer wiodących. Robi
    się to używając 2 zakresów w obu porównywanych ciągach znaków.
    Te  zakresy przesuwa się od pocz. do końca obu porównywanych
    ciągów znaków i kolejno porównuje. Do porównania ciągów znaków
    używa  się wtedy f. Unicode. Natomiast przy wykryciu numerów
    konwertuje się je do liczb 64bit. i porównuje jako l. Użycie f.
    gNormSort może być takie:
SELECT Imię, Nazwisko, WIEK FROM Użytkownik ORDER BY Imię, Nazwisko COLL
ATE gNormSort

Należy zakodować f. gWyrReg

    Ta  f.  powinna oferować normalne wyrażenia regularne zamiast
    „place holders”. Tą f. należy zakodować gdyż nie ma sensu
    szarpać się z wbudowanym operatorem LIKE polecenia SELECT, bo
    to  całkowicie nienormalne rozw. Użycie f. gWyrReg może być
    takie:
SELECT Imię, Nazwisko, WIEK FROM Użytkownik WHERE gWyrReg(Nazwisko, 'JAW
O.*KI') = 1

Raporty należy generować lokalnie na serwerze bazodanowym

    Podczas pracy z bazą sieciową do realizacji raportów nie należy
    kopiować wszystkich rekordów przez sieć. Zamiast tego należy
    zaprogramować demona który będzie działał na tym samym serwerze
    co motor SQL i który będzie uruchamiał programiki generujące
    raporty.  Ich  cechą  będzie  komunikacja  z  motorem  SQL na
    localhost (zamiast przez sieć) i to będzie główny powód ich
    wydajnego działania. Natomiast wydzielenie raportów do osobnych
    programików będzie pozwalało na ich łatwe przerywanie (przez
    ubijanie procesów – jest to chyba jedyna metoda na przerywanie
    zapytań – bo awaryjne przerwanie zapytania jest zazwyczaj jest
    blokowane na poziomie C/C++ przez bibl. dostępowe).

    Dodatkowo  takie  raporty  łatwo  uruchamiać zdalnie, co jest
    wygodne przy kodowaniu i testowaniu.

    [61]1Jednak należy mieć na uwadze, że biegłe opanowanie nowych
    narzędzi  i  procedur  zajmuje 2 lata. To wynika z niemieckich
    doświadczeń  wojennych  gdy,  w  latach  1939-41 przestawiali
    produkcję    z   rzemieślniczej   (manufaktury   opartej   na
    stanowiskach) na seryjną (opartą na linie produkcyjne tak jak w
    amerykańskich fabrykach Forda).

    [62]2Nawet gdy jest on klientem wew.

    [63]3Wszystkie   prezentowane   tu   przykłady  kodu  zostały
    skompilowane i uruchomione.

    [64]4Jest  to  polski asemebler FASM na proc. AMD64 (umożliwia
    też  kodowanie  na proc. Intel od 8080 do Pentium włącznie).
    UWAGA: aby skompilować kod trzeba skopiować do kat. bieżącego
    pliki import64.inc i elf.inc z kat.
    /usr/share/fasm/examples/elfexe/dynamic .

    [65]5W   dok.   Biblia   Tysiąclecia,   copyright  Wydawnictwo
    Pallottinum, PISMO-SW 3.0 BETA (26 lutego 2002 roku), copyright
    1994-2002  by Piotr Kłosowski klosowski@iele.polsl.gliwice.pl:
    Pwt  17,06: "Na słowo dwu lub trzech świadków skaże się na
    śmierć; nie wyda się wyroku na słowo jednego świadka.". Ten
    cytat  znaczy  tyle: Żadna ważna sprawa nie będzie oparta na
    mowie jednego świadka. Dlatego jak tylko jeden będzie mówił, że
    zachowałeś się w porządku, to uczciwy sędzia mu nie uwierzy.

    [66]6Można powiedzieć, że to stagnacja ponieważ nie są one
    jakieś nadzwyczajne.

    [67]7man errno

References

    1. mailto:jaworski1978@adres.pl
    2. mailto:energokoder@adres.pl
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads