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

Permalänk
Medlem
Skrivet av klk:

Skrivs C/C++ kod kan den användas länge.

Det är väl inte unikt för varken C eller C++. COBOL används fortfarande flitigt inom bankvärlden om jag förstått det rätt. Erlang används fortfarande flitigt.

Skrivet av klk:

Ta världens kändaste databas, sqlite. C kod där det mesta skrevs för många många år sedan. Det finns inte anledning att skriva en ny sqlite. Även om någon skulle kunna göra det på kanske ett halvår så krävs det så mycket tid för att marknadsföra och samtidigt tävla mot en databas som finns överallt.

Företaget Turso publicerade en omskrivning av sqlite i Rust(limbo) för några månader som fått en del uppmärksamhet. Personligen kommer jag använda limbo över sqlite framöver. Detta framförallt pga att det är open-source och inte source available men att det är minnessäkert känns som en trevlig bonus.

Skrivet av klk:

C++ är för effektivt, därför kommer det aldrig bli så många som jobbar i det. Omöjligt att tävla mot skickliga C++ utvecklare.

Detta är väldigt motsägelsefullt om det är så effektivt så hade alla jobbat med C++. Vad är det för tävling? Prestandamässigt så skulle jag hävda att C++, Rust och Zig är likvärdiga. Tävlar man i minnessäkerhet så finns där lite som C++ utvecklarna kan göra.

Skrivet av klk:

Det finns självklart en hel massa C++ programmerare som borde sitta i andra språk för det är inget för nybörjare. De här kan säkert orsaka ett och annat problem sett till minnet.
Är man vad så är det inte ett problem

Nu tror du att C++-utvecklare är några övermänniskor. Jag tror framförallt allt att nya utvecklare inte vill dras med legacy utan en uppenbar vinst så därför väljer dom hellre att lära sig Rust, Zig eller Go. Jag tror därför att det handlar mer egenintresse hos nya utvecklare än kompetensproblem.

Som du var inne på tidigare ... kan du försöka hålla dig till diskussionen om minnessäkerhet istället för att sväva ut om hur pass "duktig" man måste vara för att koda C++ och att det inte finns något annat som kan mäta sig med C++. Det börjar bli aningen tröttsamt. Speciellt med tanke på att du alltid kommer med extraordinära påstående som alltid saknar fakta.

Permalänk
Medlem
Skrivet av Yoshman:

I denna intervju med Herb Sutter (ordförande ISO-C++) och Steve Klabnik (som jobbat väldigt aktivt med Rust utveckling och skrivit flera böcker om Rust-programmering)
https://herbsutter.com/2024/10/23/podcast-interview-rust-and-...

Jag skulle ta det herb sutter säger med en nypa salt. förstår mig inte riktigt på honom. Alltså han är jättetrevlig och duktig på att prata men hans "mål" med C++ är inte vad de flesta andra utvecklare i C++ vill ha. Det där han höll på med och göra ett ny variant av C++ visar lite på hur fel ute han var.
https://www.reddit.com/r/cpp/comments/xim2zs/can_c_be_10x_sim...

Permalänk
Medlem
Skrivet av orp:

Detta är väldigt motsägelsefullt om det är så effektivt så hade alla jobbat med C++. Vad är det för tävling? Prestandamässigt så skulle jag hävda att C++, Rust och Zig är likvärdiga.

Om du kör "unsafe" kod så ja, men då har du tappat poängen

Skrivet av orp:

Nu tror du att C++-utvecklare är några övermänniskor. Jag tror framförallt allt att nya utvecklare inte vill dras med legacy utan en uppenbar vinst så därför väljer dom hellre att lära sig Rust, Zig eller Go. Jag tror därför att det handlar mer egenintresse hos nya utvecklare än kompetensproblem.

Absolut inga övermänniskor men ett visst intresse. Det är inte speciellt svårt att lära sig men man måste vara intresserad.

När jag började programmera kunde man skriva självmodifierande kod, att viss kod skriver om sina egna assembler instruktioner.
Det går att göra vansinnigt "smarta" lösningar på det viset men där går min gräns, det är svårt att hantera sådan kod och utvecklingsmiljöer klarar inte sådant.

Jobba direkt med minne är så starkt och därför kan inte de som låter bli att tävla. Har inte med skicklighet att göra utan att man har funktionalitet som är överlägsen

Permalänk
Medlem
Skrivet av klk:

Om du kör "unsafe" kod så ja, men då har du tappat poängen

Varför skulle man behöva köra unsafe?

Skrivet av klk:

Absolut inga övermänniskor men ett visst intresse. Det är inte speciellt svårt att lära sig men man måste vara intresserad.

"visst intresse" som vanliga dödliga inte besitter.

Skrivet av klk:

När jag började programmera kunde man skriva självmodifierande kod, att viss kod skriver om sina egna assembler instruktioner.
Det går att göra vansinnigt "smarta" lösningar på det viset men där går min gräns, det är svårt att hantera sådan kod och utvecklingsmiljöer klarar inte sådant.

Nu svävar du ut igen... Vad har detta med något att göra mer än "kolla hur bra jag var"? Framförallt har det inget med minnessäkerhet att göra.

Skrivet av klk:

Jobba direkt med minne är så starkt och därför kan inte de som låter bli att tävla. Har inte med skicklighet att göra utan att man har funktionalitet som är överlägsen

Det har ju inte heller något med minnessäkerhet att göra och för att bemöta ditt argument, varför använda bloat som C++ när du kan använda C?

Permalänk
Medlem
Skrivet av orp:

Varför skulle man behöva köra unsafe?

Exakt, varför skulle man vilja göra det. Nu kommer vi till själva kärnan i problematiken.

Om utvecklare inte vet om vad som går att göra, exempelvis de som knappt vet vad assembler är. Då är denna diskussion omöjlig.
Jag kan inte förklara för dig på några rader varför detta är så bra. Inte jättesvårt men för svårt för att beskriva i text här.

Enklaste gissar jag är att du granskar hur en "string" klass fungerar, hur den fungerar internt. Tror att en majoritet av alla implementationer av dessa sköter allokeringen själva eftersom de oftast är ordentligt optimerade.

Permalänk
Medlem
Skrivet av klk:

Exakt, varför skulle man vilja göra det. Nu kommer vi till själva kärnan i problematiken.

Nej för du har inte förklarat kärnan. Det finns en del färdigskriven funktionalitet som är optimerad genom att använda unsafe men den är ofta central och används för att implementera Rust. Vad har du för use-case som kräver unsafe utöver färdigskriven funktionalitet? Du vet också om att du inte opt:ar ut från all Rusts säkerhet enbart för att du använder unsafe?

Samt om 1% av koden är unsafe och resten inte är unsafe så "faller" inget.

Skrivet av klk:

Om utvecklare inte vet om vad som går att göra, exempelvis de som knappt vet vad assembler är. Då är denna diskussion omöjlig.

Allt går att lösa i assembler så du får nog vara lite mer specifik.

Skrivet av klk:

Jag kan inte förklara för dig på några rader varför detta är så bra. Inte jättesvårt men för svårt för att beskriva i text här.

Enklaste gissar jag är att du granskar hur en "string" klass fungerar, hur den fungerar internt. Tror att en majoritet av alla implementationer av dessa sköter allokeringen själva eftersom de oftast är ordentligt optimerade.

Det fungerar inte att enbart komma med påstående och säga detta kan jag inte beskriva i text och säga granska X. Både Rust och C++ har en sträng-typ och prestandaskillnaden är inte påtagligt så vad har detta med minnestricksande att göra?

Permalänk
Medlem
Skrivet av orp:

Varför skulle man behöva köra unsafe?

När jag ändå håller på kan jag kanske ta något som jag tror är förståeligt utan att man behöver känna till programmering

En cache line på x86 är 64 bytes, samma på de flesta ARM processorer, har för mig att apple har arm processorer med en cache line på 128 bytes.

Processorer tycker också om att hitta data på jämnt delbara adresser med siffror som 4,8,16 och så vidare.

Förutom att ovanstående så älskar också processorer förutsägbarhet. Processorer gissar ofta vart nästa minne ligger och vet utvecklare om hur processorn gissar eller kan hjälpa processorn på traven, då kan processorn arbeta så mycket snabbare.

Finns annat att tänka på, kompilatorer är idag är fantastisk. Men ovanstående är exempel på varför exempelvis kod skriven i C# eller Java omöjligt kan tävla med C++ om utvecklaren i C++ vet om hur processorn fungerar och anpassat data efter processorer.

När data placeras "rätt" fungerar också SIMD instruktioner bättre. Därför kan kod skriven i C++ se lite kryptisk ut, inte jättekonstig men samtidigt vara nästan 10 gånger snabbare än kod skriven i exempelvis C#.

För att göra sådan här kod behöver man oftast förstå minnet och kunna vara flexibel.

Permalänk
Medlem
Skrivet av klk:

När jag ändå håller på kan jag kanske ta något som jag tror är förståeligt utan att man behöver känna till programmering

En cache line på x86 är 64 bytes, samma på de flesta ARM processorer, har för mig att apple har arm processorer med en cache line på 128 bytes.

Processorer tycker också om att hitta data på jämnt delbara adresser med siffror som 4,8,16 och så vidare.

Förutom att ovanstående så älskar också processorer förutsägbarhet. Processorer gissar ofta vart nästa minne ligger och vet utvecklare om hur processorn gissar eller kan hjälpa processorn på traven, då kan processorn arbeta så mycket snabbare.

Finns annat att tänka på, kompilatorer är idag är fantastisk. Men ovanstående är exempel på varför exempelvis kod skriven i C# eller Java omöjligt kan tävla med C++ om utvecklaren i C++ vet om hur processorn fungerar och anpassat data efter processorer.

När data placeras "rätt" fungerar också SIMD instruktioner bättre. Därför kan kod skriven i C++ se lite kryptisk ut, inte jättekonstig men samtidigt vara nästan 10 gånger snabbare än kod skriven i exempelvis C#.

För att göra sådan här kod behöver man oftast förstå minnet och kunna vara flexibel.

Motsvarande går att göra i Rust

Permalänk
Medlem
Skrivet av orp:

Motsvarande går att göra i Rust

Kan du visa exempel kod i rust där någon gör detta? Har letat men inte hittat

Det går att cykla mellan stockholm och göteborg men knappt någon gör det. gissa varför?

Permalänk
Medlem
Skrivet av klk:

Kan du visa exempel kod i rust där någon gör detta? Har letat men inte hittat

Det går att cykla mellan stockholm och göteborg men knappt någon gör det. gissa varför?

Du vill ha exempel på data alignment så jag förstår dig rätt?

Permalänk
Medlem
Skrivet av orp:

Du vill ha exempel på data alignment så jag förstår dig rätt?

lite bättre får du nog prestera, helst vill jag se när man lägger data i något långt "tåg" (buffer)

Permalänk
Medlem
Skrivet av klk:

lite bättre får du nog prestera

Du har ju knappt kunnat hålla ett sammanhängande resonemang konsekvent genom hela tråden och jag har bett om data som backar dina extraordinära påstående och kodexempel som du enbart ignorerar så kanske inte så lägligt att vara nedlåtande.

Skrivet av klk:

helst vill jag se när man lägger data i något långt "tåg" (buffer)

Då får du vara väldigt mycket mer tydlig. Du skriver ett halvt inlägg om data alignment och sedan name-droppar du SIMD och C++ sedan vill du att jag ska förstå något godtyckligt scenario som du målat upp i huvudet?

Sedan låter detta som att du borde skapa en separat tråd för detta "Hur gör jag X i Rust?" för jag förstår fortfarande inte vad detta har med minnessäkerhet att göra.

Permalänk
Skrivet av klk:

När jag ändå håller på kan jag kanske ta något som jag tror är förståeligt utan att man behöver känna till programmering

Snälla @klk svara på frågorna som @orp ställer i stället för att komma med orelaterat dravel. Jag tror du kan utgå ifrån att vi alla är erfarna programmerare med god förståelse för både datorarkitektur och vad kompilatorn gör.

Skrivet av klk:

En cache line på x86 är 64 bytes, samma på de flesta ARM processorer, har för mig att apple har arm processorer med en cache line på 128 bytes.

Processorer tycker också om att hitta data på jämnt delbara adresser med siffror som 4,8,16 och så vidare.

Implementationen av klasserna i moderna bibliotek brukar vara ganska duktig på att hålla sig inom en cache line i de fall då det går. Exempelvis tog Yoshman tidigare upp implementationen av string som sparar korta strängar "inline" i string-objektet medan minne till längre strängar allokeras separat.

Skrivet av klk:

Förutom att ovanstående så älskar också processorer förutsägbarhet. Processorer gissar ofta vart nästa minne ligger och vet utvecklare om hur processorn gissar eller kan hjälpa processorn på traven, då kan processorn arbeta så mycket snabbare.

Försöker du säga att du ser till att du har en data-layout som tar hänsyn till den hardware prefetching processorn gör? Hur vet du hur processorn gör? Hur portabelt och återanvändbart är det?

Skrivet av klk:

Finns annat att tänka på, kompilatorer är idag är fantastisk. Men ovanstående är exempel på varför exempelvis kod skriven i C# eller Java omöjligt kan tävla med C++ om utvecklaren i C++ vet om hur processorn fungerar och anpassat data efter processorer.

När data placeras "rätt" fungerar också SIMD instruktioner bättre. Därför kan kod skriven i C++ se lite kryptisk ut, inte jättekonstig men samtidigt vara nästan 10 gånger snabbare än kod skriven i exempelvis C#.

Om ChatGPT inte ljuger för mig, så kommer både JVMen och .NET runtime att se till att data som används i SIMD-operationer har ett alignment som matchar data-typens storlek. Om du ser en skillnad på 10X borde det inte bero på minnes-layouten.

Permalänk
Datavetare
Skrivet av klk:

Det är inte bättre. Enda anledningen till att jag tänkte testa är att andra utvecklare i Rust är så entusiastiska. Samtidigt är koden de skriver inte speciellt bra om det inte handlar om deklarativ kod. Lite som när man ser yngre spelprogrammerare, de är också helt värdelösa i arkitektur och skriver illa. Man vill få fram sitt spel och är inte jätteintresserad av koden som skrivs.
Jag är själv typiskt entusiast programmerare (hobby och yrke). Det skiljer exempelvis gruppen mot C# och Java. De som skriver kod i dessa språk accepterar i princip vad som helst, det är bara ett yrke. Utvecklare i Rust har mycket mer driv.

Då du aldrig använt Rust gör massa fel direkt felaktiga antaganden.

T.ex. så postade du denna länk längre upp
https://linearb.io/blog/message-chains-code-smell
och hävdade att de som kodar i Rust ofta skapar "message chains". Nu är det tekniskt möjligt att göra sådant i Rust, men det är inte ett språk som explicit stödjer OOP och finns därför inget som tvingar en att trycka in allt i klasser likt C#/Java. Idiomatisk Rust innehåller inte "message chains".

Däremot, från din egen länk, så är det många som blandar ihop "message chains" (som den artikeln anser är "dåligt") med "pipeline-style" (som artikeln anser är "bra"). Pipeline-style är extremt vanligt i Rust-kod, då främst i form av filter/map/reduce/etc.

Än mer är detta trevligt då kod skriven på det sättet i Rust kan i många lägen väldigt enkelt ändras (minimal kodändring) så den effektivt utnyttja multipla CPU-kärnor, via ramverk som Rayon. C++ har ett embryo till liknande stöd i standardbiblioteket i form av execution policy, men i nuläget är Rust/Rayon både mognare, ger i nästan alla lägen bättre presterande resultat och långt enklare att använda på "rätt sätt" (i.e. inga data-race).

Skrivet av klk:

När jag ändå håller på kan jag kanske ta något som jag tror är förståeligt utan att man behöver känna till programmering

En cache line på x86 är 64 bytes, samma på de flesta ARM processorer, har för mig att apple har arm processorer med en cache line på 128 bytes.

Alla systemspråk samt Java, C#, Go, m.fl. har alla det relevanta stödet för att undvika prestandaproblem som false-sharing i de fall man delar data mellan trådar på korrekt sätt.

Utöver det är det rätt lite man behöver bry sig om cache-line storlek idag. Om man gör saker som är extremt prestandakritiskt är det i så fall idag mer relevant att tänka på page-gränser som väldigt länge "alltid" var 4k, men som på ARM64 allt oftare är 16 kB (kan vara 4, 16 eller 64 kB och är upp till OS att välja vilken storlek en viss process har, MacOS kör 16 kB och Linux har allt mer börjat gå till 64 kB på servers då enbart 4->64 kB page storlek kan ge 10-20 % bättre prestanda i "minnes-tunga" laster).

Skrivet av klk:

Processorer tycker också om att hitta data på jämnt delbara adresser med siffror som 4,8,16 och så vidare.

90-tals CPU, visst. En modern CPU har en långt mer avancerad access-pattern detektor än så...

Så länge din stride är förutsägbar spelar dess värde rätt liten roll. Det relevanta att ta med sig från det är: en array är betydligt snabbare än en hashmap, trots att båda är O(1).

INGET här är något som ger C++ en unik fördel.

Skrivet av klk:

Förutom att ovanstående så älskar också processorer förutsägbarhet. Processorer gissar ofta vart nästa minne ligger och vet utvecklare om hur processorn gissar eller kan hjälpa processorn på traven, då kan processorn arbeta så mycket snabbare.

Exakt. Enda som möjligen man kan "hjälpa" med är att försöka hålla working-set på så få "pages" som möjligt. TLB-L1 är mer prestandakritisk i moderna CPUer än L1-cache för data/kod.

Skrivet av klk:

När data placeras "rätt" fungerar också SIMD instruktioner bättre. Därför kan kod skriven i C++ se lite kryptisk ut, inte jättekonstig men samtidigt vara nästan 10 gånger snabbare än kod skriven i exempelvis C#.

Så här krångligt är det att använda SIMD i C# för de riktigt vanliga fallen

var a = new Vector3(1, 2, 3); var b = new Vector3(4, 5, 6); var dotProduct = Vector3.Dot(a, b); var crossProduct = Vector3.Cross(a, b);

Detta blir "handoptimerad" SIMD för de plattformar som .NET-runtime stödjer. Och tillskillnad från C++ där man måste välja om det ska vara SSE, AVX, NEON eller SVE2 etc när man kompilerar kommer detta välja "bästa val" när programmet körs (vid JIT).

Så i praktiken (är möjligt att skriva kod som väljer vid runtime även för C++, något som ramverk likt ISPC kan göra) kan just sådant vara snabbare hos plattformar som använder JIT.

Visa signatur

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

Permalänk
Datavetare
Skrivet av klk:

Kan du visa exempel kod i rust där någon gör detta? Har letat men inte hittat

Det går att cykla mellan stockholm och göteborg men knappt någon gör det. gissa varför?

använda Rust för att läsa/skriva minnes-mappad IO. Nedan skulle kunna vara en del till Rust-driver för någon ARM MCU

#![no_std] #![no_main] use core::ptr::{read_volatile, write_volatile}; #[repr(C)] pub struct GPIO { pub moder: u32, pub otyper: u32, pub ospeedr: u32, pub pupdr: u32, pub idr: u32, pub odr: u32, // rest of memory mapped registers... } const GPIOA_BASE: usize = 0x4800_0000; const GPIOA: *mut GPIO = GPIOA_BASE as *mut GPIO; #[entry] fn execution_starts_here() -> ! { unsafe { let moder = read_volatile(&(*GPIOA).moder); write_volatile(&mut (*GPIOA).odr, moder | 0x1); } loop {} }

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 klk:

Jobba direkt med minne är så starkt och därför kan inte de som låter bli att tävla. Har inte med skicklighet att göra utan att man har funktionalitet som är överlägsen

Hantera minnet själv är ibland en bra idé, och i enstaka fall en absolut nödvändighet.
Oftast är det en usel idé.
Sköta all minneshantering manuellt är en extra källa till buggar, och tar tid, energi, och uppmärksamhet från programmerare som tvingas hålla på och mickla med minneshantering istället för att koncentrera sig på det problem som programmet egentligen är till för att lösa.

Man kan jämföra det med att jobba i assembler jämfört med ett högnivåspråk.
Assembler ger ojämförlig flexbilitet, och enorma möjligheter till optimeringar. Men det är också mycket mer komplicerat att skriva program i assembler än i ett högnivåspråk, det blir lättare fel, och det tar mycket längre tid.

Permalänk
Medlem
Skrivet av orp:

Du vill ha exempel på data alignment så jag förstår dig rätt?

Hur gör jag det här i rust?
Det är visserligen en del kod men exemplet är ganska enkelt även om det är en del metoder.
det är en "strings" klass. Alltså ett objekt som hanterar flera strängar och lägger strängarna efter varandra

https://hastebin.com/share/iyizimobaf.cpp <- headerfilen (tror jag om den sajten fungerar)

Lägga till en sträng

/** --------------------------------------------------------------------------- * @brief Appends a new string to the buffer. * * This method appends a new string to the internal buffer by writing a 32-bit length header * followed by the string data. The total block size (header + data + padding) is aligned to a 4-byte boundary. * It reserves additional space if necessary and, in debug builds, clears any extra padding bytes. * * @param stringAppend A std::string_view representing the string to be appended. */ void strings32::append(const std::string_view& stringAppend) { assert( stringAppend.length() < 0xffff'ffff ); auto uSize = stringAppend.size(); // uSize: Length of the appended string decltype( uSize ) uBlockSize = align32_g(uSize + static_cast<decltype( stringAppend.size() )>(sizeof(uint32_t))); // uBlockSize: Total block size (length size header + string data + padding) reserve_add(uBlockSize); // Ensure there is enough capacity in the buffer for the new block // Position the insertion pointer at the end of the buffer uint8_t* puInsertionPosition = m_puBuffer + m_uSize; assert( m_uSize % sizeof(uint32_t) == 0 ); uint32_t uLength = (uint32_t)uSize; // uLength: Length of the string block, compiler will optimize this *reinterpret_cast<uint32_t*>(puInsertionPosition) = uLength; // Write the string length at the beginning of the string block memcpy(puInsertionPosition + sizeof(uint32_t), stringAppend.data(), uSize);// Copy the string data into the block after the length header #ifndef NDEBUG // ubd debug, clear padding bytes (never read those bytes so it is not necessary to clear them in release) if(uBlockSize > sizeof(uint32_t) + uSize) // If padding is needed to reach a 4-byte alignment { memset(puInsertionPosition + 4 + uSize, 0, uBlockSize - 4 - uSize); // padd bytes with zero } #endif m_uSize += uBlockSize; // Update the used buffer size to include the new block }

Exempelkod

TEST_CASE( "append", "[strings]" ) { gd::strings32 strings_; strings_.append("one"); strings_.append("two"); strings_ << "three"; strings_.append_any(100).append_any(200).append_any(300); strings_.add( 1, 2.0, true, "test" ); auto uCount = strings_.count(); REQUIRE(strings_[0] == "one"); REQUIRE(strings_[uCount-1] == "test"); for( const auto& it : strings_ ) { std::cout << it << ", "; } std::cout << "\n"; gd::strings32 strings2_({ "a", "b", "c", "d", "e" }); REQUIRE(strings2_.join() == "abcde" ); std::vector<std::string> vector_ = { "f", "g", "h", "i", "j" }; strings2_.append( vector_ ); REQUIRE(strings2_.join() == "abcdefghij" ); strings_.append_any( strings2_ ); std::cout << strings_.join(" - ") << "\n"; REQUIRE(strings_.find( "100" ) != strings_.end() ); REQUIRE(strings_.find( "101" ) == strings_.end() ); strings_ += "1"; strings_ += 1.00001; strings_ += 1ull; std::cout << strings_.join(" - ") << "\n"; for( auto it = strings_.begin(); it != strings_.end(); ) { if( std::atoi((*it).data()) != 1 ) { it = strings_.erase(it); } else { ++it; } } std::cout << strings_.join(" - ") << "\n"; REQUIRE(strings_.find( "1.00001" ) != strings_.end() ); }

Permalänk
Medlem
Skrivet av klk:

Hur gör jag det här i rust?
Det är visserligen en del kod men exemplet är ganska enkelt även om det är en del metoder.

Gillar hur du cherrypickar ett quote och totalt ignorerar 95% övriga svar..
Kudos till er här inne som fortfarande försöker föra en saklig diskussion, jag hade gett upp för längesen

Permalänk
Medlem
Skrivet av n0kturnal:

Gillar hur du cherrypickar ett quote och totalt ignorerar 95% övriga svar..
Kudos till er här inne som fortfarande försöker föra en saklig diskussion, jag hade gett upp för längesen

Koden jag klistrade in tror jag sammanfattar vad vi pratar om så tycker faktiskt inte jag ignorerar någon.
Inte så lätt att diskutera om de man diskuterar med inte är vana vid att hantera minnet själva och därför inte vet vad man kan göra.

Jag kan skicka in betydligt mer "avancerad" kod som gör att man kan återanvända mycket mer och få upp snabbheten ordentligt. Det sparar både buggar och tid

Permalänk
Datavetare
Skrivet av klk:

Hur gör jag det här i rust?
Det är visserligen en del kod men exemplet är ganska enkelt även om det är en del metoder.
det är en "strings" klass. Alltså ett objekt som hanterar flera strängar och lägger strängarna efter varandra

https://hastebin.com/share/iyizimobaf.cpp <- headerfilen (tror jag om den sajten fungerar)

Lägga till en sträng

/** --------------------------------------------------------------------------- * @brief Appends a new string to the buffer. * * This method appends a new string to the internal buffer by writing a 32-bit length header * followed by the string data. The total block size (header + data + padding) is aligned to a 4-byte boundary. * It reserves additional space if necessary and, in debug builds, clears any extra padding bytes. * * @param stringAppend A std::string_view representing the string to be appended. */ void strings32::append(const std::string_view& stringAppend) { assert( stringAppend.length() < 0xffff'ffff ); auto uSize = stringAppend.size(); // uSize: Length of the appended string decltype( uSize ) uBlockSize = align32_g(uSize + static_cast<decltype( stringAppend.size() )>(sizeof(uint32_t))); // uBlockSize: Total block size (length size header + string data + padding) reserve_add(uBlockSize); // Ensure there is enough capacity in the buffer for the new block // Position the insertion pointer at the end of the buffer uint8_t* puInsertionPosition = m_puBuffer + m_uSize; assert( m_uSize % sizeof(uint32_t) == 0 ); uint32_t uLength = (uint32_t)uSize; // uLength: Length of the string block, compiler will optimize this *reinterpret_cast<uint32_t*>(puInsertionPosition) = uLength; // Write the string length at the beginning of the string block memcpy(puInsertionPosition + sizeof(uint32_t), stringAppend.data(), uSize);// Copy the string data into the block after the length header #ifndef NDEBUG // ubd debug, clear padding bytes (never read those bytes so it is not necessary to clear them in release) if(uBlockSize > sizeof(uint32_t) + uSize) // If padding is needed to reach a 4-byte alignment { memset(puInsertionPosition + 4 + uSize, 0, uBlockSize - 4 - uSize); // padd bytes with zero } #endif m_uSize += uBlockSize; // Update the used buffer size to include the new block }

Exempelkod

TEST_CASE( "append", "[strings]" ) { gd::strings32 strings_; strings_.append("one"); strings_.append("two"); strings_ << "three"; strings_.append_any(100).append_any(200).append_any(300); strings_.add( 1, 2.0, true, "test" ); auto uCount = strings_.count(); REQUIRE(strings_[0] == "one"); REQUIRE(strings_[uCount-1] == "test"); for( const auto& it : strings_ ) { std::cout << it << ", "; } std::cout << "\n"; gd::strings32 strings2_({ "a", "b", "c", "d", "e" }); REQUIRE(strings2_.join() == "abcde" ); std::vector<std::string> vector_ = { "f", "g", "h", "i", "j" }; strings2_.append( vector_ ); REQUIRE(strings2_.join() == "abcdefghij" ); strings_.append_any( strings2_ ); std::cout << strings_.join(" - ") << "\n"; REQUIRE(strings_.find( "100" ) != strings_.end() ); REQUIRE(strings_.find( "101" ) == strings_.end() ); strings_ += "1"; strings_ += 1.00001; strings_ += 1ull; std::cout << strings_.join(" - ") << "\n"; for( auto it = strings_.begin(); it != strings_.end(); ) { if( std::atoi((*it).data()) != 1 ) { it = strings_.erase(it); } else { ++it; } } std::cout << strings_.join(" - ") << "\n"; REQUIRE(strings_.find( "1.00001" ) != strings_.end() ); }

use std::mem; pub struct Strings32 { buffer: Vec<u8>, } impl Strings32 { pub fn append(&mut self, string_append: &str) { assert!(string_append.len() < u32::MAX as usize, "String is too large"); let size = string_append.len(); let total_block_size = align32(size + mem::size_of::<u32>()); self.reserve_add(total_block_size); let insertion_position = self.buffer.len(); assert!(insertion_position % 4 == 0, "Buffer must remain 4-byte aligned"); // Append the 32-bit length header self.buffer.extend_from_slice(&(size as u32).to_le_bytes()); // Append the string data self.buffer.extend_from_slice(string_append.as_bytes()); #[cfg(debug_assertions)] if total_block_size > size + 4 { let padding_needed = total_block_size - (size + 4); self.buffer.extend(vec![0; padding_needed]); } } fn reserve_add(&mut self, additional: usize) { let new_capacity = self.buffer.len() + additional; if self.buffer.capacity() < new_capacity { self.buffer.reserve(new_capacity - self.buffer.capacity()); } } } fn align32(size: usize) -> usize { (size + 3) & !3 }

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:

use std::mem;

Det kanske inte var riktigt samma va?
Att ha en vector med speciell typ är bra ibland men när man vill ha olika typer och funktionalitet kring det. Det är då det blir problem

Att låta vektorn sköta blocket krånglar till det

Permalänk

Varför kan inte C++ ta alla dom bra sakerna som Rust har? Typ Cargo har jag hört ska vara bättre än CMake. Enklare och mera...framtid. CMake förväntar sig att alla manuella inställningar ska vara rätt. Jag misstänker att Cargo är automatiserat på en nivå som Maven eller Gradle i Java? Dessa är också mycket fina verktyg som jag djupt uppskattar. Java med.

Ni som säger "Nä, det kan inte C++". Nähä? Vi landade på månen ju!?

Permalänk
Medlem
Skrivet av heretic16:

Varför kan inte C++ ta alla dom bra sakerna som Rust har?

Då skulle du få en hel del arga C++ programmerare på dig.

Skrivet av heretic16:

Typ Cargo har jag hört ska vara bättre än CMake. Enklare och mera...framtid.

Varför är enkelt bättre? Det som är enkelt blir lätt missbrukat och sedan är det inte så enkelt. Är det lätt att dra in komponenter så kommer det snabbt drälla av komponenter.
Absolut är CMake svårt och har mycket i bagaget men det har blivit mycket bättre och det tvingar utvecklare att förstå vad de gör.
Slarvigt konfigurerade projekt är inte roligt även om det är lätt

Så är det ofta i frontend projekt, minsta lilla grej och de har flera megabyte installerade. Man behöver inget idag, går alldeles utmärkt att köra vanilla js.

Skrivet av heretic16:

CMake förväntar sig att alla manuella inställningar ska vara rätt. Jag misstänker att Cargo ska är automatiserat på en nivå som Maven eller Gradle i Java? Dessa är också mycket fina verktyg som jag djupt uppskattar. Java med.

Ni som säger "Nä, det kan inte C++". Nähä? Vi landade på månen ju!?

I C++ så har du ofta flera olika kompilatorer och olika inställningar beroende på hur projektet ser ut. CMake är gjort för att skala i. Du kan ha mängder med targets (applikationer), test, lek-kod, tutorials och annat. Även installationer.
Detta skall kompileras för olika operativsystem och olika hårdvara. Då är det inte så lätt

Permalänk
Skrivet av klk:

Då skulle du få en hel del arga C++ programmerare på dig.

Varför är enkelt bättre? Det som är enkelt blir lätt missbrukat och sedan är det inte så enkelt. Är det lätt att dra in komponenter så kommer det snabbt drälla av komponenter.
Absolut är CMake svårt och har mycket i bagaget men det har blivit mycket bättre och det tvingar utvecklare att förstå vad de gör.
Slarvigt konfigurerade projekt är inte roligt även om det är lätt

Så är det ofta i frontend projekt, minsta lilla grej och de har flera megabyte installerade. Man behöver inget idag, går alldeles utmärkt att köra vanilla js.

I C++ så har du ofta flera olika kompilatorer och olika inställningar beroende på hur projektet ser ut. CMake är gjort för att skala i. Du kan ha mängder med targets (applikationer), test, lek-kod, tutorials och annat. Även installationer.
Detta skall kompileras för olika operativsystem och olika hårdvara. Då är det inte så lätt

Jag skulle inte alls ha arga C++ programmerare efter mig. Jag är själv C++ programmerare.

Skulle C++ ha kompileringsvarningar som förklarar vad problemet finns, likt som rust. Då blir jag riktigt glad.
Skulle C++ ha automatisk analys om man överindexerar en array, glömmer frigöra minne osv. Då skulle jag bli glad.
Skulle C++ ha en pakethanterare som installerar färdiga paket. Likt VCPKG och MSBuild. Då skulle jag bli glad.

Dessa önskar jag mig i julklapp Jag menar, det är ju inga stora saker. Verktygen finns redan. Men det ingår inte i någon C++ kompilator alls! Istället får man vända sig till tredje, fjärde, femte part för att få problemet löst.

Permalänk
Medlem
Skrivet av heretic16:

Jag skulle inte alls ha arga C++ programmerare efter mig. Jag är själv C++ programmerare.

Skulle C++ ha kompileringsvarningar som förklarar vad problemet finns, likt som rust. Då blir jag riktigt glad.
Skulle C++ ha automatisk analys om man överindexerar en array, glömmer frigöra minne osv. Då skulle jag bli glad.
Skulle C++ ha en pakethanterare som installerar färdiga paket. Likt VCPKG och MSBuild. Då skulle jag bli glad.

Dessa önskar jag mig i julklapp Jag menar, det är ju inga stora saker. Verktygen finns redan. Men det ingår inte i någon C++ kompilator alls! Istället får man vända sig till tredje, fjärde, femte part för att få problemet löst.

Fråga: Vad är det du upplever som svårt i CMake idag?

Permalänk
Skrivet av klk:

Fråga: Vad är det du upplever som svårt i CMake idag?

Om jag ska installera saker. Då måste jag välja vart jag ska installera det.
Det är bara en röra. Finns ingen standardisering t.ex. "C:\cmake" där installerar man alla headers, .libs och .cpp eller vad det nu ska vara där.

Klart.
Istället är CMake en enorm konfigurering. Kolla hur VCPKG fungerar. Där har vi något som är riktigt bra.

Permalänk
Medlem
Skrivet av heretic16:

Om jag ska installera saker. Då måste jag välja vart jag ska installera det.
Det är bara en röra. Finns ingen standardisering t.ex. "C:\cmake" där installerar man alla headers, .libs och .cpp eller vad det nu ska vara där.

Klart.
Istället är CMake en enorm konfigurering. Kolla hur VCPKG fungerar. Där har vi något som är riktigt bra.

Hade jag varit er så hade jag lagt så mycket som möjligt (allt) i projektet och inte förlita sig på paketerare. Det underlättar mycket.

Jag har ingen mapp som heter "C:\cmake", det måste vara något ni gjort i eran konfiguration, känner inte till det

Permalänk
Medlem
Skrivet av heretic16:

Varför kan inte C++ ta alla dom bra sakerna som Rust har? Typ Cargo har jag hört ska vara bättre än CMake. Enklare och mera...framtid. CMake förväntar sig att alla manuella inställningar ska vara rätt. Jag misstänker att Cargo är automatiserat på en nivå som Maven eller Gradle i Java? Dessa är också mycket fina verktyg som jag djupt uppskattar. Java med.

Ni som säger "Nä, det kan inte C++". Nähä? Vi landade på månen ju!?

Kan och kan.
Om man stryker vissa äldre delar av C++, och kräver att programmerare enbart använder någon slags "Modern" C++. så är det väl möjligt.
Men då missar man en av de stora poängerna med att använda C++ idag, nämligen all den existerande C++ koden, och om man ändrar om språket så pass mycket att det har alla bra saker med Rust, så är ju frågan om det då finns någon anledning att använda C++ istället för Rust.

Permalänk
Medlem
Skrivet av heretic16:

Jag skulle inte alls ha arga C++ programmerare efter mig. Jag är själv C++ programmerare.

Skulle C++ ha kompileringsvarningar som förklarar vad problemet finns, likt som rust. Då blir jag riktigt glad.
Skulle C++ ha automatisk analys om man överindexerar en array, glömmer frigöra minne osv. Då skulle jag bli glad.
Skulle C++ ha en pakethanterare som installerar färdiga paket. Likt VCPKG och MSBuild. Då skulle jag bli glad.

Dessa önskar jag mig i julklapp Jag menar, det är ju inga stora saker. Verktygen finns redan. Men det ingår inte i någon C++ kompilator alls! Istället får man vända sig till tredje, fjärde, femte part för att få problemet löst.

En hel del av det där har inte mycket med språket i sig att göra utan har mest med utvecklingsmiljön att göra.

Permalänk
Medlem
Skrivet av Erik_T:

Men då missar man en av de stora poängerna med att använda C++ idag, nämligen all den existerande C++ koden, och om man ändrar om språket så pass mycket att det har alla bra saker med Rust, så är ju frågan om det då finns någon anledning att använda C++ istället för Rust.

Precis, byt istället till rust och gör inte om C++.
Också bra att sitta lugnt i båten och inte införa onödigt som behöver underhållas. Närmsta två-tre åren kommer vara händelserika