C++ och dess framtid att programmera minnessäkert - Hur går utvecklingen?

Permalänk
Medlem
Skrivet av Yoshman:

Öh, med builder-pattern går det ju utmärkt att ha en default-ctor + ett gäng metoder. Och det går hur bra som helst att göra i Rust!

Låt säga att du jobbar i en större kodmassa och har några generella objekt, om du vill byta ut något av dessa objekt som används frekvent i koden. Hur gör du?

EDIT: Det här kanske till och med är svar på gåtan varför så många utvecklare i rust vill skriva om kod till rust. På andra ställen skämtas det om att rust utvecklare älskar och skriva om kod till rust. Något märkligt beteende.
Språket är för oflexibelt för att skriva egna större projekt, fel i koden blir för jobbiga att refaktorera. Då är det enklare att skriva om något där designen är gjord och fungerar.

Permalänk
Medlem
Skrivet av klk:

Ett till och nu tänker jag såga Rust vid fotknölarna.

Du sågar väl som vanligt av dig själv vid fotknölarna ...

Skrivet av klk:

> Performance: kommentar: Min tolkning av rust är att rust skall likna "enkla" språk men få till snabbheten i C++

Jag vet inte vad du menar med enkla språk men jag skulle uppfatta att Rust och C++ är ungefär samma användarkomplexitet medans Rust är mer genomtänkt med tanke på att man lämnar garantier kring minnessäkerhet och UB.

Skrivet av klk:

> Reliability: kommentar: Det här tolkar jag som är det övergripande, kommer i första rum. Allt annat får stå tillbaka om det inte är säkert

Nja, du får det som att låta som att man inte kan prioritera multipla saker. Men visst om det står mellan att få något lite sämre prestanda i något enstaka fall mot att introducera UB så kommer man självklart att inte introducera UB.

Skrivet av klk:

> Productivity: kommentar: ALLA språk är designade efter produktivitet, gäller bara att förstå vilken för olika programmerare har olika krav och det varierar både med kunskap och vilken typ av mjukvara som görs

Nej alla språk är inte designade för produktivitet, det finns en hel kategori med språk som är konstprojekt eller skämt. Sedan produktivitet i detta fallet kan vara att man exempelvis inte behöver extra kod för att explicit allokera/frigöra minne och att många saker dom debug-printing, jämförelse osv kan härledas från det starka typsystemet.

Skrivet av klk:

Beskriver två områden som enligt mig är mer eller mindre katastrof i designen, de måste ändra om språket skall kunna växa till bredare skaror.

Du brukar inte ha koll på läget så jag tror Rust klarar sig bra utan att ändra något mer än att reducera tröskeln för att locka fler användare.

Skrivet av klk:

Jämför man C++ och Rust så är C++ designat efter principer. En sådan princip är att alla objekt har konstruktör och destruktion. Det viktiga är principen, att språket följer fasta regler. Även om C++ har mycket högt fokus på prestanda så är principerna det viktiga.
Att införa hack i språket är omöjligt, knappt någon C++ programmerare hade accepterat det.

Svammel... Vad jag vet så heter det "konstruktor" och "destruktor". Nu kan jag inte C++ men om en av design principerna är att classer ska ha en konstruktor och en destruktor så låter det som en 14åring skrivit design principerna.

Skrivet av klk:

Rust har inte samma principer efter vad jag lyckats lista ut. Sökt en del efter jag tittade på genomgången för att kolla upp saker som enligt mig var underliga. Det blev inte mindre underligt efter jag läst. Kan ha missuppfattat men då blir jag nog rättad snabbt här i tråden

I rust heter destruktionsobjektet Drop. Så fort man behöver destruera på ett annorlunda sätt behöver denna Drop metod implementeras. Och till det lägger man till en trait (ett beteende), i rust kan man lägga till en hel hög med beteenden (traits).
Så var i ligger problemet? Problemet är att det inte är en princip, ett fast mönster som går igen.
Skillnaden kan tyckas liten men när saker växer så behöver man i rust känna till mer om koden för att veta medan det i C++ gäller allt. Eftersom rust inte har arv och andra mer avancerad språkhantering kanske de inte tänker på principen på samma sätt som C++.
C++ är mer genomtänkt för jag tolkar metoden Drop som ett hack.

Du har som vanligt fel. traits kan väl lättast att beskriva som interface. Drop kan du se som ett interface om man vill lägga till manuell hantering när objektet går ur scope. I de flesta fallen behöver du inte bry dig om Drop eftersom alla typer i standard lib:et redan implementerar detta. Så nej, Drop är inte ett hack.

Styrkan med traits är att du kan implementera traits för objekt som du inte deklarerat.

Skrivet av klk:

Rust har ingen konstruktor utan även där en metod som heter new. Inte lika snyggt men kanske hanterbart tills man lär sig att Rust inte kan överlagra metoder.

Korrekt Rust har ingen konstruktor, precis som C och new() är inte en metod utan en funktion i objekt-namespacet som returnerar en objektinstans.

Skrivet av klk:

Rust måste lägga till att överlagra metoder annars kommer det aldrig bli mer än ett språk för en liten klick. Det tog jag för en självklarhet så inte ens letat, råkade hitta det nu när jag försökte förstå hur objekt skapas och förstörs.

Varför? Jag har personligen inte stött på ett scenario där jag känt att jag vill överlagra något ...

Skrivet av klk:

Istället för att som i C++ ha flera olika konstruktörer används en teknik kallad "builder pattern", liknar message chainging. Det här är för mig direkt i soptunnan. Om jag använder objekt och det är massa extra kod i annan kod för hur objektet skapas upp, det blir en mardröm när man skall designa om eller refaktorera koden.

Varför? Det låter som mer jobb att ha redundant kod i multipla konstruktorer.

Skrivet av klk:

Att Rust inte kan överlagra metoder gör också att jag förstår bättre varför de de standardiserade objektet har så många metoder och onödigt långa namn, enda sättet att sära på metoder är med namn och då blir namnen längre.

Det låter som att du inte förstått varför det är långa metodnamn. En anledning är konventioner som as_*, to_*, into_*.
https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc...

Ge gärna exempel på Rust-metoder med långanamn enl. dig.

Skrivet av klk:

Skapas objekt i C++ och det exempelvis har en metod kallad "add". det läggs till saker. Då behöver jag bara veta den metoden och sedan avgör argumenten av olika överlagrade "add" metoder vad som görs. C++ har stort fokus på data och för att läsa C++ kod så behöver man först läsa namnet och koppla det till argument. Minnet är helt centralt när C/C++ utvecklare skriver kod.

Än en gång förstår jag inte fixeringen med överlagring. I Rust så hade jag implementerat traitet std::ops::Add och sedan hade jag kunnat göra "my_obj += 1" eller "my_obj.add(1)".

Skrivet av klk:

Jag tror inte minnet är det för Rust, utvecklare i Rust är språkbegåvade.

Du tror mycket som vanligt...

Permalänk
Medlem

@orp Vad är det svåraste du kodat? Kan du visa lite kod skriven av dig

Permalänk
Medlem
Skrivet av klk:

@orp Vad är det svåraste du kodat? Kan du visa lite kod skriven av dig

Varför är du så fixerad över vad jag kodar?

Att du kommer med märkliga påstående, fördomar, missförstånd och inte kan betydelsen av vanliga begrepp som du använder har inget med min kod att göra.

Permalänk
Medlem
Skrivet av orp:

Varför är du så fixerad över vad jag kodar?

För att du och jag har så diametralt olika åsikter så jag är nyfiken, vad jag än skrivit har jag tolkat dig tycka allt är fel. Därför hade det varit mycket intressant att se kod från dig.

Personligen så tycker jag sådant är bra, att att se kod där tänket skiljer starkt från vad man själv gör är lärorikt och man får testa sin egna bubbla.

Permalänk
Medlem
Skrivet av klk:

För att du och jag har så diametralt olika åsikter så jag är nyfiken, vad jag än skrivit har jag tolkat dig tycka allt är fel. Därför hade det varit mycket intressant att se kod från dig.

Personligen så tycker jag sådant är bra, att att se kod där tänket skiljer starkt från vad man själv gör är lärorikt och man får testa sin egna bubbla.

Det är för att mycket av det du säger är direkt felaktigt.

Jag tror att våra meningar går isär eftersom jag upplevt scenarion som direkt dementerar många av dina påståenden. Det känns som att du lever i en bubbla och inte försöker utforska något utanför din bubbla eftersom du är rädd för att det skulle ändra din världsbild.

Som att grupper folk som Python-utvecklare eller C++-utvecklare. Alla utvecklare som jag känner skriver åtminstone 3 språk, dvs för mig finns inte sådana grupperingar.

Som att kalla Drop ett hack. Vad jag kan tolka så är du förvirrad kring Drop och traits och istället för att erkänna det för dig själv och sedan fråga, kanske skriva något i Rust och när du har bättre förståelse komma med ett sådant påstående och kunna underbygga varför du upplever det som ett hack så slänger du ur dig saker efter 5 minuters Googlande eller som i fallet med recession testing inte ens läst länkarna som du postat...

Permalänk
Medlem
Skrivet av orp:

Det känns som att du lever i en bubbla och inte försöker utforska något utanför din bubbla eftersom du är rädd för att det skulle ändra din världsbild.

Men jag är inte rädd att visa kod (vilket jag har anledningar till), då för att få lite mer substans från dig hade det varit bra med lite kött på benen

Permalänk
Medlem
Skrivet av orp:

Det är för att mycket av det du säger är direkt felaktigt.

En till för det här begriper inte jag. Vad är rätt och fel inom programmering?
Det fantastiska med programmering är att det är skapande, man får tänka till och lösa uppgifter på bästa möjliga sätt. Finns inga fel så länge man klarar att lösa uppgifterna. Däremot kan man lösa saker på bättre eller sämre sätt och vad som kan vara bra för någon är mindre bra för någon annan, beror på vad man själv har bäst kunskaper inom.

Förutom det så om någon beskriver lösningsförslag som är annorlunda mot vad man själv gör, borde inte det locka till intresse. Sådant tycker i alla fall jag är superintressant. Givetvis får man granska och det det är sällan det är lika intressant efter granskning men bara att någon kommer med nytt är bra.

Permalänk
Datavetare
Skrivet av klk:

Det är inte kritiskt med C++, XP används i olika former av nästan alla. Men det finns bättre och sämre sätt att hantera kod på ett flexibelt sätt. Poängen är ändå att ta sig från A till B och det på ett så bra sätt som möjligt.

Håller med. Men ser inte varför något i Rust skulle förhindra det.

En ett exempel på något som snarare gör att man lättare tar sig "snabbt fram" med Rust jämfört med många andra språk är saker som att "arithmetic overflow" ger notis i _debug byggen_ i Rust (men inte i Release för att inte påverka prestanda).

Skrivet av klk:

Ja eller kanske mer att Rust hjälper programmerare med att förstå riskerna med minne samtidigt som språket också förhindrar att utsätta sig för risker. Förståelse för minne kan säkert uppnås men inte vana att jobba med minne och det tror jag ogillas bland många i C och C++.
Att det går skriva OS i rust är en sak, tror inte det kommer göras.

Det är "opt-in" att stänga av alla former av rail-guards. När man skriver drivers och/eller kanske använder Rust i microkontrollers så behöver man ibland stänga av "safe Rust". Men att göra det är ju en standardfunktion, och ser man diskussionen i C++ lägret kring bl.a. minnessäkerhet har man även där kommit till insikt att säkerhet "by default, med out-out vid behov" är den vettigaste designen.

Skrivet av klk:

XP är luddigt och TDD är bara en rekommendation. Utan att kunna bevisa det skulle det inte förvåna att XP med TDD kommer från java kollektivet. Java har som jag tidigare nämnt förstört massor. Har aldrig och kommer aldrig jobba i ett projekt med som bygger på TDD men några vänner har jobbat med personer som försökt bygga program med TDD och de vart aldrig klara. Det skrivs test efter test utan att komma någonvart. Därmed inte sagt att det inte går men TDD är en fantastisk metod om man vill göra det svårt för sig själv.
Det fungerar möjligen i språk likt java och C# eftersom det inte är meningen att utvecklare där skall hitta på egna lösningar. Fokus är att använda deras respektive runtime bibliotek.

Har använt TDD i C#, Go, Rust, C++ och även C. Det fall som varit mest "coolt" är så här långt i C där vi jobbande i månader med ett projekt där vi hela tiden höll 100 % test-coverage.

Var rätt häftigt att se en komplett TCP/IP-stack köra en fullt fungerande TCP-implementation första gången den placerades i ett "riktigt system". Så nära 100k rader C som aldrig kört något annat än tester där flera personer jobbade parallellt fungerade på första försöket. Var det buggfritt? Knappast, men det var tillräckligt väl-testat via TDD för att ändå fungera helt OK på första försöket.

Här ser man också skillnad i de "nya" språken. Både Rust och framförallt Go har TDD integrerat i standard tool chain (Go har även mikrobenchmarks där, Rust kräver likt C++, C# m.fl. 3:e-parts bibliotek men där det finns "de-facto standarder").

Skrivet av klk:

C++ kan du med lite kreativitet göra så mycket mer.

Ge ett enda konkret exempel!

Skrivet av klk:

Problemet med Rust är att du måste lära dig syntaxen, det behöver du inte i C++. De som inte skriver den svåra koden i C++ projekt behöver heller inte lära sig detaljer i språket. Men projekten behöver ha minst en person som kan C++ på djupet för annars är C++ troligen det bästa språket att välja om projektet skall krascha så snabbt som möjligt. Någon måste kunna köra bilen för att ta en liknelse.

Är helt med på att väldigt få tragglar med template-meta-programming eller ens gör egna containers eller liknande. Men det gäller oavsett om det är C++, Rust, C# eller vad det nu är.

Och precis som i C++ hittar man ofta de mest komplexa delarna i Rust om man behöver ge sig på sådana fall. Annars kan det, likt C++, vara långt enklare och det är nog normalfallet.

Men ge gärna något konkret exempel där det är väsentligt enklare i C++ jämfört med motsvarande i Rust.

Skrivet av klk:

Nu jämför du äpplen och päron. C++ kod skrivs inte som den görs i Rust om man använder språket på rätt sätt.

Vänta här, var inte det där rätt mycket "hur man bör skriva modern C++20/23 kod"? Saker som iteratorer är inte super-vanligt att implementera i C++, men det är inte super-vanligt någonstans. Har ändå själv haft behov av detta i alla icke-triviala projekt och det suger i C++ då det är betydligt mer komplext än i stort sätt alla andra språk!

Skrivet av klk:

Låt säga att du jobbar i en större kodmassa och har några generella objekt, om du vill byta ut något av dessa objekt som används frekvent i koden. Hur gör du?

EDIT: Det här kanske till och med är svar på gåtan varför så många utvecklare i rust vill skriva om kod till rust. På andra ställen skämtas det om att rust utvecklare älskar och skriva om kod till rust. Något märkligt beteende.
Språket är för oflexibelt för att skriva egna större projekt, fel i koden blir för jobbiga att refaktorera. Då är det enklare att skriva om något där designen är gjord och fungerar.

Är överhuvudtaget inte med på vad skillnaden skulle ligga här.

Vill man ha den frihetsgraden måste man så långt som möjligt "programmera mot ett interface, inte en implementation". Det är traits i Rust och "pure abstract base-classes" i C++ (den senare har rätt rejält mycket märkligare syntax...).

Vidare har det inget med builder-pattern. Den är i princip en separation av konstruktion i två delar. Först bygger man upp något form av "config-object" som sedan används för att skapa en instans med det specificerade tillståndet.

Kodmässigt ser detta själva "builder-pattern" delen väldigt lika ut i Rust och C++. Att jobba mot ett interface ser lite olika ut, men det är absolut helt väldefininerat i Rust via "impl Interface for ConcreteType" konstruktionen.

Det skrivet: håller med om att Rust är komplext, men det är inte mer komplex jämfört med C++. Tyvärr är båda dessa rätt mycket de mest komplexa språken man hittar, vilket gör att de flesta nog bör välja något annat om man inte måste ha just miljöer med de unika egenskaper C, C++ och Rust råkar ha.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Yoshman:

Är överhuvudtaget inte med på vad skillnaden skulle ligga här.

kort svar (återkommer om det andra)
tog hjälp av grok så inte skrivit själv men sådan här kod är hemsk

let product = ProductBuilder::new() .id(1) .name("Laptop".to_string()) .price(999.99) .in_stock(true) .category("Electronics".to_string()) .build();

Lätt för van utvecklare att identifiera och hittas sådant så vet man att de har problem, utvecklingen kommer gå mycket långsamt

Permalänk
Skrivet av klk:

Lätt för van utvecklare att identifiera och hittas sådant så vet man att de har problem, utvecklingen kommer gå mycket långsamt

So, what's your problem?

Det är för dig ett ovant sätt att uttrycka initieringen, men om kompilatorn inline-expanderar allting blir det precis lika effektivt som ett inline-expanderat constructor-anrop. Alla medlemmar skall tilldelas "sitt" argument, känns det bättre om allting går i ett anrop än om det är olika anrop som initierar olika delar? Det är mer att skriva, men det kan väl inte du ha några problem med som tycker att man hellre skall skriva egen kod än att använda sig av färdig kod i bibliotek?

Permalänk
Medlem
Skrivet av klk:

Men jag är inte rädd att visa kod (vilket jag har anledningar till), då för att få lite mer substans från dig hade det varit bra med lite kött på benen

Jag är inte heller rädd för att visa kod men det fyller inget syfte i diskussionen. Du delar kod om saker som inte varit relevanta precis som du försöker briljera men i själva fallet framstår det enbart som korkat eftersom vi framförallt diskuterar språkaspekter och inte implementationsdetaljer. Kodexempel har varit relevanta vid diskussioner när du hävdat ineffektivitet och Yoshman gått igenom instruktionerna för att motbevisa felaktiga påstående.

Inlägget som vi pratar om var missförstånd om vad traits är och missförstånd gällande Drop-traitet. Du påstår även att builder-pattern åker i soporna utan någon direkt motivering och jag frågar varför? Så hur tillför kod från något av mina hobbyprojekt till detta? Mitt huvudsakliga hobbyprojekt är skrivet i Erlang, hur bidrar den koden till dina missförstånd om Rust?

Permalänk
Medlem
Skrivet av orp:

Jag är inte heller rädd för att visa kod men det fyller inget syfte i diskussionen. Du delar kod om saker som inte varit relevanta precis som du försöker briljera men i själva fallet framstår det enbart som korkat eftersom vi framförallt diskuterar språkaspekter och inte implementationsdetaljer.

Och det här är så svårt, hur skall jag förklara utan att andra i tråden tolkar det som att jag försöker briljera?

Permalänk
Skrivet av klk:

Ett till och nu tänker jag såga Rust vid fotknölarna.
<Lång text där du förklarar grundläggande skillnader mellan Rust och C++ och gör massa antaganden om människor som tänker helt olikt dig>

Ja, Rust och C++ är olika språk. Rust är gjort för att vara mer explicit som i många fall kan leda till mer kod. Varken explicit eller implicit kod är "bättre". Generellt sett kan man nog säga att implicit kod är lättare att skriva medan explicit kod är lättare att läsa.

Det finns annan funktionalitet som Rust har men som C++ saknar som t.ex. tagged enums, pattern matching och procedural macros. Så det som händer i praktiken är att viss kod blir mer verbos i Rust medan annan kod blir mindre verbos. Rust är t.ex. extremt bra på att skriva ett CLI som behöver parsa och omvandla massa data.

Allt detta skulle du förstå mycket bättre om du skrev lite kod i Rust. Vi är nog många i tråden som har erfarenhet från både C++ och Rust. Det är frustrerande att du hela tiden sitter och spekulerar om saker du inte känner till

Permalänk
Medlem
Skrivet av Ingetledigtnamn:

So, what's your problem?

Kallas för cuopling och är bland det värre felet en utvecklare kan göra

Permalänk
Medlem
Skrivet av klk:

En till för det här begriper inte jag. Vad är rätt och fel inom programmering?
Det fantastiska med programmering är att det är skapande, man får tänka till och lösa uppgifter på bästa möjliga sätt. Finns inga fel så länge man klarar att lösa uppgifterna.

Bob Ross nu får du lugna dig (referens "No wrongs only happy little accidents")... Du slänger ur dig påstående om allt möjligt bananas som att säga att Python är ett deklarativt språk, Rust-utvecklare bryr sig inte om minnet osv.

Skrivet av klk:

Förutom det så om någon beskriver lösningsförslag som är annorlunda mot vad man själv gör, borde inte det locka till intresse. Sådant tycker i alla fall jag är superintressant. Givetvis får man granska och det det är sällan det är lika intressant efter granskning men bara att någon kommer med nytt är bra.

Det är intressant att se andras lösningsförslag. @Yoshman Advent of code i Rust var väldigt lärorik för mig som inte kodat mängder med Rust. Jag känner dock inte att generella implementationsdiskussioner hör hemma i denna tråden.

Permalänk
Medlem
Skrivet av orp:

Det är intressant att se andras lösningsförslag.

Håller med och därför frågar jag igen, kan du inte visa lite exempel för du har åsikter om allt jag skriver.
Vet du så mycket tycker jag det är dags att visa färg

Permalänk
Medlem
Skrivet av klk:

Håller med och därför frågar jag igen, kan du inte visa lite exempel för du har åsikter om allt jag skriver.
Vet du så mycket tycker jag det är dags att visa färg

Du läste inte mitt förra meddelande ...

Om du tycker att det är givande så ha det så skoj(implementation av bittorrents peer wire-protokoll)

-module(ertorrent_peer_proto). -export([msg_handshake/2, msg_keep_alive/0, msg_choke/0, msg_unchoke/0, msg_interested/0, msg_not_interested/0, msg_have/1, msg_bitfield/2, msg_request/3, msg_piece/4, msg_cancel/3, msg_port/1]). -export([ send_keep_alive/2, send_message/2, send_bitfield/2, send_handshake/3, send_interested/1, send_unchoke/1 ]). -include_lib("eunit/include/eunit.hrl"). -define(CHOKE, 0). -define(UNCHOKE, 1). -define(INTERESTED, 2). -define(NOT_INTERESTED, 3). -define(HAVE, 4). -define(BITFIELD, 5). -define(REQUEST, 6). -define(PIECE, 7). -define(CANCEL, 8). -define(PORT, 9). % Fast Extension % http://www.bittorrent.org/beps/bep_0006.html -define(SUGGEST_PIECE, 13). -define(HAVE_ALL, 14). -define(HAVE_NONE, 15). -define(REJECT_PIECE, 16). -define(ALLOWED_FAST, 17). -define(HANDSHAKE_MSG, <<19:8/integer, "BitTorrent protocol", 0:64/integer, Info_hash:160/bitstring, Peer_id:160/bitstring>>). -define(KEEP_ALIVE_MSG, <<0:32>>). -define(CHOKE_MSG, <<1:32/integer, ?CHOKE:8/integer>>). -define(UNCHOKE_MSG, <<1:32/integer, ?UNCHOKE:8/integer>>). -define(INTERESTED_MSG, <<1:32/integer, ?INTERESTED:8/integer>>). -define(NOT_INTERESTED_MSG, <<1:32/integer, ?NOT_INTERESTED:8/integer>>). -define(HAVE_MSG, <<5:32/integer, ?HAVE:8/integer, Piece_index:32/integer>>). -define(BITFIELD_MSG, <<Length:32/integer, ?BITFIELD:8/integer, Bitfield/bitstring>>). -define(REQUEST_MSG, <<13:32/integer, ?REQUEST:8/integer, Index:32/integer, Begin:32/integer, Length:32/integer>>). -define(PIECE_MSG, <<Length:32/integer, ?PIECE:8/integer, Index:32/integer, Begin:32/integer, Block/binary>>). -define(CANCEL_MSG, <<13:32/integer, ?CANCEL:8/integer, Index:32/integer, Begin:32/integer, Length:32/integer>>). -define(PORT_MSG, <<3:32/integer, ?PORT:8/integer, Listen_port:32/integer>>). -record('peer.msg.announce', { info_hash = <<>>, peer_id = <<>> }). -record('peer.msg.keep_alive', {}). -record('peer.msg.choke', {}). -record('peer.msg.unchoke', {}). -record('peer.msg.interested', {}). -record('peer.msg.not_interested', {}). -record('peer.msg.have', { piece_index :: integer() }). -record('peer.msg.bitfield', { length :: integer(), bitfield = <<>> } ). -record('peer.msg.request', { index :: integer(), begins :: integer(), length :: integer() }). -record('peer.msg.piece', { length :: integer(), index :: integer(), begins :: integer(), block = <<>> }). -record('peer.msg.cancel', { index :: integer(), begins :: integer(), length :: integer() }). -record('peer.msg.port', { port :: integer() }). msg_handshake(Info_hash, Peer_id) -> %Flags = 16#0004000000000000, {ok, ?HANDSHAKE_MSG}. msg_keep_alive() -> {ok, ?KEEP_ALIVE_MSG}. msg_choke() -> {ok, ?CHOKE_MSG}. msg_unchoke() -> {ok, ?UNCHOKE_MSG}. msg_interested() -> {ok, ?INTERESTED_MSG}. msg_not_interested() -> {ok, ?NOT_INTERESTED_MSG}. msg_have(Piece_index) -> {ok, ?HAVE_MSG}. msg_bitfield(Bitfield_length, Bitfield) when is_binary(Bitfield)-> lager:debug("~p: ~p: length '~p', bitfield '~p'", [?MODULE, ?FUNCTION_NAME, Bitfield_length, Bitfield]), Length = 1 + Bitfield_length, {ok, ?BITFIELD_MSG}. msg_request(Index, Begin, Length) -> {ok, ?REQUEST_MSG}. msg_piece(Block_size, Index, Begin, Block) -> Length = 9 + Block_size, {ok, ?PIECE_MSG}. msg_cancel(Index, Begin, Length) -> {ok, ?CANCEL_MSG}. msg_port(Listen_port) -> {ok, ?PORT_MSG}. send_keep_alive(Socket, Timer) -> TimerRef = erlang:send_after(Timer, self(), {keep_alive_internal_timeout}), {ok, Keep_alive} = msg_keep_alive(), case gen_tcp:send(Socket, Keep_alive) of ok -> {ok, TimerRef}; {error, Reason} -> erlang:cancel_timer(TimerRef), {error, Reason} end. send_message(Socket, {Type, Message}) -> case gen_tcp:send(Socket, Message) of ok -> ok; {error, Reason} -> lager:warning("~p: ~p: failed to send '~p' '~p', reason '~p'", [?MODULE, ?FUNCTION_NAME, Type, Message, Reason]), error end. send_bitfield(Socket, Bitfield) -> Bitfield_len = bit_size(Bitfield), {ok, Bitfield_msg} = msg_bitfield(Bitfield_len, Bitfield), send_message(Socket, {bitfield, Bitfield_msg}). send_handshake(Socket, Peer_id_str, Torrent_info_bin) -> % TODO Check if this is correct after peer_id change Peer_id_bin = list_to_binary(Peer_id_str), {ok, Handshake_msg} = msg_handshake(Torrent_info_bin, Peer_id_bin), send_message(Socket, {handshake, Handshake_msg}). send_interested(Socket) -> {ok, Interested_msg} = msg_interested(), send_message(Socket, {interested, Interested_msg}). send_unchoke(Socket) -> {ok, Unchoke_msg} = msg_unchoke(), send_message(Socket, {unchoke, Unchoke_msg}). parse_msg(?KEEP_ALIVE_MSG) -> {ok, #'peer.msg.keep_alive'{}}; parse_msg(?CHOKE_MSG) -> {ok, #'peer.msg.choke'{}}; parse_msg(?UNCHOKE_MSG) -> {ok, #'peer.msg.unchoke'{}}; parse_msg(?INTERESTED_MSG) -> {ok, #'peer.msg.interested'{}}; parse_msg(?NOT_INTERESTED_MSG) -> {ok, #'peer.msg.not_interested'{}}; parse_msg(?HAVE_MSG) -> {ok, #'peer.msg.have'{ piece_index = Piece_index }}; parse_msg(?REQUEST_MSG) -> {ok, #'peer.msg.request'{ index = Index, begins = Begin, length = Length }}; parse_msg(?PIECE_MSG) -> {ok, #'peer.msg.piece'{ length = Length, index = Index, begins = Begin, block = Block }}; parse_msg(?CANCEL_MSG) -> {ok, #'peer.msg.cancel'{ index = Index, begins = Begin, length = Length }}; parse_msg(?HANDSHAKE_MSG) -> {ok, #'peer.msg.announce'{ info_hash = Info_hash, peer_id = Peer_id }}; parse_msg(?BITFIELD_MSG) -> {ok, #'peer.msg.bitfield'{ length = Length, bitfield = Bitfield}}; parse_msg(?PORT_MSG) -> {ok, #'peer.msg.port'{ port = Listen_port }}. -ifdef(EUNIT). -define(TEST_INFO_HASH, <<"c12fe1c06bba254a9dc9">>). -define(TEST_PEER_ID, <<"ET001-abcdefghijklmn">>). handshake_test() -> {ok, Msg} = msg_handshake(?TEST_INFO_HASH, ?TEST_PEER_ID), {ok, #'peer.msg.announce'{info_hash = Info_hash, peer_id = Peer_id}} = parse_msg(Msg), ?assertEqual(?TEST_INFO_HASH, Info_hash), ?assertEqual(?TEST_PEER_ID, Peer_id). keep_alive_test() -> {ok, Msg} = msg_keep_alive(), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.keep_alive'{}, Parsed). choke_test() -> {ok, Msg} = msg_choke(), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.choke'{}, Parsed). unchoke_test() -> {ok, Msg} = msg_unchoke(), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.unchoke'{}, Parsed). interested_test() -> {ok, Msg} = msg_interested(), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.interested'{}, Parsed). not_interested_test() -> {ok, Msg} = msg_not_interested(), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.not_interested'{}, Parsed). have_test() -> {ok, Msg} = msg_have(42), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.have'{ piece_index = 42 }, Parsed). bitfield_test() -> Expected = <<1:1, 1:1, 1:1, 0:1, 0:1, 0:1, 0:1, 1:1>>, {ok, Msg} = msg_bitfield(binary:referenced_byte_size(Expected), Expected), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.bitfield'{ length = 2, bitfield = Expected }, Parsed). request_test() -> {ok, Msg} = msg_request(42, 4444, 5555), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.request'{ index = 42, begins = 4444, length = 5555 }, Parsed). piece_test() -> Expected = <<4:4, 2:4, 0:4, 1:4>>, {ok, Msg} = msg_piece(byte_size(Expected), 12, 4096, Expected), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.piece'{ length = 11, index = 12, begins = 4096, block = Expected }, Parsed). cancel_test() -> {ok, Msg} = msg_cancel(42, 4444, 5555), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.cancel'{ index = 42, begins = 4444, length = 5555 }, Parsed). port_test() -> {ok, Msg} = msg_port(424242), {ok, Parsed} = parse_msg(Msg), ?assertEqual(#'peer.msg.port'{ port = 424242 }, Parsed). -endif.

Dold text
Permalänk
Medlem
Skrivet av orp:

Du läste inte mitt förra meddelande ...

Om du tycker att det är givande så ha det så skoj(implementation av bittorrents peer wire protokoll)

Det är givande för man kan lära sig mycket om en utvecklare bara genom att läsa kod.

Fråga.

Du skriver metoder med kategori före och efter namnet, varför blanda

Exempel
msg_not_interested, msg_have, msg_cancel
send_interested, send_unchoke

Men för testen
piece_test, port_test, interested_test

Inte bekant med språket, så reservation för det. Ser däremot att koden är dragen hårt åt det deklarativa hållet och då har jag nog tolkat vad du gillar för så har jag uppfattat dig tidigare

Permalänk
Medlem
Skrivet av klk:

Det är givande för man kan lära sig mycket om en utvecklare bara genom att läsa kod.

Fråga.

Du skriver metoder med kategori före och efter namnet, varför blanda

Exempel
msg_not_interested, msg_have, msg_cancel
send_interested, send_unchoke

Men för testen
piece_test, port_test, interested_test

Inte bekant med språket, så reservation för det. Ser däremot att koden är dragen hårt åt det deklarativa hållet och då har jag nog tolkat vad du gillar för så har jag uppfattat dig tidigare

Logisk separation msg_* skapar ett binär meddelande send_* skickar det. Lite som att inte lägga allt i en konstruktor. *_test är unit test, det är förutbestämt av unit test-ramverket. Språket är Erlang så det är funktionellt och faller helt riktigt in under deklarativa språk.

Jag skriver framförallt C men Erlang är fascinerande. '=' är inte assign utan match och den kommer alltid att försöka matcha så om man har ett 'X = foo.' och variablen X är unbound(odefinierad) så kommer matchningen att tilldela X värdet foo för att uppfylla matchningen men om man sedan försöker med 'X = bar.' då kommer man få en krash eftersom 'foo != bar'. Så '<< ... >>' är binära patterns så genom att matcha dom mot byte-sekvenser från en buffer så kommer man att validera formatet och parse:a binärdatan i samma veva.

Jag skriver C, Golang, Rust, Shell scripting(portabell), Python och Erlang så det är ju enbart ett deklarativt språk i en rad av imperativa så jag lutar nog mer mot imperativa språk. Det är ju just aspekter av exempelvis Erlang som vidgar ens vyer på språk(pattern matching, actor modell, tail recursion, immutability, inget delat minne) om man kommer från imperativa språk.

Permalänk
Medlem
Skrivet av klk:

Kallas för cuopling och är bland det värre felet en utvecklare kan göra

Citat:

Feature Envy
A method accesses the data of another object more than its own data.
Inappropriate Intimacy
One class uses the internal fields and methods of another class.
Message Chains
In code you see a series of calls resembling $a->b()->c()->d()
Middle Man
If a class performs only one action, delegating work to another class, why does it exist at all?

Det är väl inte någon av de här?

Permalänk
Medlem
Skrivet av pine-orange:

Det är väl inte någon av de här?

Mycket som leder till coupling (kod är beroende av annan kod) så det är ett stort område. Bäst är om utvecklare lägger stor tid på att göra saker som sköter sig själva. Måste man fylla objekt med information och fyllningen är omständig kommer det bli problem, framförallt på de områden där objektet fylls upp. Sådan kod är dessutom oftast domänspecifik och domäner har en tendens att ändra sig. Utvecklare behöver också lära sig domänen för att jobba i koden.

Lösningen exploderar i kodstorlek och blir mycket trögjobbade

Sökte lite och den här är kanske bättre, finns mycket i området
https://www.geeksforgeeks.org/software-engineering-coupling-a...

Permalänk
Skrivet av klk:

Kallas för cuopling och är bland det värre felet en utvecklare kan göra

Jag håller med om att coupling ställer till det. Om vi tar ditt favoritfel message chaining (a()->b()->c()->d()) handlar det om att du använder din kunskap om vad exempelvis b() returnerar. Här drar du nytta av/låser fast att ett funktionsanrop returnerar en pekare av en viss typ och opererar direkt på den. I detta fall måste typen ha en c-metod.

Den coupling vi ser här är inte direkt kopplad till anropskedjan i sig. Det går precis lika bra/är lika fel att göra samma antaganden även om du mellanlandar i olika variabler:

auto tmp_a = a(); auto tmp_b = tmp_a->b(); auto tmp_c = tmp_b->c(); tmp_c->d();

Den kodsnutten uppvisar precis samma problem som om man gjort anropen i direkt följd.

Om nu språket har ett antal vedertagna kodningsmönster som

Builder pattern där man logiskt delar upp en initiering i flera mindre olika delar som alla returnerar objektet de delinitierade (jag kan tycka det är enklare att förstå än att skicka 9 godtyckliga siffror in i en konstruktor)

let obj = ObjectIn3DSpace::new() .position(1.0, 2.0, 3.0) .rotation(0.0, 90.0, 0.0) .dimensions(2.0, 2.0, 2.0);

eller Pipeline pattern där man itererar över en container och operationen returnerar den nya containern för att man skall kunna applicera nya operationer på resultatet

let result = vec![1, 2, 3, 4, 5] .iter() .map(|x| x * 2) .filter(|x| x > &5) .collect::<Vec<_>>();

är det verkligen message chaining då?

Koden följer för språket vedertagna mönster och alla vet precis vad som händer. Anser du att dessa kodmönster försvårar refactoring? Skulle du vilja ha friheten att låta en del-constructor returnera något annat? I Pipeline-fallet skickar du bara in funktioner till map och filter, är det hämmande för din frihet som programmerare att map och filter beter sig som de gör?

Lyft blicken och tänk på vad koden gör istället för att låta ryggmärgsreflexen säga message chain, dåligt.

Permalänk
Medlem
Skrivet av klk:

Men jag är inte rädd att visa kod (vilket jag har anledningar till), då för att få lite mer substans från dig hade det varit bra med lite kött på benen

Men det är väl i princip bara du som tror att det du har visat (kodmässigt) säger någonting om dig som utvecklare, bra eller dåligt.

(jag skulle säga att det du visat i övrigt säger en hel del om dig som utvecklare)

Skrivet av klk:

Givetvis får man granska och det det är sällan det är lika intressant efter granskning men bara att någon kommer med nytt är bra.

Bra är bra, nytt är oftast inte bra.

Med lite självinsikt så inser man också att det man själv ser som "nytt" är nytt enbart för att 100 andra har gjort samma sak före en, men insett att det är idiotiskt och kastat den lösningen.

Terence Howard t.ex. är ett ypperligt, aktuellt exempel på detta.

Permalänk
Datavetare
Skrivet av klk:

Kallas för cuopling och är bland det värre felet en utvecklare kan göra

Du postade ju en video tidigare i denna tråd som pratade om "couplings/message-chains" och varför det är dåligt.

Samma video nämnde också att "pipeline-style" ser på ytan ut lite som "message-chains", men den förra saknar problematiken med stark koppling och är därför inte alls problematiskt.

Gissar att du missat den nyansen och framförallt missat när det är "message-chains" resp. "pipeline-style".

Om man förenklar det hela till att bara visa typer ser det typiskt ut så här

Message chains (bad)

T -> T-method -> S -> S-method -> P -> P-method -> Q -> Q-method ...

Pipeline style (good)

I fallet build pattern där T är konfig-objekt och S är skapat objekt

T -> T-method -> T -> T-method -> T -> T-method -> T -> T-method -> S

I fallet map/filter/reduce/etc

[T] -> filter[T] -> [T] -> map[T] -> [S] -> reduce[S] -> S

I.e. kod med många message-chains skapar beroende mellan många typer på ett sätt som potentiellt gör det svårare att byta ut/ändra i någon av de ingående typerna.

Kod med pipeline-style är generellt sett bara lättare att läsa då den innehåller återkommande och rätt standardiserat mönster som typiskt opererar på en/två specifika typer per gång.

Builder-pattern har flera fördelar

1. vettig sätt att exponera väldigt flexibla/komplicerade strukturer där man oftast (men inte alltid) väljer att förändrar några få parameters

2. går att ha flera builders mot samma gränssnitt, så en komplex typ T (t.ex. någon form av fordon med hjul) kan ha olika builders för att skapa t.ex. terrängbil, racerbil, arbetsfordon, lastbil, etc.

3. det lyfter ut en potentiellt rätt komplex del ur en typ där builder-delen bara är relevant för att skapa en instans av typen, men efter det är helt irrelevant.

Så var aldrig med på att du ansåg build-pattern dålig. Trodde du ansåg att den på något sätt gick att göra mycket bättre i C++ jämfört med andra språk (vilket kändes helmysko)

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Xeonist:

Men det är väl i princip bara du som tror att det du har visat (kodmässigt) säger någonting om dig som utvecklare, bra eller dåligt.

Att visa kod är självklart känsligt, då tar man risker beroende på vem som tittar.

Skrivet av Xeonist:

(jag skulle säga att det du visat i övrigt säger en hel del om dig som utvecklare)

Så är det absolut

Skrivet av Xeonist:

Med lite självinsikt så inser man också att det man själv ser som "nytt" är nytt enbart för att 100 andra har gjort samma sak före en, men insett att det är idiotiskt och kastat den lösningen.

Men det jag visat har inte 100 gjort tidigare. Och utvecklare bör också förstå kostnader i att inte klara av att sköta koden, ta in externa lösningar är inte gratis, långt ifrån. Massor av projekt har kraschat på grund av det.
Och framförallt, utvecklare lär sig inte koda, de blir pusslare

I den här tråden har jag i alla fall sedan en tid tillbaka hållit låg profil i hur man kan lösa saker för jag tolkar det som andra i tråden inte klarar "skryt". Men tål man inte skryt från andra, hur skall man då själv kunna lära sig? Fantastiskt svårt att diskutera med så känsliga personer.

Permalänk
Medlem
Skrivet av klk:

Att visa kod är självklart känsligt, då tar man risker beroende på vem som tittar.

Det är ohälsosamt att vara så pass känslomässigt knuten till sin kod. Fältet rör sig snabbare än kapaciteten för en enskilds lärande så det finns alltid något att lära sig och ingen blir fullärd.

Skrivet av klk:

I den här tråden har jag i alla fall sedan en tid tillbaka hållit låg profil i hur man kan lösa saker för jag tolkar det som andra i tråden inte klarar "skryt". Men tål man inte skryt från andra, hur skall man då själv kunna lära sig? Fantastiskt svårt att diskutera med så känsliga personer.

Nja, det är väl framförallt för att du tycker om att skylta med kod som inte har med ämnet att göra.

Jag tycker att skryt är OK men då får man backa det. Att skryta samtidigt som man är sämst i klassen ser dumt ut.

Skryt tycker inte jag hör ihop med lärande. Jag tycker att det är hälsosammare att lära sig från andra hållet. Att man publicerar sin kod för kommentarer om man har svårt med något eller vill få hjälp med optimeringar, istället för att publicera sin kod med syftet att hävda sig. Att hävda sig inför osäkra personer hämmar snarare dialogen.

Permalänk
Medlem
Skrivet av Ingetledigtnamn:

Lyft blicken och tänk på vad koden gör istället för att låta ryggmärgsreflexen säga message chain, dåligt.

Får jag säga samma sak då, för varför tror du automatiskt jag har fel? Finns gott om literatur kring det här men det är kanske inte den mest lästa då administration av kod brukar hamna långt ner i "rolighetslistan" för kodare.

Skrivet av Ingetledigtnamn:

Jag håller med om att coupling ställer till det. Om vi tar ditt favoritfel message chaining (a()->b()->c()->d()) handlar det om att du använder din kunskap om vad exempelvis b() returnerar.

auto tmp_a = a(); auto tmp_b = tmp_a->b(); auto tmp_c = tmp_b->c(); tmp_c->d();

let obj = ObjectIn3DSpace::new() .position(1.0, 2.0, 3.0) .rotation(0.0, 90.0, 0.0) .dimensions(2.0, 2.0, 2.0);

Om en erfaren utvecklare ser koden jämfört med en som inte är så erfaren kommer de troligen tolka dina exempel annorlunda. Vad erfaren innebär är subjektivt självklart man enligt mig blir man erfaren av att hela tiden försöka utveckla sig, sitta och skriva samma typ av kod i 20 år blir man inte erfaren av.

Koden där du exemplifierar med att ha variabler istället för att kedja koden är typiskt att då har en erfaren utvecklare skrivit det. Att kedja kod är typiskt för nybörjare.

Det är inte bara coupling som är problematiskt. Ser jag någon kedja kod så är det en stark signal om att personen inte debuggar sin kod, om personen inte debuggar sin kod kan man snabbt räkna ut att personen har svårt med komplexa lösningar och koden har mer buggar.

Vana utvecklare prioriterar debugvänlig kod framför "cool" kod. Kedjade kodavsnitt är inte roliga att debugga, speciellt inte när man har andra saker att hålla reda på. Är det dessutom kod som man själv inte skrivit, det blir snabbt en mardröm.

Det är dålig design trots att koden ser "snygg" ut.

När det gäller rust och konstruktör problemet är inte problemet med coupling lika stort sett till objektet som jobbas med. Så länge allt hanteras av ett enda objekt och man inte blandar så går det och släppa igenom i nödfall. Men det kräver då att utvecklaren håller den disciplinen och inte lägger in annan cool kod i argumenten.
Det är fortfarande ingen bra lösning på något vis men jag antar att i Rust finns det inte så mycket alternativ.

Message chains är annars supervanligt i C# och gissar att även java. Att försöka argumentera med en C# utvecklare hur dåligt det är går knappt och det beror på att de så sällan skriver egna lösningar. I 19 fall av 20 är C# kod upprepade mönster och fokus på att använda .NET.

Coupling är ett gigantiskt område, det är alltid en mycket stor motståndare utvecklare hela tiden behöver fajtas mot. Coupling är exempelvis orsaken till varför så många utvecklare ratar OOP (med all rätt). Objekt orienterad programmering är otroligt svårt att få till just på grund av coupling.

Permalänk
Medlem
Skrivet av orp:

Det är ohälsosamt att vara så pass känslomässigt knuten till sin kod. Fältet rör sig snabbare än kapaciteten för en enskilds lärande så det finns alltid något att lära sig och ingen blir fullärd.

Hur lyckas du? Vad jag än skriver så lyckas du tolka det på ett totalt annorlunda sätt. Påstår inte att jag är något språkgeni, långt ifrån men så otydligt var det väl inte?

Permalänk
Medlem
Skrivet av Yoshman:

[T] -> filter[T] -> [T] -> map[T] -> [S] -> reduce[S] -> S

Den koden är inte skriven av programmerare som använder debugger

Och med att använda debugger menar jag då att den går varm nästan jämnt, inte något man plockar fram i nödfall om det dyker upp en bugg