Jak můžu opustit funkci

balki

Re:Jak můžu opustit funkci
« Odpověď #45 kdy: 13. 07. 2018, 11:13:48 »
takze neni problem ne?

Je, aj nie je, testy nepokryju vsetko. Proste moze sa stat, ze take dojde do produkcie. ;) 

A uz slubujem, ze v tomto topicu na nic nereagujem, nech si napise kto chce, co che. Nepotrebujem krmit trollov. (Nemyslim konkretne vas)


PetrM

Re:Jak můžu opustit funkci
« Odpověď #46 kdy: 13. 07. 2018, 11:15:24 »
Skratka, veci ako break, continue, goto by sa mali pouzivat v stave najhorsej nudze. Procedura by mala mat podla moznosti len jeden return.  To ze tu mate vystudovane hnojariny a potrebujete si honit ego a slovickarit, na tomto fakte nic nezmeni.

Jasně, ale best practice je třeba i max. dvě úrovně zanoření a hlavně, čitelný kód. Pokud ho nedokážeš přečíst, nepochopíš ho. A když ho nepochopíš, tak v něm nevidíš chyby a problémy.

Tvoje řešení (C):

Kód: [Vybrat]
void funkce(int a, int b, int c) {
  if((a < A_MIN)|| (a > A_MAX)||(b < B_MIN)|| (b > B_MAX)||(c < C_MIN)|| (c > C_MAX)) {
    ... // cast funkcionality
    if(!chyba1) {
      ... // cast funkcionality
      if(!chyba2) {
        ... // cast funkcionality
      }
    }
  }
}

Čitelný a pochopitelný řešení:

Kód: [Vybrat]
#define check(podminka) if(!(podminka)) return
#define check_range(val, min, max) check((val >= min) && (val <= max))

void funkce(int a, int b, int c) {
  check_range(A, A_MIN, A_MAX);
  check_range(B, B_MIN, B_MAX);
  check_range(C, C_MIN, C_MAX);
  ... // cast funkcionality
  check(chyba1);
  ... // cast funkcionality
  check(chyba2);
   ... // cast funkcionality
}

A nakonec kód, který fanatik do strukturovanýho programování nemá šanci přežít:

Kód: [Vybrat]
void initStruct(struct mojestruct* data) {
  assert(data != NULL);
  ...
}

A co teprve __attribute__((noreturn)) v GCC...  Ta ti taky do plánu nezapadá, že?  ;D ;D ;D

Kit

Re:Jak můžu opustit funkci
« Odpověď #47 kdy: 13. 07. 2018, 11:41:22 »
Tvoje řešení (C):

Kód: [Vybrat]
void funkce(int a, int b, int c) {
  if((a < A_MIN)|| (a > A_MAX)||(b < B_MIN)|| (b > B_MAX)||(c < C_MIN)|| (c > C_MAX)) {
    ... // cast funkcionality
    if(!chyba1) {
      ... // cast funkcionality
      if(!chyba2) {
        ... // cast funkcionality
      }
    }
  }
}

Chybí tomu větve "else" s ošetřením chyb, které budou nejspíš úplně vespod, aby je nikdo nenašel.

PetrM

Re:Jak můžu opustit funkci
« Odpověď #48 kdy: 13. 07. 2018, 12:11:25 »
Switch je if na steroidoch. Ak to niekto pouziva ako goto, tak je to antipattern. Naduzivanie switch je zapach v kode, ale o tom som nechcel. Zaujimave, ze ste si zasa precitali, len to co sa hodilo vasemu egu. Skuste prosim prestat pisat na chvilu, bude to tak lepsie.

Ne, to není ani náhodou, protože:
1) řetězec if-else nad enumem (třeba stavy ve stavovým automatu) nehlídá, že je některý stav neošetřený. Switch aspoň hodí warning, pokud vyhodíš DEFAULT (další synonymum pto GOTO).
2) Přepiš pomocí switche bez jakýhokoliv IFu následující kód (pokud je SWITCH rozšířením IFu, neměl by to být problém):
Kód: [Vybrat]
if(a == 5) {
  akce1();
} else if(b == 7) {
  akce2();
} else if((c > 0x07) && (c <= 0xf3) {
  akce3();
} else  {
  akce4();
}
3. Dovoluje pass through z jedné větve do druhé, to bez něj dáš jenom s GOTO nebo nepřehledným chaosem a opakovaným vyhodnocováním (= drahý, zahazuješ zbytečně pipeline).

Prostě IF != SWITCH a programátor ví, kdy co použít. Pokud to zaměňuješ, jsi fakt dřevo.

PetrM

Re:Jak můžu opustit funkci
« Odpověď #49 kdy: 13. 07. 2018, 12:25:27 »
Chybí tomu větve "else" s ošetřením chyb, které budou nejspíš úplně vespod, aby je nikdo nenašel.

Nechybí. To může být na vyšší úrovni. Třeba zrovna včera jsem dělal ovladač HW, který běží ve vlastním vlákně a init vypadal (zjednodušeně) asi takhle:
Kód: [Vybrat]
error_t initDriver(struct params* params) {
  // Parametry jsou povinne
  if(!assigned(params)) return ERR_INVALID_PARAMS;

  // Potrebujeme frontu pro komunikaci
  queue = queueCreate(dataType, size);
  if(!assigned(queue)) {
     releaseMem();
     return ERR_OUT_OF_MEMORY;
  }
 
  // Potrebujeme mutex pro zamykani HW
  mutex = mutexCreate();
  if(!assigned(mutex)) {
     releaseMem();
     return ERR_OUT_OF_MEMORY;
  }

  // Init zeleza
  if(!hwInit()) return ERROR_HARDWARE;

  // Ted muzeme udelat vlakno pro obsluhu
  threadHandle = threadCreate(funkce, priorita, stack);
  if(!assigned(threadHandle)) {
     releaseMem();
     return ERR_OUT_OF_MEMORY;
  }

  // Init
  ...
  // Hotovo
  return ERROR_NONE;
}

Víš, jaký by to byl binec Balkiho stylem? A opravdu by nestačil jeden return, protože v tomhle se i jednoduše zaručí, že pokud není paměť, nebude se pokoušet tvořit další objekty...


BoneFlute

  • *****
  • 1 901
    • Zobrazit profil
Re:Jak můžu opustit funkci
« Odpověď #50 kdy: 13. 07. 2018, 12:38:33 »
Ne, to není ani náhodou, protože:
1) řetězec if-else nad enumem (třeba stavy ve stavovým automatu) nehlídá, že je některý stav neošetřený. Switch aspoň hodí warning, pokud vyhodíš DEFAULT (další synonymum pto GOTO).
To hódně záleží na jazyku. S goto to vůbec nesouvisí.

2) Přepiš pomocí switche bez jakýhokoliv IFu následující kód (pokud je SWITCH rozšířením IFu, neměl by to být problém):
Kód: [Vybrat]
if(a == 5) {
  akce1();
} else if(b == 7) {
  akce2();
} else if((c > 0x07) && (c <= 0xf3) {
  akce3();
} else  {
  akce4();
}
Kód: [Vybrat]
switch (True) {
  case (a == 5):
    akce1();
    break;
  case (b == 7):
    akce2();
    break;
  case ((c > 0x07) && (c <= 0xf3)):
    akce3();
    break;
  default:
    akce4();
}

3. Dovoluje pass through z jedné větve do druhé, to bez něj dáš jenom s GOTO nebo nepřehledným chaosem a opakovaným vyhodnocováním (= drahý, zahazuješ zbytečně pipeline).
Záleží na jazyku.
V mnoha případech to naopak zpřehledňuje.

Prostě IF != SWITCH a programátor ví, kdy co použít.
Tak to každopádně.

Pokud to zaměňuješ, jsi fakt dřevo.
Odpusť si ty invektivy.

Zásadní rozdíl mezi IF a SWITCH je v tom, že IF je binární, zatímco SWITCH je na enum. A vzhledem k tomu, že bool je taky enum, tak má balki do určité míry pravdu.

Kit

Re:Jak můžu opustit funkci
« Odpověď #51 kdy: 13. 07. 2018, 12:39:10 »
Chybí tomu větve "else" s ošetřením chyb, které budou nejspíš úplně vespod, aby je nikdo nenašel.

Nechybí. To může být na vyšší úrovni. Třeba zrovna včera jsem dělal ovladač HW, který běží ve vlastním vlákně a init vypadal (zjednodušeně) asi takhle:
Kód: [Vybrat]
...

Takhle to dělám také a snad i každý rozumný programátor. Odkazoval jsem se na předchozí špagetový zdroják.

x14

  • ***
  • 182
    • Zobrazit profil
    • E-mail
Re:Jak můžu opustit funkci
« Odpověď #52 kdy: 13. 07. 2018, 12:49:15 »
Chybí tomu větve "else" s ošetřením chyb, které budou nejspíš úplně vespod, aby je nikdo nenašel.

Nechybí. To může být na vyšší úrovni. Třeba zrovna včera jsem dělal ovladač HW, který běží ve vlastním vlákně a init vypadal (zjednodušeně) asi takhle:
Kód: [Vybrat]
error_t initDriver(struct params* params) {
  // Parametry jsou povinne
  if(!assigned(params)) return ERR_INVALID_PARAMS;

  // Potrebujeme frontu pro komunikaci
  queue = queueCreate(dataType, size);
  if(!assigned(queue)) {
     releaseMem();
     return ERR_OUT_OF_MEMORY;
  }
 
  // Potrebujeme mutex pro zamykani HW
  mutex = mutexCreate();
  if(!assigned(mutex)) {
     releaseMem();
     return ERR_OUT_OF_MEMORY;
  }

  // Init zeleza
  if(!hwInit()) return ERROR_HARDWARE;

  // Ted muzeme udelat vlakno pro obsluhu
  threadHandle = threadCreate(funkce, priorita, stack);
  if(!assigned(threadHandle)) {
     releaseMem();
     return ERR_OUT_OF_MEMORY;
  }

  // Init
  ...
  // Hotovo
  return ERROR_NONE;
}

Víš, jaký by to byl binec Balkiho stylem? A opravdu by nestačil jeden return, protože v tomhle se i jednoduše zaručí, že pokud není paměť, nebude se pokoušet tvořit další objekty...

Nechybí ti tam náhodou releaseMem() ve větvi if(!hwInit()) ?
BTW: Ten mutex, thread a queue není třeba uklízet?

BoneFlute

  • *****
  • 1 901
    • Zobrazit profil
Re:Jak můžu opustit funkci
« Odpověď #53 kdy: 13. 07. 2018, 12:54:27 »
Skratka, veci ako break, continue, goto by sa mali pouzivat v stave najhorsej nudze. Procedura by mala mat podla moznosti len jeden return.
Kód: [Vybrat]
def foo (xs):
    assertList(xs) # vyhodí výjimku
    if isEmpty(xs):
        return 0     # speciální hodnoty ukončíme předčasně
    val = fetchDefaultValueFromDB()
    for x in xs:
        val = val + x
    return val

balki

Re:Jak můžu opustit funkci
« Odpověď #54 kdy: 13. 07. 2018, 13:02:01 »
Ja len tak, ze ci ten spagetovy festival s memory leakmi nechcete predvadzat v inom vlakne. Nereagujte na mna prosim.

BoneFlute

  • *****
  • 1 901
    • Zobrazit profil
Re:Jak můžu opustit funkci
« Odpověď #55 kdy: 13. 07. 2018, 13:04:51 »
Pro původního autora:

void Kresli ()
{
 .. nějaký ten kód
if (nějaká podmínka)
vyfuň z funkce ..

}

na místo "vyfuň z funkce" jsem zkoušel dát break; ale překladači se to nelíbilo, jako že "illegal break".
Tak jsem dal return;, ten se provedl, ale vytuhnul mi celý program.
Jak mohu opustit tu funkci Kresli, aby program pokračoval za místem, odkud byla Kresli zavolána?
Z funkce můžeš vyfunět pouze pomocí:
- kód doputuje až do konce (týká se jen procedur)
- pomocí return
- pomocí výjimky

Technicky ještě můžeš ukončit celej program pomocí exit, a možná by se našel i další způsob - ale to obecně neřeš a drž se  předchozího seznamu.

return a výjimka ti vyfuní z libovolně zanořeného výrazu. break, continue, etc je součástí struktur switch, while, for.

PetrM

Re:Jak můžu opustit funkci
« Odpověď #56 kdy: 13. 07. 2018, 13:13:51 »
Nechybí ti tam náhodou releaseMem() ve větvi if(!hwInit()) ?

Koukám, že chybí, chyba při kopírování. Normálně je odeslání chyby zabaleno v makru, tady jsem to ručně rozepsal...

BTW: Ten mutex, thread a queue není třeba uklízet?

Jo, ten se uklidí. Při initu jsou handle NULL. Funkce releaseMem testuje ty pointery a pokud je některý pointer NULL, uvolní ho. Takže bez ohledu na místo chyby se paměť uvolní...

kkk

Re:Jak můžu opustit funkci
« Odpověď #57 kdy: 13. 07. 2018, 13:30:48 »
Skratka, veci ako break, continue, goto by sa mali pouzivat v stave najhorsej nudze. Procedura by mala mat podla moznosti len jeden return.  To ze tu mate vystudovane hnojariny a potrebujete si honit ego a slovickarit, na tomto fakte nic nezmeni.

Jasně, ale best practice je třeba i max. dvě úrovně zanoření a hlavně, čitelný kód. Pokud ho nedokážeš přečíst, nepochopíš ho. A když ho nepochopíš, tak v něm nevidíš chyby a problémy.

Tvoje řešení (C):

Kód: [Vybrat]
void funkce(int a, int b, int c) {
  if((a < A_MIN)|| (a > A_MAX)||(b < B_MIN)|| (b > B_MAX)||(c < C_MIN)|| (c > C_MAX)) {
    ... // cast funkcionality
    if(!chyba1) {
      ... // cast funkcionality
      if(!chyba2) {
        ... // cast funkcionality
      }
    }
  }
}

Čitelný a pochopitelný řešení:

Kód: [Vybrat]
#define check(podminka) if(!(podminka)) return
#define check_range(val, min, max) check((val >= min) && (val <= max))

void funkce(int a, int b, int c) {
  check_range(A, A_MIN, A_MAX);
  check_range(B, B_MIN, B_MAX);
  check_range(C, C_MIN, C_MAX);
  ... // cast funkcionality
  check(chyba1);
  ... // cast funkcionality
  check(chyba2);
   ... // cast funkcionality
}

A nakonec kód, který fanatik do strukturovanýho programování nemá šanci přežít:

Kód: [Vybrat]
void initStruct(struct mojestruct* data) {
  assert(data != NULL);
  ...
}

A co teprve __attribute__((noreturn)) v GCC...  Ta ti taky do plánu nezapadá, že?  ;D ;D ;D


#define check(podminka) if(!(podminka)) return

Tak ty jses dobry cune. Pouzivani maker je problematicke samo o sobe. Neda se to poradne debugovat. Atd. Takovehle makra mas v produkcnim kodu?

Kit

Re:Jak můžu opustit funkci
« Odpověď #58 kdy: 13. 07. 2018, 13:35:36 »
Ja len tak, ze ci ten spagetovy festival s memory leakmi nechcete predvadzat v inom vlakne. Nereagujte na mna prosim.

Nevadí nám, že sem píšeš příspěvky. Nemělo by ti vadit, že my sem také píšeme příspěvky.

Memory leaky se týkají zejména jazyka C, který se zde řeší.

BoneFlute

  • *****
  • 1 901
    • Zobrazit profil
Re:Jak můžu opustit funkci
« Odpověď #59 kdy: 13. 07. 2018, 13:43:40 »
Ja len tak, ze ci ten spagetovy festival s memory leakmi nechcete predvadzat v inom vlakne. Nereagujte na mna prosim.
Nevadí nám, že sem píšeš příspěvky. Nemělo by ti vadit, že my sem také píšeme příspěvky.
Ty tu určuješ pravidla komu může co vadit?