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

Permalänk
Medlem
Skrivet av klk:

Ja det är löst specat men det är så mycket att du omöjligt kan lösa det med så få personer utan att få en salig röra. Du måste välja andra vägar än vad som är "normalt". Låt säga att dessa 3-4 utvecklare har ett år på sig.

När du säger rest-api. tänker du CRUD eller hur skall så få personer hålla reda på allt? 300 tabeller blir extremt många api'n

Nu har jag ju jobbat på ställe med databaser som har 1500 tabeller och det är ju sällan att hela databasen måste API-mappas samt att du måste ju inte 1:1-mappa om du inte nödvändigtvis vill.

Permalänk
Medlem
Skrivet av orp:

Nu har jag ju jobbat på ställe med databaser som har 1500 tabeller och det är ju sällan att hela databasen måste API-mappas samt att du måste ju inte 1:1-mappa om du inte nödvändigtvis vill.

Hade det varit bra om du kunde mappa hela databasen med en fyra fem api

Permalänk
Medlem
Skrivet av klk:

Hade det varit bra om du kunde mappa hela databasen med en fyra fem api

Det låter också som ett väldigt dumt use-case, varför mappa hela databasen via 4-5 API? Varför inte skapa ett API som ger användaren ett interface direkt till pg_dump isf? ;D

Skämt åsido men sure, gör som du vill. Det är inte särskilt svårt.

Permalänk
Datavetare
Skrivet av klk:

Det är för att du inte nappar på arkitektur. Du märker inte när du läser koden

Här har du ett pattern som sällan används av andra vilket jag tycker är märklighet men det är extremt skalbart och effektivt
The command dispatcher pattern (finns några olika varianter på samma sak)

Vad har det med något av det som diskuteras att göra?

Har rätt bra koll på design-patterns, men anser att deras värde delvis kommer av att vi har flera populära programspråk som kraftigt uppmuntrar till OOP, trots att det är en teknik som må fungera bra på vissa specifika fall men som tyvärr är löjligt överanvänt.

Vad det gäller commander pattern sitter jag med den just nu. Sitter med UI-kod och commander pattern används av bla WPF, WinUI, Noesis, Avalona m.fl.

Men hur var det nu med ett exempel som faktiskt påvisar något som bara C++ kan lösa?

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

Det låter också som ett väldigt dumt use-case, varför mappa hela databasen via 4-5 API? Varför inte skapa ett API som ger användaren ett interface direkt till pg_dump isf? ;D

Skämt åsido men sure, gör som du vill. Det är inte särskilt svårt.

Ett av api för selectfrågor ett för insert ett för update och ett för delete, är det dumt?

Permalänk
Medlem
Skrivet av klk:

Ett av api för selectfrågor ett för insert ett för update och ett för delete, är det dumt?

Det är metoder/operationer enl. min terminologi och inte API. API för mig är grupperingen av resurser, metoder/operationer och möjligen access-kontroll om inte det hanteras av en reverse proxy eller load balancer.

Men visst om man inte vill använda HTTP metoder för hantera CRUD i samma API så för all del ha ett separat API per metod/operation.

Permalänk
Medlem

Ahhh, jag ser att vi äntligen börjar närma oss endgame här i tråden!

klk har gjort ett väldigt omfattande och gediget förarbete genom att konsekvent ignorera 98% av varje detaljerat, uppbackat och välformulerat svar han fått på sina påståenden; istället har han helt enkelt plockat ut en enskild irellevant detalj i de sista 2% och använt den för att oändligt osmidigt byta spår till ett annat ämne, fullständigt orelaterat hans grundpåstående som svaret handlade om.

Detta för att nu äntligen i slutskedet ha byggt upp tillräckligt med frustration och face-palming hos sweclockers mest erfarna programmerare så att de rakt av kommer att lösa hans skol eller arbetsuppgift på ett så snyggt och smidigt sätt som möjligt.
ggwp!

Permalänk
Medlem
Skrivet av orp:

Det är metoder/operationer enl. min terminologi och inte API. API för mig är grupperingen av resurser, metoder/operationer och möjligen access-kontroll om inte det hanteras av en reverse proxy eller load balancer.

Men visst om man inte vill använda CRUD för att hantera dom delarna så för all del ha ett separat API per metod/operation.

Äntligen börjar diskussionen blir intressant och komma in på lösningar?

Varför tänker du CRUD? CRUD är troligen en av de sämsta skitarkitekturerna som något kommit på. Enda fördelen är att den är enkel för juniora utvecklare

Om vi tar exemplet med att man istället gör fyra API eller jag väljer faktiskt 5.
1 = SELECT frågor
2 = UPDATE
3 = INSERT
4 = UPSERT (antingen uppdatera eller insert)
5 = DELETE

Då kan man göra serveranrop som ser ut så här

/execute/44411 = "UPSERT,UPSERT,UPSERT,SELECT ,SELECT"

Tre stycken update och två select i ett request. Då kanske någon tänker, men varför skall jag göra så. Det ser ju krångligt ut

Då har den utvecklare aldrig gjort något speciellt svårt. Kan man packa anrop blir det mycket enklare att jobba mot servern. Prestanda går upp eftersom det blir färre anrop.

Tidigare i tråden pratade vi lite om att inte göra en server stateless, att servern skall snurra hela tiden.
Om servern kan hålla state kan man läsa in information.

Alla? relationsdatabaser stödjer exempelvis information_schema. Kör man ODBC (C/C++ och en del andra språk stödjer det) är man heller inte knuten till en speciell databas.

Då kan servern läsa in det mesta om databasen och med det kan man bygga generella regelverk och bygga SQL dynamiskt.

Men den här tekniken kan servern göras fristående från klienten (frontend), och då kan man kasta ut bygg-pipelinen. Allt blir så mycket enklare och det går att skala upp till mycket stora system utan att det blir allt för mycket för utvecklare att hålla i huvudet.

Server (backend) och frontend är decoupled

Permalänk
Medlem
Skrivet av klk:

Äntligen börjar diskussionen blir intressant och komma in på lösningar?

Varför tänker du CRUD? CRUD är troligen en av de sämsta skitarkitekturerna som något kommit på. Enda fördelen är att den är enkel för juniora utvecklare

Om vi tar exemplet med att man istället gör fyra API eller jag väljer faktiskt 5.
1 = SELECT frågor
2 = UPDATE
3 = INSERT
4 = UPSERT (antingen uppdatera eller insert)
5 = DELETE

Då kan man göra serveranrop som ser ut så här

/execute/44411 = "UPSERT,UPSERT,UPSERT,SELECT ,SELECT"

Tre stycken update och två select i ett request. Då kanske någon tänker, men varför skall jag göra så. Det ser ju krångligt ut

Då har den utvecklare aldrig gjort något speciellt svårt. Kan man packa anrop blir det mycket enklare att jobba mot servern. Prestanda går upp eftersom det blir färre anrop.

Tidigare i tråden pratade vi lite om att inte göra en server stateless, att servern skall snurra hela tiden.
Om servern kan hålla state kan man läsa in information.

Alla? relationsdatabaser stödjer exempelvis information_schema. Kör man ODBC (C/C++ och en del andra språk stödjer det) är man heller inte knuten till en speciell databas.

Då kan servern läsa in det mesta om databasen och med det kan man bygga generella regelverk och bygga SQL dynamiskt.

Men den här tekniken kan servern göras fristående från klienten (frontend), och då kan man kasta ut bygg-pipelinen. Allt blir så mycket enklare och det går att skala upp till mycket stora system utan att det blir allt för mycket för utvecklare att hålla i huvudet.

Server (backend) och frontend är decoupled

Om detta är en intressant diskussion så får du gärna starta en separat tråd om API-design.

Permalänk
Medlem
Skrivet av orp:

Om detta är en intressant diskussion så får du gärna starta en separat tråd om API-design.

?

Det är ju just det här som är poängen med diskussionen vi har nu. Det jag visade är inte "standard" men det förenklar mycket. Och kod återanvänds, investerad programmerings tid i en server kan användas i flera olika system.

Har nämnt tidigare i tråden att de bästa utvecklarna är från 90 talet eller möjligen strax efter 2000. Orsaken är att på den tiden behövde man fundera ut lösningar själv. Då var det ont om standardlösningar.

Idag när utvecklare lär sig "så här gör man" är det så mycket svårare och förklara att så är det inte alls. Man kan göra väldigt mycket bättre

Permalänk
Skrivet av klk:

Vad jag vet finns inget språk som kan abstrahera så mycket som C++. Det går att skriva C++ kod så att python kodare skulle bli avundsjuka

Ooops, you did it again.

Här kom det ett klassiskt @klk -uttalande, svepande penseldrag och uttalat med sån säkerhet att alla måste tro att det är sant och att det inte behöver backas upp med någon form av fakta. Återigen måste jag syna ditt uttalande.

Vilka abstraktionsmekanismer har C++? Vilka av dessa saknas i de språk som vi täckt tidigare i tråden, typ Python, Rust, Go, C# och Java? Fyll gärna på med andra språk, kanske Zig och Carbon Här kan du klara dig undan på "vad jag vet", men jag skulle vilja veta vad du faktiskt vet om denna fråga och vad du baserat ditt uttalande på.

Permalänk
Medlem
Skrivet av Ingetledigtnamn:

Vilka abstraktionsmekanismer har C++? Vilka av dessa saknas i de språk som vi täckt tidigare i tråden, typ Python, Rust, Go, C# och Java? Fyll gärna på med andra språk, kanske Zig och Carbon Här kan du klara dig undan på "vad jag vet", men jag skulle vilja veta vad du faktiskt vet om denna fråga och vad du baserat ditt uttalande på.

Om man jämför C++ templates med de andra, hur väl står de sig? Och hur är det med kompilatorns kunskap om koden den skannar av för hur mycket smart kod som går att göra beror mycket på hur mycket kompilatorn vet om koden.

C++ får ofta kritik för kompileringstider och det är absolut en nackdel, kanske den största. Men kompilatorn vet mycket om koden och det är också användbart.
Tar man det senaste och det som kommer, tänker på concepts och snart reflection (osäker på vad för finesser de hittat på där). Det är tekniker som underlättar för att generera kod.

Något jag själv använder mycket är strängar som används för att producera kod i compiletime (constseval, constexpr).

Annan stor fördel är att stl är genomtänkt. Namn liknar varandra vilket gör att kod fungerar bättre om man använder templates.

När jag tittat på rust så anser jag att detta är deras största misstag. Det som följer med verkar ha missat att generera kod, att jag tror det beror på att de har förkortningar och metoder som inte ser så där likvärdiga ut.

Tipsar om två bibliotek för att visa vad C++ kan (och då är de inte gjorda med det nyaste)
sol2 - https://github.com/ThePhD/sol2
compile-time-regular-expressions - https://github.com/hanickadot/compile-time-regular-expression...

Permalänk
Medlem
Skrivet av klk:

Det är ju just det här som är poängen med diskussionen vi har nu.

Nja ursprunget till det sidospåret var att visa att andra språk fyller sin funktion och kan reducera bl a kodmängden jämfört med C och C++. Eftersom vi långt bak i diskussionen sa att det finns alternativa språk om minnessäkerhet är prio där du började påstå alla möjliga saker om X går inte för i språk Y och Z går enbart att göra i C++.

Jag anser därför att en diskussion om API-design förtjänar en separat tråd för nu har vi passerat off-topic gränser tillräckligt mycket för att jag iaf ska dra i bromsen. Jag diskuterar gärna API-design i en annan tråd.

Permalänk
Medlem
Skrivet av orp:

Nja ursprunget till det sidospåret var att visa att andra språk fyller sin funktion och kan reducera bl a kodmängden jämfört med C och C++. Eftersom vi långt bak i diskussionen sa att det finns alternativa språk om minnessäkerhet är prio där du började påstå alla möjliga saker om X går inte för i språk Y och Z går enbart att göra i C++.

Jag anser därför att en diskussion om API-design förtjänar en separat tråd för nu har vi passerat off-topic gränser tillräckligt mycket för att jag iaf ska dra i bromsen. Jag diskuterar gärna API-design i en annan tråd.

Görs en lösning som kanske har en kodmängd som är en tiondel eller mindre ändå, och som återanvänder kod. Är inte det säkrare än större kodmängd som är hårdkodad

Permalänk

Jag börjar tro att du kanske har en alternativ betydelse av abstraktion relativt oss andra.

Skrivet av klk:

Om man jämför C++ templates med de andra, hur väl står de sig?

Tja, Rust, Java och C# har Generics som ger liknande funktionalitet. Python saknar templates, men har med sin duck-typing inget behov av dessa. Har objektet bara en funktion med rätt namn så funkar det.

Skrivet av klk:

Tar man det senaste och det som kommer, tänker på concepts och snart reflection (osäker på vad för finesser de hittat på där). Det är tekniker som underlättar för att generera kod.

Concepts är en form av constraints under instansiering. Dess stora finess är att ge mer lättförståeliga fel från template-instansieringen. SFINAE i modern C++1X gav alldeles för kryptiska fel när något gick fel i instansieringen. Rusts Traits är väl en snarlik mekanism?

Det är bra att du själv påpekar att detta inte är en abstraktionsmekanism.

Skrivet av klk:

Något jag själv använder mycket är strängar som används för att producera kod i compiletime (constseval, constexpr).

Nu är väl den stora poängen med constexpr och consteval just att man slipper generera kod. Definitivt en feature, men är detta en abstraktionsmekanism? Jag skulle nog snarast klassa det som en optimering.

Skrivet av klk:

Annan stor fördel är att stl är genomtänkt. Namn liknar varandra vilket gör att kod fungerar bättre om man använder templates.

Och detta gäller inte för andra språk? Du får allt komma med lite exempel på att andra språk inte har en enhetlig namngivning i biblioteken.

Skrivet av klk:

När jag tittat på rust så anser jag att detta är deras största misstag. Det som följer med verkar ha missat att generera kod, att jag tror det beror på att de har förkortningar och metoder som inte ser så där likvärdiga ut.

Jag hade lite svårt att parsa den meningen.

Skrivet av klk:

Tipsar om två bibliotek för att visa vad C++ kan (och då är de inte gjorda med det nyaste)
sol2 - https://github.com/ThePhD/sol2
compile-time-regular-expressions - https://github.com/hanickadot/compile-time-regular-expression...

OK. Compile-time regular expression är ett cool trick, det måste jag medge. Men är det verkligen en abstraktionsmekanism? Traditionellt handlar väl abstraktion om att gömma implementationsdetaljer och minska komplexitet? Constexpr och consteval exponerar all implementation och komplexitet, men låter kompilatorn evaluera det vid compile-time. Till nöds kan jag sträcka mig till att man abstraherar bort evalueringstillfället.

Kan vi inte skippa sagoberättandet och istället utgå från denna länk när du skall beskriva vilka abstraktionsmekanismer i C++ som saknas i andra språk? Du skrev

Skrivet av klk:

Vad jag vet finns inget språk som kan abstrahera så mycket som C++. Det går att skriva C++ kod så att python kodare skulle bli avundsjuka

Nu är det upp till bevis!

Permalänk
Medlem
Skrivet av klk:

Om man jämför C++ templates med de andra, hur väl står de sig? Och hur är det med kompilatorns kunskap om koden den skannar av för hur mycket smart kod som går att göra beror mycket på hur mycket kompilatorn vet om koden.

Det är väl du som skall svara på det. Det är du som påstår att C++ är så mycket bättre än allt annat, så då borde du veta hur andra språk står sig mot C++ i olika avseenden.

Fast det är klart, att döma av den här tråden så vet du nästan ingenting om andra programmeringsspråk än C++, vilket är en stor anledning till att dina påståenden om andra språk är fyllda med felaktigheter. Anser du att jag har fel om den saken så får du gärna försöka motbevisa mig.

Permalänk
Medlem
Skrivet av Ingetledigtnamn:

Rust, Java och C# har Generics som ger liknande funktionalitet.

Det är de väl ändå inte va, inte i närheten ens. templates är mycket kraftfullare och blir snabbt bättre med. Många av de som sitter och bestämmer vad som skall in C++ älskar ju templates.

Permalänk
Medlem
Skrivet av klk:

Det är de väl ändå inte va, inte i närheten ens. templates är mycket kraftfullare och blir snabbt bättre med. Många av de som sitter och bestämmer vad som skall in C++ älskar ju templates.

Det är samma grupp ur språkperspektiv: templates, generics och macros.

Permalänk
Medlem
Skrivet av orp:

Det är samma grupp ur språkperspektiv: templates, generics och macros.

Ok, men C++ är 100% compile time. Hur är det med den saken och de andra språken?

Går detta i rust exempelvis

/// create rule for `variant_view` and `variant` template <typename VARIANT> concept IsVariant = std::is_same_v<VARIANT, gd::variant> || std::is_same_v<VARIANT, gd::variant_view>; /// `std::get` like function for `variant` and `variant_view` template <typename RETURN_TYPE, typename VARIANT> requires IsVariant<VARIANT> RETURN_TYPE get(const VARIANT& v_) { if constexpr( std::is_same_v<RETURN_TYPE, bool> ) return v_.as_bool(); else if constexpr( std::is_same_v<RETURN_TYPE, uint32_t> ) return v_.as_uint(); else if constexpr( std::is_same_v<RETURN_TYPE, uint64_t> ) return v_.as_uint64(); else if constexpr( std::is_same_v<RETURN_TYPE, int32_t> ) return v_.as_int(); else if constexpr( std::is_same_v<RETURN_TYPE, int64_t> ) return v_.as_int64(); else if constexpr( std::is_same_v<RETURN_TYPE, double> ) return v_.as_double(); else if constexpr( std::is_same_v<RETURN_TYPE, std::string> ) return v_.as_string(); else return (RETURN_TYPE)v_; }

Permalänk
Datavetare
Skrivet av klk:

Ok, men C++ är 100% compile time. Hur är det med den saken och de andra språken?

Går detta i rust exempelvis

/// create rule for `variant_view` and `variant` template <typename VARIANT> concept IsVariant = std::is_same_v<VARIANT, gd::variant> || std::is_same_v<VARIANT, gd::variant_view>; /// `std::get` like function for `variant` and `variant_view` template <typename RETURN_TYPE, typename VARIANT> requires IsVariant<VARIANT> RETURN_TYPE get(const VARIANT& v_) { if constexpr( std::is_same_v<RETURN_TYPE, bool> ) return v_.as_bool(); else if constexpr( std::is_same_v<RETURN_TYPE, uint32_t> ) return v_.as_uint(); else if constexpr( std::is_same_v<RETURN_TYPE, uint64_t> ) return v_.as_uint64(); else if constexpr( std::is_same_v<RETURN_TYPE, int32_t> ) return v_.as_int(); else if constexpr( std::is_same_v<RETURN_TYPE, int64_t> ) return v_.as_int64(); else if constexpr( std::is_same_v<RETURN_TYPE, double> ) return v_.as_double(); else if constexpr( std::is_same_v<RETURN_TYPE, std::string> ) return v_.as_string(); else return (RETURN_TYPE)v_; }

Rust

pub trait IsVariant { fn as_bool(&self) -> bool; fn as_u32(&self) -> u32; fn as_u64(&self) -> u64; fn as_i32(&self) -> i32; fn as_i64(&self) -> i64; fn as_f64(&self) -> f64; fn as_string(&self) -> String; } pub trait FromVariant<V: IsVariant>: Sized { fn from_variant(v: &V) -> Self; } macro_rules! impl_from_variant { ($target:ty, $method:ident) => { impl<V: IsVariant> FromVariant<V> for $target { fn from_variant(v: &V) -> Self { v.$method() } } }; } impl_from_variant!(bool, as_bool); impl_from_variant!(u32, as_u32); impl_from_variant!(u64, as_u64); impl_from_variant!(i32, as_i32); impl_from_variant!(i64, as_i64); impl_from_variant!(f64, as_f64); impl_from_variant!(String, as_string);

Go

type IsVariant interface { AsBool() bool AsUint() uint32 AsUint64() uint64 AsInt() int32 AsInt64() int64 AsDouble() float64 AsString() string } func Get[T any](v IsVariant) T { var zero T switch any(zero).(type) { case bool: return any(v.AsBool()).(T) case uint32: return any(v.AsUint()).(T) case uint64: return any(v.AsUint64()).(T) case int32: return any(v.AsInt()).(T) case int64: return any(v.AsInt64()).(T) case float64: return any(v.AsDouble()).(T) case string: return any(v.AsString()).(T) default: return any(v).(T) } }

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:

Det verkar vara samma, men syntaxen... En del klagar hårt på C++ templates, det här var faktiskt värre

Skrivet av Yoshman:

Tolkar jag Go koden rätt så är det inte samma, det är runtime där

Permalänk
Datavetare
Skrivet av klk:

Det verkar vara samma, men syntaxen... En del klagar hårt på C++ templates, det här var faktiskt värre

Tolkar jag Go koden rätt så är det inte samma, det är runtime där

Går att skriva Rust-versionen på ett långt enklare sätt, om man inte är van med Rust-macros. Men blir fler rader då och exakt samma runtime-prestanda.

Helt korrekt kring Go, ska man absolut efterlikna ditt C++ API krävs en dynamisk check. Men en annan killer-feature i Go är att det använder sig av compile-time duck-typing. Så om vi antar att man använder variant:en på följande sätt (BTW: variant är rejäl code-smell, behövs det är det nog något som inte står helt rätt till)

#include "get.hpp" #include <benchmark/benchmark.h> uint32_t fib(gd::variant v) { auto n = get<uint32_t>(v); if (n < 2) { return n; } return fib(gd::variant(int32_t(n - 1))) + fib(gd::variant(int32_t(n - 2))); } static void BM_GetInt32(benchmark::State &state) { gd::variant v(int32_t(35)); for (auto _ : state) { benchmark::DoNotOptimize(fib(v)); } } BENCHMARK(BM_GetInt32);

Så är en Go variant denna vars huvudmål är att se så lika ut C++ varianten. C++ versionen är ca dubbelt så snabb som denna

func fib(v IsVariant) uint32 { n := Get[uint32](v) // <-- Use runtime-check to fetch right variant if n < 2 { return n } return fib(MyVariant{n - 1}) + fib(MyVariant{n - 2}) } func BenchmarkGetUint32(b *testing.B) { v := MyVariant{35} b.ResetTimer() for b.Loop() { _ = fib(v) } }

Fast givet det interface som specificerads går också detta att göra (och detta är "hur man borde gör i Go"), som på min dator körs 60 % snabbare jämfört med C++ varianten

func fib(v IsVariant) uint32 { n := v.AsUint() // <-- Use compile-time-duck-typing-check to fetch right variant, i.e. this works for any type that implements the IsVariant interface if n < 2 { return n } return fib(MyVariant{n - 1}) + fib(MyVariant{n - 2}) } func BenchmarkGetUint32(b *testing.B) { v := MyVariant{35} b.ResetTimer() for b.Loop() { _ = fib(v) } }

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

Fick lite feeling och skrev ihop en typ av logik som är ganska enkel men som också behöver tänja lite på språket för att det också skall bli lätt att skriva kod. Lätt att förstå utan att nörda ner sig

Följande länk går till en "archive" class. det handlar om att serializera data och den vill man gärna få smidig för att kunna skriva och läsa information så enkelt som möjligt.

Länken till hela headern (nästan all kod är i headern så det lilla som ligger i en cpp behövs inte).
https://pastebin.com/4te3rTZD

Exempel kod i kommentarer men klistrar in det här också

* Usage example: * \code * // Writing to a file archive ar("output.bin", std::ios::out | std::ios::binary); uint32_t value = 42; ar << value; // Reading from a file archive archiveRead("output.bin", std::ios::in | std::ios::binary); uint32_t read_value; archiveRead >> read_value; * \endcode * * Write samples - shows how to write values to file in different ways * \code // pathFile = some file path archive ar_(pathFile, gd::io::tag_io_write{}); int iValue = 10, iValue2 = 20, iValue3 = 30; ar_.write(iValue).write(iValue2).write(iValue3); ar_.write_all(iValue, iValue2, iValue3); ar_ << iValue << iValue2 << iValue3; std::string s1 = "1111111", s2 = "2222222", s3 = "3333333"; ar_.write_all(s1, s2, s3); archive ar2_(pathFile2, gd::io::tag_io_write{}); gd::argument::shared::arguments arguments( {{"one", "one"}, {"one", "one"}, {"one", "one"}, {"one", "one"}, {"one", "one"}}); ar2_.write_block( (uint64_t)arguments.buffer_size(), arguments.data() ); \endcode Read samples - shows how to read values from file in different ways * \code // pathFile = some file path archive ar_(pathFile, gd::io::tag_io_read{}); int iValueRead = 0, iValue2Read = 0, iValue3Read = 0; ar_.read(iValueRead).read(iValue2Read).read(iValue3Read); ar_.read_all(iValueRead, iValue2Read, iValue3Read ); ar_ >> iValueRead >> iValueRead >> iValueRead; archive ar2_(pathFile2, gd::io::tag_io_read{}); std::string s1, s2, s3; ar2_.read_all(s1, s2, s3); archive ar2_(pathFile2, gd::io::tag_io_write{}); gd::argument::shared::arguments arguments( {{"one", "one"}, {"one", "one"}, {"one", "one"}, {"one", "one"}, {"one", "one"}}); ar2_.write_block( (uint64_t)arguments.buffer_size(), arguments.data() ); ar2_.close(); ar2_.open(pathFile, gd::io::tag_io_read{}); gd::argument::shared::arguments argumentsRead; ar2_.read_block64( [&argumentsRead](uint64_t uSize) -> void* { argumentsRead.reserve( uSize ); argumentsRead.buffer_set_size( uSize ); return (void*)argumentsRead.data(); }); * \endcode

Är självklart hemmablind då jag är van vid C++ men min reaktion när jag ser andra språk och de skall göra likartade saker, det är att koden ser krångligare ut. Men jag tror att många som jämför kod mellan C++ och andra språk jämför saker som är färdigjorda. Och så tänker inte alltid C++ utvecklare. I C++ skrivs så mycket själv, medan annat språk, där väljs eventuellt att leta upp någon serialize klass även om det bara tar några timmar att skriva ihop en.

Hur skulle delar av ovanstående kod se ut i andra språk?

Permalänk
Medlem
Skrivet av flexm:

Detta för att nu äntligen i slutskedet ha byggt upp tillräckligt med frustration och face-palming hos sweclockers mest erfarna programmerare så att de rakt av kommer att lösa hans skol eller arbetsuppgift på ett så snyggt och smidigt sätt som möjligt.
ggwp!

This! Det verkar mer och mer troligt.

Skrivet av klk:

Fick lite feeling och skrev ihop en typ av logik som är ganska enkel...

Berätta gärna vad du menar att det är du har gjort här. Och då menar jag inte att du ska beskriva vad classen gör, utan mer generellt, vad du menar att det bevisar.

Jag erkänner gärna att jag är alldeles för dålig på C++, så det kanske är så att det faktiskt
(be)visar din briljans, men det känns som att du spelar ett ackord från Stairway to heaven, och tror att bara för att det ackordet finns i Stairway to heaven, så bevisar det att du hade kunnat skriva Stairway to heaven.

Problemet är att det är så generiskt och så vanligt förekommande att det inte bevisar någonting, utom möjligtvis på förekomsten av Dunning-Kruger.

Permalänk
Medlem
Skrivet av Xeonist:

This! Det verkar mer och mer troligt.

Eller så spetsa öronen för jag tror det är ovanligt att få så mycket "udda" tips i dagens strömlinjeformade utvecklingstekniker

Permalänk
Datavetare
Skrivet av klk:

Fick lite feeling och skrev ihop en typ av logik som är ganska enkel men som också behöver tänja lite på språket för att det också skall bli lätt att skriva kod. Lätt att förstå utan att nörda ner sig

Följande länk går till en "archive" class. det handlar om att serializera data och den vill man gärna få smidig för att kunna skriva och läsa information så enkelt som möjligt.

Länken till hela headern (nästan all kod är i headern så det lilla som ligger i en cpp behövs inte).
https://pastebin.com/4te3rTZD

Exempel kod i kommentarer men klistrar in det här också

* Usage example: * \code * // Writing to a file archive ar("output.bin", std::ios::out | std::ios::binary); uint32_t value = 42; ar << value; // Reading from a file archive archiveRead("output.bin", std::ios::in | std::ios::binary); uint32_t read_value; archiveRead >> read_value; * \endcode * * Write samples - shows how to write values to file in different ways * \code // pathFile = some file path archive ar_(pathFile, gd::io::tag_io_write{}); int iValue = 10, iValue2 = 20, iValue3 = 30; ar_.write(iValue).write(iValue2).write(iValue3); ar_.write_all(iValue, iValue2, iValue3); ar_ << iValue << iValue2 << iValue3; std::string s1 = "1111111", s2 = "2222222", s3 = "3333333"; ar_.write_all(s1, s2, s3); archive ar2_(pathFile2, gd::io::tag_io_write{}); gd::argument::shared::arguments arguments( {{"one", "one"}, {"one", "one"}, {"one", "one"}, {"one", "one"}, {"one", "one"}}); ar2_.write_block( (uint64_t)arguments.buffer_size(), arguments.data() ); \endcode Read samples - shows how to read values from file in different ways * \code // pathFile = some file path archive ar_(pathFile, gd::io::tag_io_read{}); int iValueRead = 0, iValue2Read = 0, iValue3Read = 0; ar_.read(iValueRead).read(iValue2Read).read(iValue3Read); ar_.read_all(iValueRead, iValue2Read, iValue3Read ); ar_ >> iValueRead >> iValueRead >> iValueRead; archive ar2_(pathFile2, gd::io::tag_io_read{}); std::string s1, s2, s3; ar2_.read_all(s1, s2, s3); archive ar2_(pathFile2, gd::io::tag_io_write{}); gd::argument::shared::arguments arguments( {{"one", "one"}, {"one", "one"}, {"one", "one"}, {"one", "one"}, {"one", "one"}}); ar2_.write_block( (uint64_t)arguments.buffer_size(), arguments.data() ); ar2_.close(); ar2_.open(pathFile, gd::io::tag_io_read{}); gd::argument::shared::arguments argumentsRead; ar2_.read_block64( [&argumentsRead](uint64_t uSize) -> void* { argumentsRead.reserve( uSize ); argumentsRead.buffer_set_size( uSize ); return (void*)argumentsRead.data(); }); * \endcode

Är självklart hemmablind då jag är van vid C++ men min reaktion när jag ser andra språk och de skall göra likartade saker, det är att koden ser krångligare ut. Men jag tror att många som jämför kod mellan C++ och andra språk jämför saker som är färdigjorda. Och så tänker inte alltid C++ utvecklare. I C++ skrivs så mycket själv, medan annat språk, där väljs eventuellt att leta upp någon serialize klass även om det bara tar några timmar att skriva ihop en.

Hur skulle delar av ovanstående kod se ut i andra språk?

Med andra ord: till väldigt nära 100 % kommer din kritik mot andra språk från att du helt enkelt inte har en aning om hur de faktiskt fungerar.

Finns en brilliant presentation av Rich Hickey just kring det temat (för det är tyvärr rätt vanligt att tänka exakt så)
https://www.infoq.com/presentations/Simple-Made-Easy/

I.e. folk blandar ihop "easy/hard" (lätt/svårt) med "simple/complex" (enkelt/komplicerat). Det man tror är att man gör saker som är enkla, inte komplexa. Men många gånger gör man onödigt komplexa saker bara för att de råkar vara lätt(are) för just dig din tidigare erfarenhet gör att du vet hur problemet ska lösas med en viss teknik.

Och till Richs ära, ditt exempel i Clojore (disclaimer, lärde mig Clojure och en del funktionella språk under åren jag forskade i multicore optimering, men insåg att även om det i teorin är bra så höll inte praktiken med... Så är nybörjare i Clojure, det är en lisp och gjort en hel del i Emacs-lisp, men skiljer sig ändå rejält)

Det är inte en perfekt översättning då många objekt lever betydligt längre än nödvändigt i ditt exempel och just det (som sällan i.o.f.s är önskvärt) är inte helt lätt att efterlikna i Lisp. Saker lever rätt mycket så kort som möjligt i koden nedan

Klicka för mer information

(ns example (:require [ar])) (defn -main [] ;; Writing to a file (let [ar (ar/open "output.bin" :write)] (-> ar (ar/write 42))) ;; Reading from a file (let [archive-read (ar/open "output.bin" :read) read-value (ar/read archive-read)] (println "Read value:" read-value)) ;; Write samples (let [path-file "data.bin" ar_ (ar/open path-file :write) i-value 10 i-value2 20 i-value3 30] (-> ar_ (ar/write i-value) (ar/write i-value2) (ar/write i-value3)) (ar/write-all ar_ i-value i-value2 i-value3) (-> ar_ (ar/write i-value) (ar/write i-value2) (ar/write i-value3)) (ar/write-all ar_ "1111111" "2222222" "3333333")) ;; Block writing (let [path-file2 "block.bin" ar2_ (ar/open path-file2 :write) arguments {"one" "one" "two" "two" "three" "three"} ;; pretend representation buffer (.getBytes "onetwo..." "UTF-8") ;; mock arguments.data() size (count buffer)] ;; mock buffer_size (ar/write-block ar2_ size buffer)) ;; Read samples (let [ar_ (ar/open "data.bin" :read) i-value-read (ar/read ar_) i-value2-read (ar/read ar_) i-value3-read (ar/read ar_)] (println "Read:" i-value-read i-value2-read i-value3-read) (let [[a b c] (ar/read-all ar_ 3)] (println "Read all ints:" a b c)) (dotimes [_ 3] (println "Read (>>) " (ar/read ar_)))) ;; Read strings (let [ar2_ (ar/open "data.bin" :read) [s1 s2 s3] (ar/read-all ar2_ 3)] (println "Strings:" s1 s2 s3)) ;; Rewrite block and reopen (let [ar2_ (ar/open "block2.bin" :write) args {"one" "one" "one" "one" "one" "one"} data (.getBytes "aaaaabbbbbccccc" "UTF-8")] (ar/write-block ar2_ (count data) data) (ar/close ar2_) (let [ar2_ (ar/open "block2.bin" :read) arguments-read (atom nil)] (ar/read-block64 ar2_ (fn [size] (let [buf (byte-array size)] (reset! arguments-read buf) buf))))))

Visa mer

En viktig orsak att Go blivit populärt i flera väldigt stora organisationer är att språket är otroligt lätt att lära sig (specifikationerna för språk som C++ och C# känns som ett skämt i deras komplexitet jämfört med Go). Personligen tycker jag också det är väldigt lätt att läsa, men kring det råder delade meningar (gissar att bl.a. C-programmerar har lite extra lätt att lära sig Go för det är hyfsat mycket ett modernt C)

"defer" är ett nyckelord i Go för att implementera RAII. Den closure som står efter "defer" körs när man går ur existerande block, om flera "defer" använts körs saker i LIFO-ordning.

Ett vanligt klagomål på Go är "jag hatar alla 'if checkIfSomethingFailed'". Personligen anser jag att exceptions ger en brutalt falsk säkerhet, de tenderas användas väldigt fel och rent krasst är exceptions en modern "goto on steriods". I.e. föredrar att ha felhantering väldigt explicit, likt C uppmuntrar det också till att skriva funktioner som inte kan misslyckas så långt det går!

Klicka för mer information

package main import ( "fmt" "log" "yourmodule/archive" ) func main() { path := "output.bin" // Writing { ar, err := archive.Open(path, true) if err != nil { log.Fatal(err) } defer ar.Close() var i1, i2, i3 = int32(10), int32(20), int32(30) if err := ar.Write(i1, i2, i3); err != nil { log.Fatal(err) } // Write strings for _, s := range []string{"1111111", "2222222", "3333333"} { if err := ar.WriteString(s); err != nil { log.Fatal(err) } } // Write block block := []byte("some large binary blob...") if err := ar.WriteBlock(block); err != nil { log.Fatal(err) } } // Reading { ar, err := archive.Open(path, false) if err != nil { log.Fatal(err) } defer ar.Close() var r1, r2, r3 int32 if err := ar.Read(&r1, &r2, &r3); err != nil { log.Fatal(err) } fmt.Println("Read ints:", r1, r2, r3) // Read strings for i := 0; i < 3; i++ { s, err := ar.ReadString() if err != nil { log.Fatal(err) } fmt.Println("Read string:", s) } // Read block block, err := ar.ReadBlock() if err != nil { log.Fatal(err) } fmt.Println("Read block:", string(block)) } }

Visa mer
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:

Med andra ord: till väldigt nära 100 % kommer din kritik mot andra språk från att du helt enkelt inte har en aning om hur de faktiskt fungerar.

Det är möjligt men i det du beskriver just nu så verkar jag ha förstått rätt, att min kritik stämmer. Och att det snarare handlar om att du missuppfattar C++.

Som jag skrivit tidigare i varianter. C++ är inte gjort för att vara lätt, det är inte gjort för att använda sig av ett paket med funktionalitet som följer med språket. C++ är ett verktyg för att möjliggöra bästa möjliga lösning (utan att börja hacka assembler).

Att andra språk är "enklare" vet jag också. Men vad som är enkelt i ett språk är inte samma sak som att det skulle vara enkelt att skriva kompletta applikationer som skall installeras hos kunder eller köras av väldigt många användare.

Min poäng med mitt senaste C++ exempel var att visa hur "enkelt" det är att själv skriva en lösning som fungerar flexibelt utan extra bibliotek.

Andra språk har inte det målet, andra språk försöker göra det så lätt som möjligt att byta ut programmerare. Om du tar ett mycket vanligt konsultspråk, och jag skriver konsultspråk eftersom när bolag hyr in konsulter så kommer de också bytas ut. Då måste koden skrivas på ett sätt så att det går att byta ut konsulten.
C# är vanligt och orsaken är bl.a. att C# skeppar med ett gigantiskt bibliotek med funktionalitet som backas upp av Microsoft. Företag känner sig trygga med det och programmerare skall inte lösa saker själva, de skall använda .NET.

C# och C++ är två totalt olika språk, samma med java. De är varandras motpoler.
java skeppar också en herrans massa funktionalitet.

Skrivet av Yoshman:

Det är inte en perfekt översättning då många objekt lever betydligt längre än nödvändigt i ditt exempel och just det (som sällan i.o.f.s är önskvärt) är inte helt lätt att efterlikna i Lisp. Saker lever rätt mycket så kort som möjligt i koden nedan

Exakt, det är verkligen ingen översättning. Det är en helt annan lösning för du plockar där in ett bibliotek som någon annan skrivit åt dig. Och det är självklart bra för utvecklare då de slipper skriva koden själv men det visar brister i språket. Istället för att lära sig programmera behöver du lära dig bibliotek.

Lite tidigare diskuterades lösningar weblösningr och vi (inte du och jag) kom in på CRUD lösningar. CRUD är fantastiskt populärt för att det är lätt. Men är det bra? Verkligen inte.
CRUD lösning görs för att det är lätt att byta ut utvecklare. Det fungerar hyffsat för en till tre utvecklare om antalet tabeller håller sig under 25 och inte kräver allt för komplicerad hantering. Går man över är det stor risk att allt plötsligt tar väldigt lång tid. Skall de göra större system brukar de därför splitta upp databaslösningar på olika sätt, kan vara microservices eller annat så varje utvecklare slipper komma ihåg.

I C++ tänker man inte så (jag vet att det finns men de har missuppfattat). Att göra en CRUD lösning i C++ är galenskap.

Permalänk
Medlem
Skrivet av klk:

Och så tänker inte alltid C++ utvecklare. I C++ skrivs så mycket själv, medan annat språk, där väljs eventuellt att leta upp någon serialize klass även om det bara tar några timmar att skriva ihop en.

Intressant om det stämmer. Vad är det för brister i C++ jämfört med dessa andra språk som orsakar det fenomenet?

Har man tillgång till färdig, fungerande kod som löser problemet så är det ju oftast rakt av korkat att återuppfinna hjulet genom att skriva egen ny kod istället.
Är det för besvärligt att hitta sådana klasser till C++?
Eller är C++ kod för krånglig att återanvända?
Eller är sådan C++ kod allmänt av såpass undermålig kvalitet så att man inte vågar använda den?
Eller är det C++ programmerarna det är fel på, som lider av NIH syndrom?

Permalänk
Medlem
Skrivet av Erik_T:

Intressant om det stämmer. Vad är det för brister i C++ jämfört med dessa andra språk som orsakar det fenomenet?

Det är ingen brist utan språket är så kraftfullt att du kan göra det mesta bättre själv

Skrivet av Erik_T:

Har man tillgång till färdig, fungerande kod som löser problemet så är det ju oftast rakt av korkat att återuppfinna hjulet genom att skriva egen ny kod istället.

Menar du att det är riskfritt att använda annans kod?
Skall du göra ett system som skall ha en livslängd på +20 år, då begränsar det ordentligt vad du kan plocka in. Du måste tänka annorlunda jämfört med om livslängden är kanske 3 år innan det behöver börja refaktoreras.
Externa bibliotek är att bygga in sig.

Med det menar jag inte att man aldrig lägger med externa bibliotek i C++. Det gör jag också men inte alls på samma sätt som när man gör det i andra språk. Personligen så handlar det nästan uteslutande om extern funktionalitet där källkoden är med och kompileras upp. Är det inte källkoden som är med skall det väldigt mycket till för att ta med det.

Mitt första program jag skrev som utvecklare, jobbade 7 månader på ett bolag och skrev då ihop en. mitten på 1990 talet. Det säljs fortfarande och omsätter runt 50 miljoner varje år. Det skrev för Windows 3.11 och Microsoft MFC användes som ramverk.

Att C++ lösningar kan leva så länge är för många mycket viktigare än att slänga ihop något som endast fungerar för vad just det företaget har behov av.

Permalänk
Medlem
Skrivet av Erik_T:

Intressant om det stämmer. Vad är det för brister i C++ jämfört med dessa andra språk som orsakar det fenomenet?

Kan ta ett exempel här för att beskriva och som jag tror är lätt att förstå.
XML och JSON är supervanligt att använda för att transportera data. De flesta klarar sig med formatet. De flesta nya språk måste snabbt lägga till bibliotek/funktionalitet för att hantera dessa format om språken skall kunna locka till sig användare.

Microsoft och sitt .NET vet jag har logik för det och de har även med XPATH för att kunna selektera XML, minns jag rätt har de till och med två versioner. Men man är alltså begränsad till vad som finns i .NET. Skall du hitta andra bibliotek för att hantera så vanliga format som JSON och XML är det väldigt tomt.

C++ har flera stycken open source för att hantera formaten. Värstingen för XML tror jag är Xerecs from mozilla. Den har det mesta och har för mig att det är den som används för olika plugins i editorer när de skall ha funktionalitet för att smidigt editera xml.
pugixml är vanligt om man vill ha XPATH men samtidigt hålla nere komplexiteten. Har man inte behov av att selektera xml finns en hel hög med bibliotek.

För JSON vet jag att jsoncons har selekteringsmöjligheter, två olika faktiskt. Annars är de flesta bibliotek i C++ för JSON oftast gjorda för hastighet eller enkelhet att jobba med.

Jämför jag med hur C# utvecklare tänker när de jobbar med json jämfört med C++ så för att måla med bred pensel skiljer det.

i C# används json för att transportera data till alla DTO objekt. Microsoft har byggt funktionalitet kring detta och därför är det lätt. De skall inte experimentera själva.

Skulle någon C++ utvecklare säga till mig att börja göra DTO objekt i C++ och skeppa data med json är det dags för den utvecklaren att växla upp. I C++ kan du göra så mycket mer för att få till smidiga lösningar. Att bygga ett system men +500 tabeller i en databas med C++ är inte speciellt svårt. Men det är helt omöjligt med CRUD lösning