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

Permalänk
Medlem
Skrivet av klk:

Som jag skrev tidigare, den här diskussionen går inte att föra i sådant här media. Det blir för mycket bös och går ändå inte att förklara

Det är inte ämnet i sig som gör att du inte kan förklara det. Utan det beror helt på din bristande förmåga att förklara. Du gör inte ens några seriösa försök att förklara någonting, utan svamlar bara iväg på ovidkommande sidospår istället.

Permalänk
Medlem

Även om det varit extremt intressanta diskussioner och argument här i tråden så känns det nu som att den helt tappat vad originaltråden skapades för.

Nu har det mer blivit att en person styr diskussionen genom sina absurda åsikter och bristande härledningsförmåga, och alla andra försöker bara få honom att inse hur fel han har utan att det har nån som helst effekt. Är det inte dags att inse att det inte är värt att försöka nå fram till honom och låta tråden vara för c++ och minnessäkerhet istället?

Permalänk
Medlem
Skrivet av martengooz:

Även om det varit extremt intressanta diskussioner och argument här i tråden så känns det nu som att den helt tappat vad originaltråden skapades för.

Håller med om att tråden spårar ur och tänkte låta bli och svara om det är svårt att förklara. Men ville bara understryka det du skrev för de tekniskt intresserade som läser i tråden så finns det gott om bra tips. Skit i pajkastningen och fokusera på tekniken

Kommer ett inlägg snart som är skryt, får kritik hela tiden för att jag inte kan förklara men hur gör man det utan att det uppfattas som skryt? Det är mitt stora problem i och kunna svara

Permalänk
Datavetare
Skrivet av klk:

SIMD är ganska ovanligt att skriva kod för enligt mig, det får processorn avgöra eftersom det krävs bra förutsättningar för att göra det och det tar ofta för lång tid att pilla med sådant. Men vet jag om att det är någon passande operation där man bara skall gå igenom en lång rad med data, enkel operation och det passar för SIMD så gör jag en buffer (som är alignad), kopierar ett block eller möjligen läser minne från olika ställen och opererar på buffern. Tror det är kanske den vanligaste optimeringen och det är enkelt och debugvänligt.
Exakt hur beror på vad som skall göras. Ligger data i en lång rad perfekt för SIMD slipper man det men då bör man tänka igen hela lösningen och det kostar i tid och underhåll.
Risken med att skriva egen SIMD kod är eventuellt att nya processorer och kompilatorer plötsligt kan göra det bättre själva men så har man långsammare hårdkodad lösning.

Så för att sammanfatta: du använder inte SIMD alls egentligen.

Intel m.fl. lade rätt mycket resurser på att försöka göra auto-vektorisering till något som kunde ge en relevant boost via SIMD. Det gick inget vidare och skulle varit väldigt kul att höra diskussionerna hos Intel när ett skunkworks som en person jobbade på sopade banan med delen av Intels kompilator-team som jobbade hårt med auto-vektorisering.

Men för att motbevisa mig: posta gärna en Godbolt-länk som visar hur mycket SIMD då faktiskt får ut!

std::simd i C++/Rust kommer långtifrån lösa allt, men det har absolut en poäng för det tar portabel C++/Rust från att i praktiken sakna SIMD stöd om man håller sig till standarden till att i alla fall ha ett bra grundläggande stöd.

Skrivet av klk:

Det vanligaste är vad jag tror att dra nytta av flera kärnor och kan man göra det så enkelt som möjligt underlättar det ordentligt. En del använder OpenMP eller liknande men jag tror att ofta så vet de inte om det blir snabbare, hade de mätt prestanda är det inte säker att den trådade delen är snabbare än en enkeltrådad.

Right! De som jobbar med HPC i systemet närmare hundratals CPU-kärnor per nod lär ju knappast veta vad de håller på med, för varför skulle de annars använda bl.a. OpenMP?

Det skrivet: OpenMP har en hel del vassa hörn, är inte själv speciellt bekväm med OpenMP trots att jag använt det en del samt implementerat OpenMP stöd i det RTOS jag jobbade många år med.

Men finns andra alternativ. För C++ lär Intel TBB vara det mest naturliga. Sen finns ju sedan ett par generationer också parallel execution i standard C++, fungerar ihop med de flesta algoritmer (GCC har i nuläget baserat sin std:: implementation på just Intel TBB).

För just detta föredrar jag personligen Rust Rayon, tycker det är lättast att använda och det är snabbt.

Skrivet av klk:

Annars så senaste gångerna jag kollat genererad assemblerkod och haft optimering för SIMD så är kompilatorerna rätt duktiga på att hitta ställen lite över allt där de kan optimera. Men man bör ju veta att minnet ligger i cache, se till så det ligger där för annars blir det att elda för kråkorna
Av det jag sett kan kompilatorer idag räkna ut en hel del om kod för att förstå hur de skall processa information utan att programmeraren behöver "hjälpa" kompilatorn. Gjorde en del sådana test för några år sedan och det spelade minimal roll om jag "berättade" för kompilatorn att data låg på ett visst sätt jämfört med att den försökte själv.

Hur ser man till att saker ligger i cache? För CPUer som är aktuella idag finns ju ett brutalt span på cache-storlek. Från 8-16 kB på enklare embedded CPUer upp till hundratals MB på de mest avancerade server-modellerna. Vilken optimerar man för?

Sen utanför ett par special-system har man inte direktkontroll över cache. Har varit i diskussioner med bl.a. HW Intel-arkitekter kring varför de inte ger sådan access. Svaret är rätt naturligt, om de ger ut ett publikt API för detta blir de fast att supporta det "forever".

Går att göra lite saker med x86 prefetch instruktioner. DPDK har med hyfsat stor framgång utnyttjat dessa för att få CPUn att läsa in _nästa_ IP-pakets headers medan man processar föregående paket.

Skrivet av klk:

Jag tvivlar, det är så mycket säljsnack i sådant här och de testat saker med perfekta förhållanden. Sedan gör man något och märker att det inte fungerar så bra som de påstår.
Vet lösningar som var full med OpenMP kod och så togs det bort och det blev snabbare.

Och jag vet C++ program som är långsammare än motsvarande Python, men det säger ingenting relevant...

Det finns väldigt många som med stor framgång använder OpenMP, Intel TBB, Rust Rayon m.fl.

Skrivet av klk:

Området är för stort för att diskutera här men om du jobbat med stl så kommer du märka att om du lärt dig en containerklass så vet du nästan hur alla de andra fungerar. Det vet du eftersom de följer samma mönster.
begin(), end(), emtpy(), size(), clear(), insert(), erase(), find() är vanliga. de finns inte på alla men så många olika metoder behöver man inte veta och det går ganska enkelt och lista ut resten.
Det blir också enklare att använda ett ord för metoden vilket är viktigt. Samma med konstruktorer och överlagrade operatorer. Man behöver inte minnas så mycket olika namn och det är lättare att få upp hastigheten när man slipper bråka med namnen

Skriver någon egna C++ objekt och följer namnstandarden är det lätt för andra utvecklare som kan stl och förstå objekten

Ovanstående var alltså ett exempel på hur man förenkla arbetet med kod

Och det går naturligtvis att göra med andra aktuella språk

Concept / Operation

C++ STL

Rust

Go

C#

Iterate (begin/end)

c.begin(), c.end()

iter(), iter_mut(), into_iter()

for _, v := range ...

foreach (var x in c)

Empty?

c.empty()

is_empty()

len(c) == 0

c.Count == 0

Size

c.size()

len()

len(c)

c.Count

Clear

c.clear()

clear()

slice: s = s[:0]; map: clear(m)

c.Clear()

Insert (vector/list)

v.insert(pos, val)

v.insert(i, val)

s = append(s[:i], val, s[i:]...)

list.Insert(i, val)

Insert (set)

s.insert(v)

set.insert(v) → bool

set[v] = struct{}{}

hashSet.Add(v) → bool

Insert (map)

m.insert(k, v)

m.insert(k, v) → Option

m[k] = v

dict[k] = v / dict.Add(k, v)

Erase by key

m.erase(k)

map.remove(&k)

delete(m, k)

dict.Remove(k)

Erase by index

v.erase(it)

v.remove(i), swap_remove(i)

s = append(s[:i], s[i+1:]...)

list.RemoveAt(i)

Remove by value

v.remove(val)

vec.retain(|x| ...)

manual filter

list.Remove(val) / hashSet.Remove(val)

Find (map)

m.find(k)

m.get(&k) → Option

v, ok := m[k]

dict.TryGetValue(k, out v) / dict.ContainsKey(k)

Find (set)

s.find(v)

set.contains(&v)

_, ok := set[v]

hashSet.Contains(v)

Capacity

v.capacity()

v.capacity()

cap(s)

list.Capacity

Reserve

v.reserve(n)

v.reserve(n)

make([]T, 0, n)

list.EnsureCapacity(n) / dict.EnsureCapacity(n)

Push back

v.push_back(x)

v.push(x)

s = append(s, x)

list.Add(x)

Pop back

v.pop_back()

v.pop()

x, s = s[len-1], s[:len-1]

x = list[^1]; list.RemoveAt(list.Count-1)

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

Även om det varit extremt intressanta diskussioner och argument här i tråden så känns det nu som att den helt tappat vad originaltråden skapades för.

Nu har det mer blivit att en person styr diskussionen genom sina absurda åsikter och bristande härledningsförmåga, och alla andra försöker bara få honom att inse hur fel han har utan att det har nån som helst effekt. Är det inte dags att inse att det inte är värt att försöka nå fram till honom och låta tråden vara för c++ och minnessäkerhet istället?

Vad det gäller trådens ursprungliga ämne maler kvarnarna rätt långsamt.

Även om bl.a. Bjarne sagt att man måste agera är det ju lite svårt att hålla högt tempo givet en release var 3:e år. Men han verkar ändå se allvarligt på att allt fler rätt mycket säger: använde inte C eller C++ i nya projekt om ni inte absolut måste, det finns allt för mycket potentiella säkerhetsproblem som många nyare språk i praktiken löst.

Den mest intressanta diskussionen kring C++ utveckling är nog den om man primärt bör göra det bästa för all existerande kod, fokusera främst på stabilitet.

Eller om man ska försöka hänga med och slänga in "all the new cool stuff" bara för att försöka hålla sig relevant. Största nyheten i C++26 är reflektion. Finns rätt mycket kritik mot det också, det gör världens mest komplexa språk ännu mer komplext, reflektion lägger till rätt mycket nytt till standarden även i form av syntax.

I ett lite större scope ändå helt i linje med tråden blir det faktiskt fullt rimligt att börja prata om andra språk/ramverk. Givet att många av de problem man i C++ funderar på hur man bäst löser redan är löst i flera andra populära språk blir ju en uppenbar följdfråga: vilka eventuella hinder finns för att välja andra språk än C++?

C# är inte mitt favoritspråk (det har förvånansvärt många footguns...), men är så glad att valet ändå föll på det i projektet jag jobbar med nu. Enda realistiska valen var C++ och C#, vi valde det senare trots (eller p.g.a.?) att alla som var inblandade från start hade långt mer erfarenhet av C++ än av C#/.NET. Prestanda p.g.a. av språkval har inte blivit en flaskhals, trots en domän där "vettig hantering av minne" är viktigt. Ändå: anekdotiskt (3:e gången jag valt bort C++ p.g.a. möjlighet att göra det, i de två andra blev det Rust i ett fall och Go i ett annat).

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:

Och det går naturligtvis att göra med andra aktuella språk

Jag frågade AI hur man skulle implementera följande kod i Rust (klipt för att det skall gå och se)

variant() : m_uType(variant_type::eType variant( bool b ) : m_uType(variant_type::eType variant( int8_t v ) : m_uType(variant_type::eType variant( int16_t v ) : m_uType(variant_type::eType variant( int32_t v ) : m_uType(variant_type::eType variant( int64_t v ) : m_uType(variant_type::eType variant( uint8_t v ) : m_uType(variant_type::eType variant( uint16_t v ) : m_uType(variant_type::eType variant( uint32_t v ) : m_uType(variant_type::eType variant( uint64_t v ) : m_uType(variant_type::eType variant( float v ) : m_uType(variant_type::eType variant( double v ) : m_uType(variant_type::eType variant( void* p ) : m_uType(variant_type::eType variant( const char* v ): m_uType(variant_type::eType #if defined(__cpp_char8_t) variant( const char8_t* v ): m_uType(variant_type::eT #endif variant( const wchar_t* v ): m_uType(variant_type::eT variant( const char* v, size_t uLength ): m_uType(var variant( const char* v, size_t uLength, bool ): m_uTy variant( const wchar_t* v, size_t uLength ): m_uType( variant( const wchar_t* v, size_t uLength, bool ) : m variant( const unsigned char* v, size_t uLength, bool variant( const utf8& v ) : m_uType(variant_type::eTyp variant( const utf8& v, unsigned int uType ) : m_uTyp variant( const utf8& v, bool ) : m_uType(variant_type variant( unsigned int uType, void* v, size_t uLength, variant( const uint8_t* v, size_t uLength ): m_uType( variant( const variant_type::guid& v ): m_uType(varia variant( const std::string& v ): m_uType(variant_type explicit variant( const std::string_view& v ): m_uTyp variant( const std::string& v, unsigned int uType ): variant(const char* v, bool) : m_uType(variant_type:: variant(const std::string_view& v, bool) : m_uType(va variant( const variant& o ) { common_construct( o ); variant( variant&& o ) noexcept { move_construct( o )

Fick det här förslaget

use std::mem; // Placeholder for the custom `utf8` type (assuming it’s a string-like type) #[derive(Clone)] struct Utf8 { data: String, length: usize, } // Placeholder for the `guid` type (assuming it’s a 16-byte structure) #[derive(Clone, Copy)] struct Guid { data: [u8; 16], } // Enum to represent the variant types (similar to `variant_type` in C++) #[derive(Clone, Copy, PartialEq)] enum VariantType { Unknown, Bool, Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, CFloat, CDouble, Pointer, String, Utf8String, WString, Binary, Guid, } // Struct to hold the variant data #[derive(Clone)] pub struct Variant { vtype: VariantType, size: usize, value: VariantValue, } // Enum to store the actual data #[derive(Clone)] enum VariantValue { Unknown, Bool(bool), Int8(i8), Int16(i16), Int32(i32), Int64(i64), UInt8(u8), UInt16(u16), UInt32(u32), UInt64(u64), CFloat(f32), CDouble(f64), Pointer(*mut u8), // Unsafe pointer for raw pointer compatibility String(String), // Owned string Utf8String(String), // Owned UTF-8 string WString(Vec<u16>), // Owned wide string Binary(Vec<u8>), // Owned binary data Guid(Guid), StringRef(&'static str), // Non-owning string reference } impl Variant { // Default constructor pub fn new() -> Self { Variant { vtype: VariantType::Unknown, size: 0, value: VariantValue::Unknown, } } // Constructor for bool pub fn from_bool(b: bool) -> Self { Variant { vtype: VariantType::Bool, size: mem::size_of::<bool>(), value: VariantValue::Bool(b), } } // Constructor for i8 pub fn from_i8(v: i8) -> Self { Variant { vtype: VariantType::Int8, size: mem::size_of::<i8>(), value: VariantValue::Int8(v), } } // Constructor for i16 pub fn from_i16(v: i16) -> Self { Variant { vtype: VariantType::Int16, size: mem::size_of::<i16>(), value: VariantValue::Int16(v), } } // Constructor for i32 pub fn from_i32(v: i32) -> Self { Variant { vtype: VariantType::Int32, size: mem::size_of::<i32>(), value: VariantValue::Int32(v), } } // Constructor for i64 pub fn from_i64(v: i64) -> Self { Variant { vtype: VariantType::Int64, size: mem::size_of::<i64>(), value: VariantValue::Int64(v), } } // Constructor for u8 pub fn from_u8(v: u8) -> Self { Variant { vtype: VariantType::UInt8, size: mem::size_of::<u8>(), value: VariantValue::UInt8(v), } } // Constructor for u16 pub fn from_u16(v: u16) -> Self { Variant { vtype: VariantType::UInt16, size: mem::size_of::<u16>(), value: VariantValue::UInt16(v), } } // Constructor for u32 pub fn from_u32(v: u32) -> Self { Variant { vtype: VariantType::UInt32, size: mem::size_of::<u32>(), value: VariantValue::UInt32(v), } } // Constructor for u64 pub fn from_u64(v: u64) -> Self { Variant { vtype: VariantType::UInt64, size: mem::size_of::<u64>(), value: VariantValue::UInt64(v), } } // Constructor for f32 pub fn from_f32(v: f32) -> Self { Variant { vtype: VariantType::CFloat, size: mem::size_of::<f32>(), value: VariantValue::CFloat(v), } } // Constructor for f64 pub fn from_f64(v: f64) -> Self { Variant { vtype: VariantType::CDouble, size: mem::size_of::<f64>(), value: VariantValue::CDouble(v), } } // Constructor for raw pointer pub fn from_ptr(p: *mut u8) -> Self { Variant { vtype: VariantType::Pointer, size: mem::size_of::<*mut u8>(), value: VariantValue::Pointer(p), } } // Constructor for owned string (from &str) pub fn from_str(v: &str) -> Self { Variant { vtype: VariantType::String, size: v.len(), value: VariantValue::String(v.to_string()), } } // Constructor for non-owning string (static lifetime) pub fn from_str_ref(v: &'static str) -> Self { Variant { vtype: VariantType::String, size: v.len(), value: VariantValue::StringRef(v), } } // Constructor for owned UTF-8 string pub fn from_utf8_string(v: &str) -> Self { Variant { vtype: VariantType::Utf8String, size: v.len(), value: VariantValue::Utf8String(v.to_string()), } } // Constructor for owned wide string (Vec<u16>) pub fn from_wstr(v: &[u16]) -> Self { Variant { vtype: VariantType::WString, size: v.len(), value: VariantValue::WString(v.to_vec()), } } // Constructor for owned binary data pub fn from_binary(v: &[u8]) -> Self { Variant { vtype: VariantType::Binary, size: v.len(), value: VariantValue::Binary(v.to_vec()), } } // Constructor for guid pub fn from_guid(v: Guid) -> Self { Variant { vtype: VariantType::Guid, size: mem::size_of::<Guid>(), value: VariantValue::Guid(v), } } // Constructor for custom Utf8 type pub fn from_utf8(v: &Utf8) -> Self { Variant { vtype: VariantType::Utf8String, size: v.length, value: VariantValue::Utf8String(v.data.clone()), } } // Constructor for custom type with raw data pub fn from_raw(vtype: VariantType, data: &[u8], length: usize) -> Self { Variant { vtype, size: length, value: VariantValue::Binary(data.to_vec()), } } } // Optional: Implement Drop to handle cleanup for types requiring it (e.g., pointers) impl Drop for Variant { fn drop(&mut self) { match self.value { // No manual cleanup needed for String, Vec, etc., as they are managed by Rust VariantValue::Pointer(_p) => { // If the pointer needs manual cleanup, implement it here (unsafe) // For now, we assume the pointer is managed externally } _ => {} } } }

Dold text

I C++ beöver jag inte minnas något extra mer än objektes namn och att det är en massa konstruktorer, samma sak i rust och det är en hel hög nya namn som behöver introduceras

Permalänk
Medlem
Skrivet av klk:

Kommer ett inlägg snart som är skryt, får kritik hela tiden för att jag inte kan förklara men hur gör man det utan att det uppfattas som skryt? Det är mitt stora problem i och kunna svara

Det känns som du har någon konstig prestige i skryt. Varför är skryt viktigt för dig?

Jag tycker att det är ok att folk som Torvalds, Dan J. Bernstein eller Ulrich Drepper skryter men dom har ju faktiskt åstadkommit något.

Om du bara ger sakliga och tekniska beskrivningar utan saker som "90% av utvecklare förstår sig inte på det jag beskriver" så kommer det inte uppfattas som skryt.

Permalänk
Medlem
Skrivet av orp:

Jag tycker att det är ok att folk som Torvalds, Dan J. Bernstein eller Ulrich Drepper skryter men dom har ju faktiskt åstadkommit något.

försöker hålla mig borta från att svara dig men det du skriver är intressant, för om de personer gått in på forum med annat namn, att ingen visste vilka de var. då hade de inte kunnat prata, de hade blivit sågade vid fotknölarna

Torvalds som är benhård på att skriva C kod med 8 spaces indentering, du fattar själv..

Permalänk
Medlem
Skrivet av klk:

försöker hålla mig borta från att svara dig men det du skriver är intressant, för om de personer gått in på forum med annat namn, att ingen visste vilka de var. då hade de inte kunnat prata, de hade blivit sågade vid fotknölarna

Jag tror inte att dom hade blivit sågade vid fotknölarna eftersom dom hade gett svar med högre kvalitet.

Skrivet av klk:

Torvalds som är benhård på att skriva C kod med 8 spaces indentering, du fattar själv..

Alltså nu är du igång igen ... Vad har 8 spaces med något att göra? Vet du ens varför det är 8 spaces?

Permalänk
Datavetare
Skrivet av klk:

Jag frågade AI hur man skulle implementera följande kod i Rust (klipt för att det skall gå och se)

variant() : m_uType(variant_type::eType variant( bool b ) : m_uType(variant_type::eType variant( int8_t v ) : m_uType(variant_type::eType variant( int16_t v ) : m_uType(variant_type::eType variant( int32_t v ) : m_uType(variant_type::eType variant( int64_t v ) : m_uType(variant_type::eType variant( uint8_t v ) : m_uType(variant_type::eType variant( uint16_t v ) : m_uType(variant_type::eType variant( uint32_t v ) : m_uType(variant_type::eType variant( uint64_t v ) : m_uType(variant_type::eType variant( float v ) : m_uType(variant_type::eType variant( double v ) : m_uType(variant_type::eType variant( void* p ) : m_uType(variant_type::eType variant( const char* v ): m_uType(variant_type::eType #if defined(__cpp_char8_t) variant( const char8_t* v ): m_uType(variant_type::eT #endif variant( const wchar_t* v ): m_uType(variant_type::eT variant( const char* v, size_t uLength ): m_uType(var variant( const char* v, size_t uLength, bool ): m_uTy variant( const wchar_t* v, size_t uLength ): m_uType( variant( const wchar_t* v, size_t uLength, bool ) : m variant( const unsigned char* v, size_t uLength, bool variant( const utf8& v ) : m_uType(variant_type::eTyp variant( const utf8& v, unsigned int uType ) : m_uTyp variant( const utf8& v, bool ) : m_uType(variant_type variant( unsigned int uType, void* v, size_t uLength, variant( const uint8_t* v, size_t uLength ): m_uType( variant( const variant_type::guid& v ): m_uType(varia variant( const std::string& v ): m_uType(variant_type explicit variant( const std::string_view& v ): m_uTyp variant( const std::string& v, unsigned int uType ): variant(const char* v, bool) : m_uType(variant_type:: variant(const std::string_view& v, bool) : m_uType(va variant( const variant& o ) { common_construct( o ); variant( variant&& o ) noexcept { move_construct( o )

Fick det här förslaget

use std::mem; // Placeholder for the custom `utf8` type (assuming it’s a string-like type) #[derive(Clone)] struct Utf8 { data: String, length: usize, } // Placeholder for the `guid` type (assuming it’s a 16-byte structure) #[derive(Clone, Copy)] struct Guid { data: [u8; 16], } // Enum to represent the variant types (similar to `variant_type` in C++) #[derive(Clone, Copy, PartialEq)] enum VariantType { Unknown, Bool, Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, CFloat, CDouble, Pointer, String, Utf8String, WString, Binary, Guid, } // Struct to hold the variant data #[derive(Clone)] pub struct Variant { vtype: VariantType, size: usize, value: VariantValue, } // Enum to store the actual data #[derive(Clone)] enum VariantValue { Unknown, Bool(bool), Int8(i8), Int16(i16), Int32(i32), Int64(i64), UInt8(u8), UInt16(u16), UInt32(u32), UInt64(u64), CFloat(f32), CDouble(f64), Pointer(*mut u8), // Unsafe pointer for raw pointer compatibility String(String), // Owned string Utf8String(String), // Owned UTF-8 string WString(Vec<u16>), // Owned wide string Binary(Vec<u8>), // Owned binary data Guid(Guid), StringRef(&'static str), // Non-owning string reference } impl Variant { // Default constructor pub fn new() -> Self { Variant { vtype: VariantType::Unknown, size: 0, value: VariantValue::Unknown, } } // Constructor for bool pub fn from_bool(b: bool) -> Self { Variant { vtype: VariantType::Bool, size: mem::size_of::<bool>(), value: VariantValue::Bool(b), } } // Constructor for i8 pub fn from_i8(v: i8) -> Self { Variant { vtype: VariantType::Int8, size: mem::size_of::<i8>(), value: VariantValue::Int8(v), } } // Constructor for i16 pub fn from_i16(v: i16) -> Self { Variant { vtype: VariantType::Int16, size: mem::size_of::<i16>(), value: VariantValue::Int16(v), } } // Constructor for i32 pub fn from_i32(v: i32) -> Self { Variant { vtype: VariantType::Int32, size: mem::size_of::<i32>(), value: VariantValue::Int32(v), } } // Constructor for i64 pub fn from_i64(v: i64) -> Self { Variant { vtype: VariantType::Int64, size: mem::size_of::<i64>(), value: VariantValue::Int64(v), } } // Constructor for u8 pub fn from_u8(v: u8) -> Self { Variant { vtype: VariantType::UInt8, size: mem::size_of::<u8>(), value: VariantValue::UInt8(v), } } // Constructor for u16 pub fn from_u16(v: u16) -> Self { Variant { vtype: VariantType::UInt16, size: mem::size_of::<u16>(), value: VariantValue::UInt16(v), } } // Constructor for u32 pub fn from_u32(v: u32) -> Self { Variant { vtype: VariantType::UInt32, size: mem::size_of::<u32>(), value: VariantValue::UInt32(v), } } // Constructor for u64 pub fn from_u64(v: u64) -> Self { Variant { vtype: VariantType::UInt64, size: mem::size_of::<u64>(), value: VariantValue::UInt64(v), } } // Constructor for f32 pub fn from_f32(v: f32) -> Self { Variant { vtype: VariantType::CFloat, size: mem::size_of::<f32>(), value: VariantValue::CFloat(v), } } // Constructor for f64 pub fn from_f64(v: f64) -> Self { Variant { vtype: VariantType::CDouble, size: mem::size_of::<f64>(), value: VariantValue::CDouble(v), } } // Constructor for raw pointer pub fn from_ptr(p: *mut u8) -> Self { Variant { vtype: VariantType::Pointer, size: mem::size_of::<*mut u8>(), value: VariantValue::Pointer(p), } } // Constructor for owned string (from &str) pub fn from_str(v: &str) -> Self { Variant { vtype: VariantType::String, size: v.len(), value: VariantValue::String(v.to_string()), } } // Constructor for non-owning string (static lifetime) pub fn from_str_ref(v: &'static str) -> Self { Variant { vtype: VariantType::String, size: v.len(), value: VariantValue::StringRef(v), } } // Constructor for owned UTF-8 string pub fn from_utf8_string(v: &str) -> Self { Variant { vtype: VariantType::Utf8String, size: v.len(), value: VariantValue::Utf8String(v.to_string()), } } // Constructor for owned wide string (Vec<u16>) pub fn from_wstr(v: &[u16]) -> Self { Variant { vtype: VariantType::WString, size: v.len(), value: VariantValue::WString(v.to_vec()), } } // Constructor for owned binary data pub fn from_binary(v: &[u8]) -> Self { Variant { vtype: VariantType::Binary, size: v.len(), value: VariantValue::Binary(v.to_vec()), } } // Constructor for guid pub fn from_guid(v: Guid) -> Self { Variant { vtype: VariantType::Guid, size: mem::size_of::<Guid>(), value: VariantValue::Guid(v), } } // Constructor for custom Utf8 type pub fn from_utf8(v: &Utf8) -> Self { Variant { vtype: VariantType::Utf8String, size: v.length, value: VariantValue::Utf8String(v.data.clone()), } } // Constructor for custom type with raw data pub fn from_raw(vtype: VariantType, data: &[u8], length: usize) -> Self { Variant { vtype, size: length, value: VariantValue::Binary(data.to_vec()), } } } // Optional: Implement Drop to handle cleanup for types requiring it (e.g., pointers) impl Drop for Variant { fn drop(&mut self) { match self.value { // No manual cleanup needed for String, Vec, etc., as they are managed by Rust VariantValue::Pointer(_p) => { // If the pointer needs manual cleanup, implement it here (unsafe) // For now, we assume the pointer is managed externally } _ => {} } } }

Dold text

I C++ beöver jag inte minnas något extra mer än objektes namn och att det är en massa konstruktorer, samma sak i rust och det är en hel hög nya namn som behöver introduceras

Om någon gav ovan till mig i en code-review så räcker INTE högerbilden här... WTF skulle man vilja ha ovan till i något rationellt designat program???

Men nåväl, det viktigaste är väl ändå att det är effektivt samt lätt att använda. Då du har noll tester går det inte riktigt att se hur man skulle använda ditt exempel.

use core::fmt; use std::borrow::Cow; use std::ffi::{c_char, c_void, CStr}; use uuid::Uuid; #[derive(Clone, PartialEq)] pub enum Variant<'a> { Null, Bool(bool), I64(i64), U64(u64), F64(f64), Ptr(*mut c_void), Str(Cow<'a, str>), Bytes(Cow<'a, [u8]>), Guid(Uuid), } impl<'a> fmt::Debug for Variant<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use Variant::*; match self { Null => write!(f, "Null"), Bool(b) => write!(f, "Bool({b})"), I64(v) => write!(f, "I64({v})"), U64(v) => write!(f, "U64({v})"), F64(v) => write!(f, "F64({v})"), Ptr(p) => write!(f, "Ptr({p:p})"), Str(s) => write!(f, "Str({:?})", s), Bytes(b) => write!(f, "Bytes(len={})", b.len()), Guid(g) => write!(f, "Guid({g})"), } } } impl<'a> From<bool> for Variant<'a> { fn from(v: bool) -> Self { Variant::Bool(v) } } macro_rules! impl_from_ints_as { ($($t:ty => $arm:ident),* $(,)?) => { $(impl<'a> From<$t> for Variant<'a> { fn from(v: $t) -> Self { Variant::$arm(v as _) } })* } } impl_from_ints_as! { i8 => I64, i16 => I64, i32 => I64, i64 => I64, isize => I64, u8 => U64, u16 => U64, u32 => U64, u64 => U64, usize => U64, } impl<'a> From<f32> for Variant<'a> { fn from(v: f32) -> Self { Variant::F64(v as f64) } } impl<'a> From<f64> for Variant<'a> { fn from(v: f64) -> Self { Variant::F64(v) } } impl<'a> From<&'a str> for Variant<'a> { fn from(s: &'a str) -> Self { Variant::Str(Cow::Borrowed(s)) } } impl<'a> From<String> for Variant<'a> { fn from(s: String) -> Self { Variant::Str(Cow::Owned(s)) } } impl<'a> From<&'a [u8]> for Variant<'a> { fn from(b: &'a [u8]) -> Self { Variant::Bytes(Cow::Borrowed(b)) } } impl<'a> From<Vec<u8>> for Variant<'a> { fn from(b: Vec<u8>) -> Self { Variant::Bytes(Cow::Owned(b)) } } impl<'a> From<Uuid> for Variant<'a> { fn from(g: Uuid) -> Self { Variant::Guid(g) } } impl<'a> Variant<'a> { pub const fn null() -> Self { Variant::Null } pub fn from_ptr(p: *mut c_void) -> Self { Variant::Ptr(p) } pub fn from_c_str<'p>(ptr: *const c_char) -> Result<Variant<'p>, std::str::Utf8Error> { let cs = unsafe { CStr::from_ptr(ptr) }; let s = cs.to_str()?; // UTF-8 check Ok(Variant::Str(Cow::Borrowed(s))) } pub fn into_owned(self) -> Variant<'static> { use Variant::*; match self { Null => Null, Bool(b) => Bool(b), I64(v) => I64(v), U64(v) => U64(v), F64(v) => F64(v), Ptr(p) => Ptr(p), Str(s) => Str(Cow::Owned(s.into_owned())), Bytes(b) => Bytes(Cow::Owned(b.into_owned())), Guid(g) => Guid(g), } } } impl<'a> Variant<'a> { pub fn is_null(&self) -> bool { matches!(self, Variant::Null) } pub fn type_name(&self) -> &'static str { use Variant::*; match self { Null => "null", Bool(_) => "bool", I64(_) => "i64", U64(_) => "u64", F64(_) => "f64", Ptr(_) => "ptr", Str(_) => "str(utf8)", Bytes(_) => "bytes", Guid(_) => "guid", } } pub fn as_bytes(&self) -> Cow<'_, [u8]> { match self { Variant::Bytes(b) => Cow::Borrowed(b), Variant::Str(s) => Cow::Owned(s.as_bytes().to_vec()), _ => Cow::Owned(Vec::new()), } } pub fn as_str(&self) -> Option<&str> { match self { Variant::Str(s) => Some(s.as_ref()), _ => None, } } pub fn as_i64(&self) -> Option<i64> { if let Variant::I64(v) = self { Some(*v) } else { None } } pub fn as_u64(&self) -> Option<u64> { if let Variant::U64(v) = self { Some(*v) } else { None } } pub fn as_f64(&self) -> Option<f64> { if let Variant::F64(v) = self { Some(*v) } else { None } } } #[cfg(test)] mod tests { use super::*; #[test] fn basics() { let v = Variant::from(true); assert_eq!(v.type_name(), "bool"); let v = Variant::from(123i32); assert_eq!(v.as_i64(), Some(123)); let v = Variant::from(3.14f32); assert!((v.as_f64().unwrap() - 3.14).abs() < 1e-6); let v = Variant::from("hello"); assert_eq!(v.as_str(), Some("hello")); let g = Uuid::from_bytes([0x12; 16]); let v = Variant::from(g); assert!(matches!(v, Variant::Guid(_))); } #[test] fn into_owned_roundtrip() { let s = String::from("owned"); let v = Variant::from(s); let o = v.into_owned(); assert_eq!(o.as_str(), Some("owned")); } #[test] fn unsafe_c_str() { use std::ffi::CString; let cs = CString::new("utf8!").unwrap(); let v = Variant::from_c_str(cs.as_ptr()).unwrap(); assert_eq!(v.as_str(), Some("utf8!")); } }

Här är ett lite städad variant, betänk att jag inte är en supervan Rust-programmerare. Tester på slutet
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:

Torvalds som är benhård på att skriva C kod med 8 spaces indentering, du fattar själv..

Vidare... Du väljer coding style i dina projekt och Torvalds i sina. Om du inte gillar 8 spaces så strunta i att lägg patchar till kerneln så slipper du "problematiken" och skriv din egna jäkla kärna.

Torvalds har skrivit ett marknadsledande operativsystem och versionshanteringssystem men 8 spaces är det viktiga ... där slog du verkligen ihop dina två hjärnceller

Du har skrivit C++ kod som pillar in saker i en SQL-databas ... grattis

Permalänk
Medlem
Skrivet av Yoshman:

Om någon gav ovan till mig i en code-review så räcker INTE högerbilden här... WTF skulle man vilja ha ovan till i något rationellt designat program???

Nu flyttade du målstolpen en del va?
Just den här biten handlade om namngivning och överlagring

@orp påstår inte att jag är någon torvalds... men möjligen kan du någon gång i framtiden klara av att skilja på person och sak

Permalänk
Datavetare
Skrivet av klk:

Nu flyttade du målstolpen en del va?
Just den här biten handlade om namngivning och överlagring

Och du fick ett fungerade exempel, med tester. Så vad är problemet? Det är helt uppenbart görbart och att använda det hela var rätt smutt.

Edit: och vad är problemet här med överlagring och namngivning i Rust? Man får fler kompilator-garantier där jämfört med C++, tyvärr medför det ibland lite extra ceremoni för den som skriver bibliotek. Men allt gick väl ändå att göra?

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:

@orp påstår inte att jag är någon torvalds...

Det är du inte och varför kan du inte vara sluta med att kläcka ur dig så korkade påstående? Torvalds har åstadkommit mer än du och jag kommer göra så varför inte bara ge honom creds för det? Han har ju om inte annat lyckats vägleda sitt hobbyprojekt till att vara ett världsledande OS även om han föredrar 8 space indentering.

Varför är det 8 spaces indentering i kärnan? Vet du ens det? Där är liksom en baktanke med det.

Permalänk
Medlem
Skrivet av orp:

Det är du inte och varför är det 8 spaces indentering i kärnan?

Exakt, varför. Tänk när du börjar lära dig att det ibland finns anledningar till att man gör saker. Varför vissa saker passar bättre än andra, vad är det med HN som möjligen kan fungera även om det ser jobbigt ut i liten skala och så vidare.

Det finns oftast anledningar, speciellt om teknik används i stora projekt
HN används i Windows, Windows är mycket större än Linux, kan det finnas någon anledning?

Permalänk
Datavetare
Skrivet av klk:

HN används i Windows, Windows är mycket större än Linux, kan det finnas någon anledning?

Nu är Windows closed source, men de uppskattningar som finns på antal rader kernel-kod är långt mindre än dagens Linux-kärna.

Så oavsett om man räknar SLOC eller hur många enheter i världen som använder OS:en är Linux klart större

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:

Och du fick ett fungerade exempel, med tester. Så vad är problemet? Det är helt uppenbart görbart och att använda det hela var rätt smutt.

Edit: och vad är problemet här med överlagring och namngivning i Rust? Man får fler kompilator-garantier där jämfört med C++, tyvärr medför det ibland lite extra ceremoni för den som skriver bibliotek. Men allt gick väl ändå att göra?

Repeterar
Om du får en telefonbok med tusentals sidor, spelar det någon roll för hur snabbt du hittar i telefonboken?
Om du får en tjock skönlitterär bok på tusen sidor, hur snabbt hittar du i den?

Är du med?

Permalänk
Medlem
Skrivet av klk:

Exakt, varför. Tänk när du börjar lära dig att det ibland finns anledningar till att man gör saker. Varför vissa saker passar bättre än andra, vad är det med HN som möjligen kan fungera även om det ser jobbigt ut i liten skala och så vidare.

Det finns oftast anledningar, speciellt om teknik används i stora projekt
HN används i Windows, Windows är mycket större än Linux, kan det finnas någon anledning?

Fast nu sitter ju inte Torvalds och påstår att han kör 8 space indentering istället för enhetstestning eller att "koden blir mer scanvänlig för att det är inte viktigt att kunna läsa kod utan att scanna den"

Du får köra HN bäst du vill jag bryr mig inte men att påstå att "man inte kan ha stora kodprojekt" utan HN är ju objektivt falskt.

Permalänk
Medlem
Skrivet av klk:

Exakt, varför. Tänk när du börjar lära dig att det ibland finns anledningar till att man gör saker. Varför vissa saker passar bättre än andra, vad är det med HN som möjligen kan fungera även om det ser jobbigt ut i liten skala och så vidare.

Det finns oftast anledningar, speciellt om teknik används i stora projekt
HN används i Windows, Windows är mycket större än Linux, kan det finnas någon anledning?

För att Windows paketerar ihop sin kärna med massa annan skit?

Permalänk
Datavetare
Skrivet av klk:

Repeterar
Om du får en telefonbok med tusentals sidor, spelar det någon roll för hur snabbt du hittar i telefonboken?
Om du får en tjock skönlitterär bok på tusen sidor, hur snabbt hittar du i den?

Är du med?

Brukar fatta "data-prylar" rätt snabbt, men nä. Fattar inte vad du menar.

De överlägset viktigaste egenskapen är väl ändå hur lätt/svårt/effektivt det är att använda resultatet? Om det kräver lite mer jobb att få till det är i sammanhanget irrelevant.

(BTW: är exakt det Epic är ute efter med Verse. Deras observation är att även om det teoretiskt är möjligt att skriva allt i C++ lägger det så mycket av problematiken i knät på varje UE-användare att det i praktiken blir värdelöst. Med Verse hoppas man att en liten "elit" behöver tänka på de riktigt svåra multicore-problemen, användarna kan dra nytta av det rätt automagiskt. DET är vad man vill uppnå)

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:

De överlägset viktigaste egenskapen är väl ändå hur lätt/svårt/effektivt det är att använda resultatet? Om det kräver lite mer jobb att få till det är i sammanhanget irrelevant.

För mindre projekt, kanske under 50 000 rader kod spelar det mindre roll om koden är sökbar
Desto mer koden växer desto viktigare är det, är du över 200 000 rader så är det mycket viktigare och ingen läser sådan kod
Skrivs kod på ett visst sätt går det att se hur långt de kommer för som jag sagt, det finns gränser för hur mycket programmerare kan komma ihåg

Vad är det som är så svårt att förstå med det här?

Permalänk
Datavetare
Skrivet av klk:

För mindre projekt, kanske under 50 000 rader kod spelar det mindre roll om koden är sökbar
Desto mer koden växer desto viktigare är det, är du över 200 000 rader ungefär så är det mycket viktigare och ingen läser sådan kod
Skrivs kod på ett visst sätt går det att se hur långt de kommer för som jag sagt, det finns gränser för hur mycket programmerare kan komma ihåg

Fast detta

pub enum Variant<'a> { Null, Bool(bool), I64(i64), U64(u64), F64(f64), Ptr(*mut c_void), Str(Cow<'a, str>), Bytes(Cow<'a, [u8]>), Guid(Uuid), }

är betydligt kortare än detta

variant() : m_uType(variant_type::eType variant( bool b ) : m_uType(variant_type::eType variant( int8_t v ) : m_uType(variant_type::eType variant( int16_t v ) : m_uType(variant_type::eType variant( int32_t v ) : m_uType(variant_type::eType variant( int64_t v ) : m_uType(variant_type::eType variant( uint8_t v ) : m_uType(variant_type::eType variant( uint16_t v ) : m_uType(variant_type::eType variant( uint32_t v ) : m_uType(variant_type::eType variant( uint64_t v ) : m_uType(variant_type::eType variant( float v ) : m_uType(variant_type::eType variant( double v ) : m_uType(variant_type::eType variant( void* p ) : m_uType(variant_type::eType variant( const char* v ): m_uType(variant_type::eType #if defined(__cpp_char8_t) variant( const char8_t* v ): m_uType(variant_type::eT #endif variant( const wchar_t* v ): m_uType(variant_type::eT variant( const char* v, size_t uLength ): m_uType(var variant( const char* v, size_t uLength, bool ): m_uTy variant( const wchar_t* v, size_t uLength ): m_uType( variant( const wchar_t* v, size_t uLength, bool ) : m variant( const unsigned char* v, size_t uLength, bool variant( const utf8& v ) : m_uType(variant_type::eTyp variant( const utf8& v, unsigned int uType ) : m_uTyp variant( const utf8& v, bool ) : m_uType(variant_type variant( unsigned int uType, void* v, size_t uLength, variant( const uint8_t* v, size_t uLength ): m_uType( variant( const variant_type::guid& v ): m_uType(varia variant( const std::string& v ): m_uType(variant_type explicit variant( const std::string_view& v ): m_uTyp variant( const std::string& v, unsigned int uType ): variant(const char* v, bool) : m_uType(variant_type:: variant(const std::string_view& v, bool) : m_uType(va variant( const variant& o ) { common_construct( o ); variant( variant&& o ) noexcept { move_construct( o )

och båda ger bl.a. en katalog över vilka datatyper som kan lagras i varianten. Så du säger att Rust är telefonkatalogen?

I ett realistiskt use-case kommer ju ändå en modern text-editor med vettig language-server tala om för mig vad som passar i mitt nuvarande kontext. Så än mer kanske frågan är: vem ger bäst hints för riktigt bra intellisense?

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:

och båda ger bl.a. en katalog över vilka datatyper som kan lagras i varianten. Så du säger att Rust är telefonkatalogen?

I ett realistiskt use-case kommer ju ändå en modern text-editor med vettig language-server tala om för mig vad som passar i mitt nuvarande kontext. Så än mer kanske frågan är: vem ger bäst hints för riktigt bra intellisense?

Har försökt förklara och nej, det är inte något realistiskt exempel du gav. Vet inte hur jag enklare skall beskriva

Permalänk
Datavetare
Skrivet av klk:

Har försökt förklara och nej, det är inte något realistiskt exempel du gav. Vet inte hur jag enklare skall beskriva

Nu var det ju ändå du som gav kodexemplet. Lade bara in ett par tester som visar hur det kan användas i praktiken.

Vad var det du försökte visa med det då om det inte var "hur lätt/svårt är det att använda och/eller scanna för att se vad det kan göra"?

Skrivet av klk:

För mindre projekt, kanske under 50 000 rader kod spelar det mindre roll om koden är sökbar
Desto mer koden växer desto viktigare är det, är du över 200 000 rader så är det mycket viktigare och ingen läser sådan kod
Skrivs kod på ett visst sätt går det att se hur långt de kommer för som jag sagt, det finns gränser för hur mycket programmerare kan komma ihåg

Vad är det som är så svårt att förstå med det här?

Är faktiskt inte med alls på vad svårigheten skulle vara om man inte använder "supertekniker som HN och C++". Har ändå jobbat med rätt många kodbaser som är >1M rader kod utan att vare sig HN eller C++ användes, gick hur bra som helst! Och många andra var där och petade också.

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:

Nu var det ju ändå du som gav kodexemplet. Lade bara in ett par tester som visar hur det kan användas i praktiken.

Vad var det du försökte visa med det då om det inte var "hur lätt/svårt är det att använda och/eller scanna för att se vad det kan göra"?

När man skriver kod så bör den vara användbar, det finns som jag skrivit anledningar. Om enda syftet är att försöka skriva något som ser mindre ut men är oanvändbart så vad spelar det för roll?

Tog ut en liten del ur annan kod (endast konstruktörer), det är massor till runt allt det där
Att jag tog ut det var för att beskriva problemet med att sakna överlagring, och jag tog endast konstruktörer för en ganska simpel klass. Jag kan visa mer och det kommer explodera i språk som rust, saker som är betydligt enklare att hantera i exempelvis C++

Permalänk
Medlem
Skrivet av klk:

För mindre projekt, kanske under 50 000 rader kod spelar det mindre roll om koden är sökbar
Desto mer koden växer desto viktigare är det, är du över 200 000 rader så är det mycket viktigare och ingen läser sådan kod
Skrivs kod på ett visst sätt går det att se hur långt de kommer för som jag sagt, det finns gränser för hur mycket programmerare kan komma ihåg

Vad är det som är så svårt att förstå med det här?

Vad som är svårt att förstå är dels vad du menar med att "koden är sökbar".
Oavsett om koden är 10 rader eller 10 miljoner rader så kan jag ju använda grep eller något annat sökverktyg för att söka efter saker i koden.
Men då det inte blir svårare bara för att det blir fler rader kod så antar jag att du menar något annat med att "koden är sökbar". Så vad menar du?

Ännu svårare att förstå är exakt hur du menar att Hungarian Notation skulle hjälpa till med något?

Permalänk
Medlem
Skrivet av klk:

För mindre projekt, kanske under 50 000 rader kod spelar det mindre roll om koden är sökbar
Desto mer koden växer desto viktigare är det, är du över 200 000 rader så är det mycket viktigare och ingen läser sådan kod
Skrivs kod på ett visst sätt går det att se hur långt de kommer för som jag sagt, det finns gränser för hur mycket programmerare kan komma ihåg

Vad är det som är så svårt att förstå med det här?

Sökbar? Det är naturligtvis inte lätt för en enda person att utan hjälpmedel hålla reda på programkod oavsett storlek men med bra hjälpmedel, vettigt kommenterad kod, bra tester osv är det inga problem med en stor kodbas. Är det som i mitt största projekt fråga om c:a 360 000 rader kod är det inget en ensam person har koll på men där var det ett team på, om jag minns rätt, runt 25 personer. Var och en höll reda på sina delar med en hel del hjälp. Sedan är antalet kodrader ett trubbigt verktyg för att beskriva kod eftersom 360 000 rader kod i de språk vi använde mycket väl kunde vara 25% mindre om andra språk använts. Dessutom var runt 60% testkod. Och visst, ingen kan komma ihåg så många rader kod men man har hjälpmedel. Testerna hittar buggarna och anger var det blev fel. Det viktigaste är att hitta platsen där felet finns, att rätta felet brukar vara ganska trivialt.

Vid ett möte på Ericsson för många år sedan frågade någon om det inte var svårt med ett jätteprojekt man höll på med och fick till svar att "kodbasen för närvarande inte är större än 2 200 000 rader kod, är väl kommenterad och väl dokumenterad så det är inget problem"

Permalänk
Medlem
Skrivet av Erik_T:

Men då det inte blir svårare bara för att det blir fler rader kod så antar jag att du menar något annat med att "koden är sökbar". Så vad menar du?

Hur vet du vad du skall söka efter? För är det 10 miljoner rader kod så förstår alla att ingen kan komma ihåg det där, enda sättet att få så stora mängder kod hanterbar är att koden följer mönster, att det räcker att utvecklare lär sig mönstren så förstår den hur de kan hantera koden.
Tänk på telefonbok, så länge telefonboken följer sitt mönster så fungerar det. Minsta lilla "hack" i telefonboken och ingen kommer hitta detta hacket.

Har du regler så alla utvecklare skriver samma typ av kod kommer också alla utvecklare veta hur de söker i annans kod. De vet vad det skall söka efter. En regel för hur variabler och annat benämns gör det enklare för hela teamet.
Om man tvärtom får namnge saker till vad man känner för så lycka till med att söka efter saker, instanser av olika objekt kan ha många olika namn. metoder och annat kan också följa olika mönster. Lägg på dessäng problem, metoder som gör flera saker, kanske objekt där "här slänger vi in det här också". det går fort att förstöra kod

Skrivet av Erik_T:

Ännu svårare att förstå är exakt hur du menar att Hungarian Notation skulle hjälpa till med något?

HN är ett "mönster" och desto mer komplicerad kod eller desto mer den växer desto viktigare blir sådant här. HN är inte det enda för att hantera stora kodmängder utan de måste ta till alla möjliga trick för att klara av och hantera koden.

Små projekt på mindre än 10 000 rader går inte att jämföra med större

Bara att en utvecklare säger "men jag ser ju med intellisense", ja det gör man i små projekt. Vänta till de växer, då börjar det balla ur.
Söker du med grep i 10 miljoner rader kod så går inte det heller för då får så mycket träffar att det är svårt att få det effektivt.

Permalänk
Medlem
Skrivet av klk:

När man skriver kod så bör den vara användbar, det finns som jag skrivit anledningar. Om enda syftet är att försöka skriva något som ser mindre ut men är oanvändbart så vad spelar det för roll?

Tog ut en liten del ur annan kod (endast konstruktörer), det är massor till runt allt det där
Att jag tog ut det var för att beskriva problemet med att sakna överlagring, och jag tog endast konstruktörer för en ganska simpel klass. Jag kan visa mer och det kommer explodera i språk som rust, saker som är betydligt enklare att hantera i exempelvis C++

All kod skrivs med ett syfte (hoppas jag) och ska naturligtvis vara användbar. Och bespara oss flera kodexempel, de ger ingenting. Man väljer datastrukturer och algoritmer efter vad som är tillgängligt i det valda språket så jämförelserna är onödiga och vad du vill uppnå med exemplen är obegripligt. Att Rust inte är objektorienterat är väl känt och vad jag vet är det främst i objektorienterade språk som överlagring existerar, har inte stött på det i andra typer av språk och t.ex. i Python som har ett klassbegrepp finns inte överlagring.

Permalänk
Medlem
Skrivet av serafim:

All kod skrivs med ett syfte (hoppas jag) och ska naturligtvis vara användbar.

Känner du till data orienterad design (vilket är ett ganska nytt begrepp) för att beskriva en teknik för att få upp hastigheten. Idag pratar mest spelutvecklare om data orienterad design för det är mest de som hela tiden söker mer hastighet, i alla fall är just spel ett stort och vanligt område som söker mer prestanda.
Förr var i princip allt data orienterad design för saker gick så långsamt, man vart nära nog tvingad att tänka på prestanda vad man än gjorde.

Det ökade tröskeln ordentligt till vilka som klarade av att programmera.
Idag går det att lära sig programmera utan att förstå hur en processor fungerar eller vad som tar tid, förr vart alla mer eller mindre tvingade till att lära sig det.

Att färre lär sig DOD (data orineterad design) idag betyder inte att det är dålig, tvärtom. Det är fortfarande en fantastisk teknik för att kunna göra mycket mer. Också för att klara av att återanvända kod.
Så varför praktiseras inte det mer, för att det är en svårare programmering. Tröskeln är högre men de som lär sig blir mycket mer effektiva

Jobbar de i team där alla inte kan så måste de självklart anpassa sig eller se till så koden är uppdelad