Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer
C++ och dess framtid att programmera minnessäkert - Hur går utvecklingen?
Ä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?
Ä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
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.
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.
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.
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.
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) |
Ä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).
Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer
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
}
_ => {}
}
}
}
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
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.
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..
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.
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?
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
}
_ => {}
}
}
}
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!"));
}
}
Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer
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
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
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?
Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer
@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.
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?
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
Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer
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?
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.
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?
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å)
Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer
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?
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?
Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer
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
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"?
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å.
Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer
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++
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?
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"
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
Ä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.
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.
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
- Aggressivare påminnelser om att uppgradera Windows 1084
- Formel 1-tråden10k
- Köpråd för olika paket av delar1
- 14900k går varm med 360 aio15
- Planerat bygge 2026 - Vad ska jag hålla utkik efter?37
- Övriga fynd (bara tips, ingen diskussion) — Läs första inlägget först!1,6k
- C++ och dess framtid att programmera minnessäkert - Hur går utvecklingen?1,6k
- Dagens fynd — Diskussionstråden55k
- Plats för lite gubbgnäll13k
- Uppgradera gammal build3
- Skänkes Äldre skärmar (utan och med fot) 19-22"
- Skänkes Dell E5520 (Core i5 2520M, 8GB DDR3, ingen disk, inget batteri)
- Säljes Uppgraderingspaket I5 14600kf, Asus b760, 32GB ram
- Säljes Zalman reserator V1 Plus och V2 (Tornkylare)
- Säljes Pulsar X2V2 + 4K dongel, Ducky Mecha Mini cherry MX-red
- Säljes 5900x dator utan grafikkort
- Köpes Dator till 9-åring för Fortnite och liknande
- Säljes Intressekoll; Nintendo Switch v2
- Säljes Ubiquiti Access-Point UniFi U7-Pro-Wall – kvitto & garanti kvar + bordstativ
- Säljes Ubiquiti UniFi Dream Machine Pro (UDM Pro) – kvitto & garanti kvar
- Intel visar upp Clearwater Forest - Xeon med 288 E-kärnor13
- Allvarlig sårbarhet i Docker för Windows9
- Aggressivare påminnelser om att uppgradera Windows 1084
- När Mega Man ersatte dansband – så föddes Moderskeppet3
- Asrock hoppas stävja smältande kablar med temperatur-sensor44
- MSI satsar på hårdvaru-waifus31
- Snabbkoll: Har du blivit av med användarkonto?64
- Windows 95 fyller 3078
- AMD om brända moderkort: Komplext problem28
- Moddare kör Doom på laddare8
Externa nyheter
Spelnyheter från FZ
- Jag, min son och det där jäkla jättetrollet idag
- Playstations live service-floppar: "När vi misslyckas är det bättre att göra det tidigt" idag
- Läckra, cyberpunkiga Replaced släpps 2026 idag
- Forza Horizon 6 tros bli avtäckt på Tokyo Game Show igår
- MGS Delta: Snake Eater – högt betygssnitt tightar till FZ High Score igår