Pierwszym rozszerzeniem o którym chciałem napisać, jest brakująca mi od długiego czasu stała binarna, czyli po prostu:
int a = 0b01010101;
Taaak, w końcu nie będę musiał przeliczać stałych binarnych na hexadecymalne przed ich wpisaniem w kod (chociaż doszedłem w tym już do nie lada wprawy).
Drugim ciekawym rozszerzeniem jest możliwość deklaracji zmiennych w pamięci lokalnej wątku (thread-local storage). Czyli w końcu użytkownicy gcc/g++ (łącznie z MinGW, o tym za chwilę) dostali możliwość operowania na TLS bez korzystania z API systemowego :)
W gcc/g++ wygląda to tak:
__thread int asdf;
W Visual C++ operowanie na TLS jest już od pewnego czasu, tyle że deklaracja wygląda trochę inaczej (ale nadal podobnie):
__declspec(thread) int asdf;
W C++0x ma być to zawarte w standardzie, i ma do tego służyć jeszcze inny keyword: thread_local.
Powyższe rozszerzenie przetestowałem (kod i output na dole postu) na MinGW gcc 4.4.0, jednak nie zadziałało poprawnie, tj. zmienna typu __thread została potraktowana jak zwykła zmienna globalna. Co ciekawe, nie było również żadnego błędu, więc keyword __thread został rozpoznany jako keyword. Na stronie MinGW znalazłem tylko enigmatyczną uwagę dotyczącą GCC 4.4, mówiącą że keyword __thread jest honorowany, a w importach exeka znalazłem funkcje od Tls*, więc wygląda na to, że __thread na 4.4.0 nie jest zaimplementowany poprawnie.
Natomiast wystarczył upgrade do wersji 4.5.0 żeby wszystko zaczęło działać OK (btw, nowy mingw-get mi całkiem podszedł) :)
Z innych ciekawych rozszerzeń które widziałem dodano liczby stałoprzecinkowe oraz liczby zmiennoprzecinkowe o podstawie 10 (zwykłe floaty mają podstawę 2, co nie zawsze jest nam na rękę).
O powyższych napiszę być może przy okazji (tj. o ile się im bliżej przyjże) :)
Natomiast, mimo wielu nowych i ciekawych extensionów, nadal brakuje mi dwóch rzeczy:
1. break wyskakującego z kilku pętli
2. break wyskakującego z if'a
Ta pierwsza rzecz przydaje się, gdy mamy dwie zagnieżdżone pętle, i chcemy je nagle szybko opuścić, np:
while(a) {
while(b) {
if(c) break_out_of_both_loops;
}
}
Niestety, w C czy C++ nie można tego zrobić w sposób bezpośredni. Jeśli chodzi o emulowanie takiej konstrukcji, to stosuje się kilka różnych trików:
1. goto do etykiety znajdującej się za pętlami - streight forward, ale fanatykom anty-goto się noże w kieszeni właśnie pootwierały
2. rzucić wyjątek łapany poza pętlami - ale nie każdy lubi stosować wyjątki, a niektóre rodzaje wyjątków są dość kosztowne
3. obie pętle przepisać do innej funkcji i stosować return - mało czytelne, kod niepotrzebnie się rozrasta, dodatkowa funkcja, etc
4. przepisać tylko wewnętrzną pętle do innej funkcji zwracającej bool i stosować return false, oraz if(!funkcja()) break; - j/w
5. sprawić aby warunek 'a' był niespełniony i zrobić break (np. jeśli jest while(a < 10), to zrobić if(c) { a = 10; break; }) - mało eleganckie, poza tym błędogenne, i trudne do zastosowania jeśli 'a' jest wywołaniem jakiejś funkcji na którą nie można bezpośrednio wpłynąć
6. zadeklarować flagę, i po wewnętrznej pętli ją testować (np. bool run_away = false; while(b) { if(c) { run_away = true; break; } } if(run_away) break;) - to chyba najbardziej eleganckie rozwiązanie, ale wymaga wprowadzenia dodatkowej zmiennej
7. etc...
Osobiście marzy mi się rozwiązanie z nazywanymi blokami kodu, tak jak mają w Javie. Np.
while(a) petla_zew: {
while(b) {
if(c) break petla_zew;
}
}
Innym pomysłem jest break
By the way...
On 22nd Nov'24 we're running a webinar called "CVEs of SSH" – it's free, but requires sign up: https://hexarcana.ch/workshops/cves-of-ssh (Dan from HexArcana is the speaker).
Cóż, może kiedyś się tego doczekam ;)
Drugą rzeczą o której pisałem jest break ze "słabszego" bloku kodu, np. wolno-stojącego bloku kodu { }, albo if(c) { }. Np. chciałbym w poniższym przypadku opuścić blok if() dużo wcześniej:
if(a) {
some_code;
if(b) break_out_of_if;
some_code;
}
Niestety, tak się nie da.
Żeby wyemulować takie zachowanie, można użyć goto i etykiet lub przekształcić blok if na while i dodać dodatkowy break; na końcu:
while(a) {
some_code;
if(b) break;
some_code;
break; // dodatkowy break
}
Jeśli nasz if(a) był jednak w pętli, i korzystaliśmy z break, to niniejszy problem został sprowadzony do wcześniej opisywanego problemu, czyli braku break
Tak więc nie miałbym nic przeciwko jakiemuś break_weak :)
(swoją drogą, perl ma jeszcze inną ciekawą konstrukcje z rodziny break/continue, a mianowicie redo)
I w sumie tyle.
Załącznik 1 - eksperymentalny programik wykorzystujący __thread:
#include <stdio.h>
#include <windows.h>
static __thread int var;
DWORD WINAPI MyThread(LPVOID lpParameter)
{
int param = (int)lpParameter;
printf("MyThread initial var:\t\t%i\n", var);
var = param;
printf("MyThread var at end:\t\t%i\n", var);
return 0;
}
int main(void)
{
HANDLE th;
var = 13;
printf("Main initial var:\t\t%i\n", var);
th = CreateThread(NULL, 0, MyThread, (LPVOID)37, 0, NULL);
WaitForSingleObject(th, INFINITE);
CloseHandle(th);
printf("Main after MyThread finishes:\t%i\n", var);
return 0;
}
Niepoprawny output (MinGW gcc 4.4.0):
Main initial var: 13
MyThread initial var: 13
MyThread var at end: 37
Main after MyThread finishes: 37
Poprawny output (MinGW gcc 4.5.0):
Main initial var: 13
MyThread initial var: 0
MyThread var at end: 37
Main after MyThread finishes: 13
Comments:
=>
Tak więc nie miałbym nic przeciwko jakiemuś break_weak :)
http://theserverpages.com/php/manual/en/control-structures.break.php
Faktycznie przydałoby się coś takiego w C++, ale już dawno nic w nim nie robiłem, a szkoda, bo bardzo lubię ten język. ;]
Czym się różni użycie if/break od if/goto? Poza tym, że już 'jest', pozwala wyskoczyć i z pętli i z ifa?
Thx, fixed.
@Tomasz Kowalczyk
Przyznaje, że nie wiedziałem, że w PHP jest break <N>. Cool, thx za linka :)
@mt3o
Przy teoretycznym weak_break nie musiałbyś tworzyć dodatkowej etykiety na końcu bloku if, więc to kwestia wygody / jednej linii mniej do napisania.
Poza tym, jak wynika z mojego doświadczenia, inni programiści (tj. nie autor kodu) mniej wojowniczo reagują na używanie extensionów języka niż goto (co jest dość dziwne, ponieważ poziom niżej zarówno break jak i goto i tak są sprowadzone do goto adres, a w zasadzie jmp adres).
do {
if(c) break;
} while(0);
Czy to nie zadziała tak jak trzeba? I nie trzeba dwóch breaków i pierwszego sprawdzania.
Do tego wiele z tych pomysłów jest w języku D, który jest bardzo podobny do C++... no ale wiem, że nie o to chodzi.
Mały błąd - labele (nazywanie pętli) w javie robi się na odwrót:
petla_zew: while(a) {
while(b) {
if(c) break petla_zew;
}
}
Najpierw ktoś wymyśla język C - swego rodzaju zastępstwo assemblera... Potem ktoś dorabia do tego języka filozofię, wręcz religię, która zakazuje używać niektórych wyrazów... nagle okazuje się że np. "goto" to przekleństwo... w odróżnieniu od "break" i "continue", które zasadniczo robią to samo, ale brzmią jakże mniej wulgarnie.
I tak oto kilkadziesiąt lat później, w XXI wieku, entuzjaści języka C mają kolejną filozoficzno-religijną zagwózdkę: jak zastąpić niegdysiejsze "goto" nowym, mniej wulgarnym wyrazem? Aby wszystko wróciło do normy.. :-)
Paranoja
@Gyn: nie ma sprawy, też dowiedziałem się przypadkiem "studiując" jakiś blog. PHP ma wiele takich kruczków o których 90% programistów nie ma pojęcia i myśli "e tam ten pehap to taki trywialny, dwie pętle i ot, język". ;]
Fanatyzm naty-goto wynika chyba z traumatycznych doświadczeń z czytaniem kodu gdzie goto było główną instrukcją sterującą.
O nazywaniu pętli/bloków w javie nie wiedziałem więc dziękuję za info.
Zauważ, że kod pierwotny wygląda tak:
if(a) {
if(c) break;
}
Natomiast zaproponowane przez Ciebie rozwiązanie pomija w ogóle sprawdzanie warunku 'a', które, wg założenia, jest kluczowe dla zachowania logiki kodu. Więc użyć trzeba nie do {} while(0); a while(a) { ...; break; }, ponieważ to ostatnie nie zmienia (nie wymaga zmiany) logiki kodu.
D muszę się w końcu poważniej zainteresować, bo poza Hello World to dużo w nim nie napisałem. Chyba nawet mniej niż w Go (w którym btw też są bloki z etykietami - http://golang.org/doc/go_spec.html#Break_statements ).
Co do przykładu o którym piszesz, to nie chodziło mi o konkretne zacytowanie jak to jest w Javie, tylko o zaprezentowanie jak ja bym to widział w C/C++.
W C/C++ nie może być tak samo jak w Javie/Go, ponieważ w ten sposób są tworzone etykiety do goto. No chyba żeby powiązać ich funkcje, co moim zdaniem najlepszym pomysłem nie jest - wystarczy że keyword static używa się do trzech różnych rzeczy, wystarczy tych wieloznaczności hehehe.
@beem
Very insightful ;)
W sumie wystarczy pamiętać, że goto to narzędzie, które można użyć dobrze (np. jak młotek do wbicia gwoździa) lub źle (np. jak młotek do uderzenia się (albo innego programistę który ma pecha trzymać nasz gwóźdź) w palec). That's life hehehe....
Hmm, hehe, może reverse-engineering, który m.in. polega na rozplątywaniu różnych goto (jmp/Jcc) powinien być obowiązkowy dla większości programistów? ;)
@Tomasz Kowalczyk
"nie posądzam go o kod w stylu 10 GOTO 20, 20 GOTO 10"
Haha takiego kodu naklepałem niemało w życiu, szczególnie że pierwszymi językami w których pisałem były BASICi (ATARI Basic, Microsoft GW BASIC, etc), w których poza GOTO i średnio wygodnego GOSUB za dużego wyboru nie było ;p
Hmm, chociaż przyznaje, że kod który niedawno pisałem w BASICu miał mniej GOTO a więcej GOSUB niż kiedyś (http://gynvael.coldwind.pl/?id=119).
Ad "każda instrukcja goto powinna być opatrzona odpowiednimi informacjami o nazwisku, adresie i numerze karty kredytowej programisty"
Hehe tekst zabawny, chociaż się z nim nie zgadzam ;)
PHP ma trochę fajnych rzeczy, np. pomysł z __autoload bardzo mi się podoba :)
1) w szkole uczy się, że w językach strukturalnych goto jest zabronione, zachowane jest tylko ze względu na kompatybilność z językami liniowymi.
2) w każdej książce, która jednego z języków strukturalnych czy obiektowych uczy, mówią, że nie powinno się używać goto. (Może lepiej jakby mówili żeby używać z rozsądkiem) Zwykle podają za argument czytelność kodu.
3) nawet na egzaminach zawodowych na TI (312[01]) pojawiało się, której instrukcji nie należy używać, i z czterech proponowanych, należało wybrać goto, aby dostać punkt.
Skoro młody, nie zawsze związany z kodowaniem człowiek już wie takie rzeczy, to nie ma co się dziwić, że reakcje są takie a nie inne
W każdym razie, parafrazując: "Nie po to powstały wszystkie struktury kontrolne, pętle i całe OOP, żeby programy chodziły szybko i były czytelne" :)
Na egzaminie zawodowym? Huh... Widać ta indoktrynacja "anty-goto" ma podłoże polityczne ;DDD
@Dab
Co do do {} while(0) - rzuć okiem na moją odpowiedź na komentarz Faramira kilka komentarzy wyżej, ale w skrócie - tak, jest to metoda na wyemulowanie weak_break ;)
Chociaż niektóre kompilatory (pozdrawiam Visual C++ 2k5) przy /W4 są niepocieszone, że warunek jest stały przy while(0), i trzeba kombinować z for(;;) { ...; break; } (w którym warunku po prostu nie ma).
Podłoże polityczne? Cośty. Po prostu korzystają ze źródeł, które tak to napisały. No wiesz, nie wiadomo kto te testy układa. Kurka zarzuciłabym linkami, ale CKE nie upublicznia a fora co upubliczniają wymagają rejestracji.
Dobrze uargumentować użycie "goto"?
Hmmm... Cóż, jedyny argument jakiego mógłbym ewentualnie użyć w stosunku do zarzutu, iż zaniżam poziom języka wysokiego poziomu to "bo lubię prostotę".
Co do goto - jestem za goto (jak pewnie większość) jeśli sprawia że kod staje się czytelniejszy.
;D
@MSM
O ja, COME FROM od dzisiaj jest moim ulubionym mechanizmem, szczególnie COME FROM warunkowe ;D
Hmm, przypomina to trochę mechanizm hooków btw. Trochę bardzo ;)
W symfonii C++ standard (wydanie jednotomowe) jest napisane, że goto należy używać w szczególnych przypadkach i jest podany przykład wychodzenia z zagnieżdżonych pętli.
Wcześniej jest napisane wytłuszczonym drukiem, że używanie goto zdradza, że człowiek jest złym programistą i chyba większość ludzi właśnie tylko do tego momentu doczytała ten podrozdział(?).
@Gynvael Coldwind
:D
Na początek powiem, że bardzo rzadko zaglądam do dokumentacji kompilatora, więc rozszerzenia nie są mi znane. Jednak to, że dodawane są nowości oznacza, że język C się rozwija.
Stałe binarne, ok może to się przydać, ale ja przywykłem już do zamiany na hex. Chociaż przy eksperymentach można będzie szybko zmieniać stałą, a nie liczyć od nowa.
Co do zmiennych w wątkach się nie wypowiem.
Liczby stałoprzecinkowe i floaty z podstawą 10. Ostatnio przy zabawie z fraktalami zastanawiałem się nad floatami, ale chyba nic konkretnego nie wpadło mi do głowy. Chyba to będzie implementacja programowa gdyż nie jestem pewien jak koprocesor to przełknie. Jaki jest zakres tych liczb i jakie mają zastosowania? Zapodajcie jakimiś linkami z matematycznego punktu widzenia
Do pętli to oczywiście trzeba coś zmienić, jak tak patrzę to chyba bloki kodu z nazwami są dobre. Ale ifa nie rozumiem, chyba podałeś zbyt prosty przykład. Stosowałem ogromne bloki if zagnieżdżone kilka razy i nie miałem takich problemów. Zawsze pomaga narysowanie drzewa. (programiści lubią drzewa)
if(a) {
some_code;
if(!b) {
some_code;
}
}
Ale do tego można stosować też goto. Mam wrażenie, że z goto jest jak ze zmiennymi globalnymi, na uczelni powtarzają, aby stosować zmienne lokalne, więc ja się pytam po co są zmienne globalne jeżeli nie mogę ich użyć. Btw: W książce UNIX programowanie usług sieciowych pierwszy raz zobaczyłem kod z goto, co trochę mnie zdezorientowało; normalnie używa się ifa dla ominięcia kodu w przypadku błędu.
Btw. kompilator pascala był pisany w pascalu i ręcznie tłumaczony na assembler. Mimo trudności dobry programista zawsze da rade. No ale jeżeli gcc zaczęło stosować lukier programistyczny wykorzystując C++?...
kto by tego używał? chyba tylko rekreacyjnie.
większość ludzi (zwłaszcza tych co nigdy nie używają goto) nie wie np. że jak się ma "int *a, b;" to "a[b]" działa tak samo jak "b[a]" - myślą, że b[a] nie ma sensu... tak samo goto - myślą że nie ma sensu, a jednak po coś ono tam jest :)
dla kogoś kto go nie używa na pewno sensu nie ma (co najwyżej taki że go wnerwia)
ale dla kogoś komu się bez ogródek goto zdarza, jakiś tam sens jednak ma :)
Jakoś w kompie przed OSami musiał się znaleźć nie?
Hmm, czyli napisane jest 'goto jest złe, ale można je używać do tego, tego i tego' tak?
Mógłbyś napisać, co oprócz wyskakiwania z kilku pętli jest tam wymienione jako 'dozwolone użycie goto'?
@nikt_mnie_nie_zna
Co do floatów radix10 i stałoprzecinkowyc, to stawiam że jest to implementacja software'owa, przynajmniej na x86 - FPU x86 wspiera tylko radix 2, a obsługi stałoprzecinkowych liczb brak (chociaż je jest bardzo łatwo zapisać jako kilka operacji na intach).
Hmm, trochę ironią losu jest to, że kompilator dostaje teraz wsparcie dla liczb stałoprzecinkowych, kiedy ono najbardziej by się przydało 15 lat temu jak FPU było na oddzielnym chipie i do tego bardzo wolne, więc programiści często pisali obsługę liczb stałoprzecinkowych, bo po prostu była szybsza. Teraz ta różnica szybkości chyba taka wielka nie będzie.
Co do Twoich pytań odnośnie zasięgu, to jeśli chodzi o float radix 10, to sporo info podane jest w standardzie który jest implementowany, czyli:
www.open-std.org/Jtc1/sc22/wg14/www/docs/n1176.pdf
Powyższy draft powołuje się na IEEE-754R aka IEEE-754-2008, który jest całkiem znośnie streszczony na wiki: http://en.wikipedia.org/wiki/IEEE_floating-point_standard
A co do fixed-point, to polecam
http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Fixed_002dPoint.html#Fixed_002dPoint
http://en.wikipedia.org/wiki/Fixed-point_arithmetic
www.open-std.org/jtc1/sc22/wg14/www/docs/n1005.pdf (standard, warto rzucić okiem)
Co do matematycznego punktu widzenia, to nie mam żadnego linka pod ręką. Jak byś coś znalazł to wklej w komentarz plz. Może coś skrobnę o tym w wolnej chwili...
Co do if'ów, to imo przykład jest dobry.
Nie chodzi o to, że miałem jakieś kłopoty z if'ami or sth (hehehe po 20 latach programowania raczej się problemów z ifami nie ma ;p) tylko o to potencjalne zmniejszenie ilości zagłębień w kodzie oraz uproszczenie samego kodu i jego konstrukcji :)
O to samo chodzi z goto w przypadku obsługi błędów - o uniknięcie niepotrzebnych zagłębień kodu.
Co do kompilatora pascala, nie wiedziałem o tym, fajna ciekawostka ;)
"No ale jeżeli gcc zaczęło stosować lukier programistyczny wykorzystując C++?" - nie za bardzo rozumiem tą wypowiedź, czy mógłbyś ją rozwinąć?
@beem
Nie do końca się z Tobą zgodzę co do rozszerzeń kompilatora.
Jeśli się pisze kod dla siebie lub np. programy które będą kompilowane tylko na jednym kompilatorze, to nie widzę nic złego w używaniu rozszerzeń kompilatora - po to są, żeby je używać.
Poza tym, różne kompilatory (mam tu na myśli VC++ i GCC głównie) mają sporo pokrywających się rozszerzeń, czasami różniących się np. w keywordach (żeby daleko nie szukać __thread vs __declspec(thread)), więc można np. skorzystać/stworzyć zestaw makr który zunifikuje keywordy, np.
#ifdef _MSC_VER
# define __thread __declspec(thread)
#endif
Oczywiście, jeśli uczestniczy się w dużym projekcie którego leaderzy zdefiniowali pewien standard kodu (np. zabraniający używać rozszerzeń z jakiś tam przyczyn), to zupełnie inna sprawa.
Z ciekawości, czy umiesz podać jakiś przykład kiedy pisanie b[a] ma jakiś logiczny sens, inny niż zaciemnienie kodu? ;)
@piatkosia
Ah, chyba muszę o tym napisać jakiś post, bo słysze to pytanie dość często ;)
Więc... pierwszego assemblera w cale nie musiało być ;)
Zauważ, że CPU z wbudowanym readerem kart perforowanych wystarcza.
Na takiej karcie masz np. kilka zestawów miejsc do podziurkowania, np.
1. dziurka przy instrukcji: MOV, ADD, SUB, INC, DEC
2. dziurka przy numerze rejestru pierwszego argumentu / zmiennej (np tabela 16 x 16, dziurke robisz w jakimś miejscu i x + y * 16 reprezentuje liczbe lub numer rejestru)
3. drugi taki sam zestaw jak 2. dla drugiego argumentu
No i potem czysta elektronika sprawdza które dziurki są wybite i albo od razu wykonuje to na CPU, albo wpisuje rozkaz do pamięci - na tym poziomie abstrakcji wszelki firmware (i OSy) jest w zasadzie zbędny, można spokojnie elektroniką to zaimplementować.
Mając coś takiego, taki hardware'owy assembler czy nawet interpreter, możesz spokojnie stworzyć software'owy kompilator jakiegoś prostego języka (np assembly znowy) w którym instrukcje będziesz już wpisywać z klawiatury :)
crackerzy z dziurkaczami to jednak nie mit osób interesujących się historią IT:P
Generalnie tak.
Szczególne przypadki to wyskakiwanie z wielokrotnych zagnieżdżeń().
@fani goto :)
Ja sobie innych zastosowań nieutrudniających szybkiego czytania kodu ze zrozumieniem nie umiem wyobrazić(chociaż pewnie ze 2 są).
Dla mnie nadużywanie goto jest jak "rozsiewanie" zmiennych czy pisanie wszytkiego w jednej linijce bo przecież można.
Hacking dziurkaczem rulz! Niezly pomysl na compo :DDD
@Karton
Goto ma jedno świetne zastosowanie w C - przeskoczenie do kodu obsługującego błąd po jego wystąpieniu (podobne działanie maja throw w C++):
int func(void) {
FILE *a = NULL, *b = NULL, *c = NULL;
a = fopen("plik.1", "r");
if(!a) goto err;
b = fopen("plik.2", "r");
if(!b) goto err;
c = fopen("plik.3", "r");
if(!c) goto err;
/* do sth */
fclose(a); fclose(b); fclose(c);
return 0; // All OK
err:
if(a) fclose(a);
if(b) fclose(b);
if(c) fclose(c);
return 1; // Error
}
Zapisanie powyższego kodu w C bez użycia goto będzie trochę bardziej kłopotliwe, szczególnie jeśli resource'ów które trzeba zwolnić w razie błędu się jeszcze trochę pojawi (np. jakiś malloc, jakieś połączenia sieciowe, albo uchwyt do potoku).
Myślałem o C++, ale spoko, jedno z dwóch zastosowań poznałem :)
Co do Pascala: info zaczerpnięte z książki Kompilatory - Reguły, metody, narzędzia. Niestety książka nie jest już w sprzedarzy.
"Według Wirtha [1971] Pascal był pierwszym językiem, którego kompilator napisano właśnie w Pascalu. Kompilator ten był "ręcznie", bez żadnych optymalizacji, przetłumaczony na jeden z niskopoziomowych języków i rozumiał tylko podzbiór (> 60 procent) Pascala. Po kilku krokach wciągania otrzymano kompilator dla całego Pascala. (...) "
Zachęcam do przeczytania książki, warto wiedzieć jak powstał pierwszy kompilator. Jest bardzo ciężka, zabierałem się do niej 3 razy, ale naprawdę warto.
Asembler można napisać w hexedytorze. Poprostu tłumaczy on mnemoniki na kod, nie ma makr i innych wysokopoziomowych pomocy. Tylko teraz IA-32 ma obszerny kod maszynowy.
Kiedyś coś czytałem o 'prawdziwych programistach' i tam był opisany facet, który wgrał do komputera własnej konstrukcji, system operacyjny również własny, z pamięci za pomocą klawiatury szesnastkowej. Do tej pory zastanawiam się czy to prawda, nie mogę znaleźć więcej info.
Co do jakości, czytelności, optymalności kodu zastanawiam się nad tym chyba już od miesiąca. Moje przemyślenia umieszczę na 4programmers.net tu jest 'niewygodnie'.
Mhm ;)
@nikt_mnie_nie_zna
Mhm, thx ;>
Thx również za cytat ;)
Co do assemblera i klawiatury szesnastkowej, to kiedyś (tj. jak jeszcze na chleb mówiłem 'bep', a na muchy 'tapty') widziałem urządzenie - programator assemblera.
Sprawa wyglądała tak, że urządzenie mialo klawiaturę z wszystkimi rozkazami assemblera (a za dużo tego nie było, jakiś prosty RISC) + klawiaturę numeryczną do wprowadzania wartości + strzalki gora/dol do zmiany adresu na którym się operuje (tj. adresu gdzie instrukcja zostania / została zakodowana).
Dodatkowo, każdy klawisz był podświetlany jeśli pod danym adresem pamięci była zapisana dana instrukcja (np. jeśli miało się wybrany adres 0x124 i był tam LD r1, to podświetlony był klawisz LD, a na wyświetlaczu segmentowym pokazane było '1').
Program zapisywał się na EEPROMie albo czymś podobnym.
Więc było to zarówno urządzenie do tworzenia nowych programów, jak i edycji starych czy też też ich analizy.
I to to nie miało ani monitora ani normalnej klawiatury ;DD
Swoją drogą, podobną klawiaturę miał chyba Spectrum, tj. rozkazy BASICa dzieliły klawisze z literkami. http://en.wikipedia.org/wiki/File:ZXSpectrum48k.jpg
long int operator"b"(const char * $, size_t n)
{
assert(!( n < (sizeof(long int)*8)));
long int out = 0x00;
unsigned char i = 0;
while(n-->0)
{
assert(($[n] != "0") and ($[n] != "1"));
out | = ($[n] == "1"? 0x01 or 0x00)<<(i++);
}
return out;
}
auto x = 0101011010b;
;)? Kod i tak nie działa, ale ważna idea.
#define forever if (__STOP__) __STOP__ = false; else while(true)
#define while(cond) if (__STOP__) __STOP__ = false; else while(cond)
#define for(cond) if (__STOP__) __STOP__ = false; else for(cond)
#define label(name) __STOP__ = false;
name:
#define break(name) { __STOP__ = true; goto name; }
label(petla) for(int i = 0; i < 100; ++i) {
for (int n = 0; n < 5; ++n) {
if (i > 10)
break(petla);
}
}
---
W VS2010 to działa choć założę się, że zaraz ktoś podrzuci przykład dla którego to nie zadziała ;P
#define label(name) if (false) {
name:
break;
} else
#define break(name) goto name;
for(int i = 0; i < 100; ++i) label(petla) {
std::cout << i << "
";
for (int n = 0; n < 5; ++n) {
if (i > 10)
break(petla);
}
}
Ahaha ale makro wymyśliłeś :)
Przetestowałem, działa ;)
W zasadzie problem jest jeden: VC++ z /W4 będzie się rzucał o if(false) (warning C4127: conditional expression is constant).
Anyway, fajny workaround (mimo iż defacto jest to goto zakamuflowane ;>), szczególnie podoba mi się w jaki sposób nazwe pętli wrzuciłeś przed blok kodu :)
Myślę, że dałoby się makra z drugiego rozwiązania całkiem przyjemnie używać. Ale w nieco rozbudowanej wersji tego kodu nawet przy /W3 pełno ostrzeżeń wyskakuje przez nieużyte etykiety :-/. Nie mam pomysłu jak zrobić makro dla IFów bez tworzenia dodatkowych zmiennych globalnych i oddzielnych makr. Gdyby to się udało można by używać tylko makra label. Fajna zagwozdka nad która bym pomyślał gdyby nie późna godzina ;-)
#define label(name) if(0) {
__break_##name:
break;
__continue_##name:
continue; /* w zasadzie zbedna linijka ale ladnie wyglada ;) */
} else
#define break(name) goto __break_##name;
#define continue(name) goto __continue_##name;
Add a comment: