How I use C++: a line-by-line code review

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
10x developer what's his name Stuart Lynch 10x developer Stuart Lynch made a video series on what he called sane C plus plus he walked through his code base and his code style I'm going to do something similar but walking through my code base maybe we'll call it insane C plus blast but because compared to his style I'm a little bit weird so uh just like him I'm going to open a file and then we're going to talk a bit different features I use in C plus plus that might be interesting so the first thing you might notice is that this fonts is very comical I use the comic code font I find it readable I like it I enjoy it I'm not going to get in the history of what fonts I've used in the past but this is what I've settle on I've been using this for about two years and I'm using the font in Vim this is vim9 and I've used them for 15 years so it's more of an inertia thing I don't have any bells and whistles LSP server clang D CLS ccls uh so I don't have autocomplete at least not semantic autocomplete I don't have uh semantic syntax coloring I don't have go-to definition I use other tools for those things I'm also using gnome terminal so this is running in a terminal usually I have two terminal tabs open uh one for coding and then one for running commands uh like you know mostly git commands and stuff but sometimes other things so uh I'm open at this file utf-8.cpp so it's a utility uh utility file so at the top right here we see some code comments well not code comments some some C plus plus line comments so it says copyright and then you're in a name and then C enter the file for copyright information so what the free stuff this this project is GPO the free software Foundation recommends that you include a copyright notice in every file and I found that to be useful not for me working on the project but for me using other people's projects um sometimes I needed to consult some other code and maybe copy code from their project into mine and it's really nice when I have when I could just I don't have if there's no code comment say if there's no copyright comment saying where to find uh who the author is or what the license is I gotta go find it elsewhere and sometimes they make it easy sometimes they make it really hard I found finding the license is usually easy uh usually there's a file in the project called license though sometimes there's a caveat where they say some files are in this license some files are in that license and then I'm like well the file I'm looking at what license is it if the license information is right in the file then I can just tell um similarly the owner uh I have an owner's name or a corporation name or something is helpful um just to like dot the legal aspect and give proper attribution so licenses such as MIT license say you need to give attribution but if they don't if the project doesn't include a copyright line how am I supposed to give attribution including this information is helpful now you notice it says C end of file so if you jump to the end of the file here's the full copyright stuff originally when I started this project all the copyright stuff was at the top of the file that got really annoying because you open a file and half the screen is copyright comments so I decided to put at the bottom of the file I like that approach it does mean if I want to see something at the bottom of the file I have to deal with this but it's better than nothing um this is the wording that free software Foundation recommends for using the GPL so I just copy the wording another thing that they recommend is saying what part of what project it's from so if somebody copies my whole file I I can more easily find it because there's a reference to the project name so this project is called quickly.js so um not only is it uh there's attribution there's also where the project came what project came from that's nice if people are copy pasting code around so that's the copyright comment I put in all the files I put it in build Scripts so if you could look at the cmake lists uh it's here or whoops uh shell scripts and stuff you put it everywhere I have a tool that checks to make sure that it's in the right files oh there's a question you don't need to update the year I I don't know if you need to update the year the FSF does recommend you update the year every year when you touch the file I'm too lazy about that if it really matters I can go look at the git history and figure out what year was last touch and stuff I'm not that um I don't care that much at Facebook when I worked at Facebook they always had 2004 as the year um and then I think recently they got rid of the Year entirely such as copyright meta or whatever I'm not a lawyer I'm not telling you what you should do I'm just saying what I do keep that in mind copyright expires so now it's public domain that's not how it works do I have a script that appends the license to each store spot no but I should make one I do have a script that detects when a license is missing so if I delete this and run the script says hey you're missing a file copyright but it doesn't add one for you but that would be nice if it added one for you maybe make a boilerplate generator where you give it a file name and it gives a CPP companion header file class and name is basically specified uh that maybe makes sense sounds like more of an IDE feature but I see what you're saying egb usually the way I write code I haven't gotten much into my workflow if we have time but uh my workflow involves writing tests and often I will start a class in the test file and then I will kick it out when I'm done testing and I want to integrate it I am you know I make a little sandbox where I can play around with it and I don't have to worry about include files and all that stuff what you're suggesting the boilerplate thing wouldn't work with that style okay so the second thing you see here is includes so I put the includes at the top of the file standard C plus plus practice this is a CPP file so I'm including a bunch of H files and all of my code is inside of a quick link.js folder or when you look at includes I know that's my stuff versus that's somebody else's stuff in this case all these includes are my stuff so so you see the the file name for this file is Source quickly JS utility df8 the corresponding header you know is in this folder but it's dot h instead so if I go to the folder see there's UTF CPP and utf-8h I used to not keep the H and the CPP in this the same file uh same folder there used to be separate but I found a much easier if they're in the same folder some people uh tried contributing and they were confused where the files were and uh so I just put them in the same place and I like the style better so um for a public library it might be a good idea to keep them separate because there's like the includes that are public for users of the library and CPP files which aren't supposed to be used by the library at least not directly so but this is an application so I I think that's a different has different needs so uh we have namespace quickly JS I put everything in quick Link in the namespace some projects uh build multiple namespaces so this is in the util folder some people might make a quick link JS namespace and inside of that till namespace I think that's annoying when you do this now you need a bunch of you know in the code that uses it either you need to write until everywhere uh like until encode utf-8 uh or you would need to say using namespace util and I just want to be able to use my code it's all my code I don't have symbol collisions I don't have the same class name I don't have two different classes named the same thing in two different files so uh and even if I did that'd be confusing so I don't use multiple level name spaces um well I do in a few spots but I'll get to that later maybe so yeah all my codes into quickly name space so pretty much all files will open after all the includes they'll open the namespace and at the very end of the file is the closing bracket and when you open an A Space in C plus plus you can use any function in that namespace so I have this Char 8 which is my type and it is part of the quickland.js namespace but I don't have to write I don't have to write equivalent J as colon colon there because I'm in the namespace already so I can use all my code without any namespace prefix nice then we have this line this is a comments that describes the standard that I'm referring to whenever I'm referencing a standard generally unless okay so this project is a JavaScript parser I'm not going to cite the JavaScript standard in every line of code but sometimes there's different standards such as the LSP standard or Unicode standard and those aren't the whole project they're like little pieces here and there so I will cite the standard um like this utf-8 module cite the Unicode standard and then we have the function uh function names are lower snake case I did that because for this project I decided to follow CPP core guidelines for naming convention in the CPP core guidelines said use what the standard does as far as naming convention so the standard or functions for free functions and for member functions these lower snake for types classes and enums and stuff they also use lower snake case so I just picked that style just to try it out turns out I don't like it I would much prefer um a style like this where you have capital letters to denote uh it's a type because sometimes it can get hardened to see or like uh padded string view here I think that'd be nicer some people omit the underscores I think if you admit the underscores then it starts to get weird when you have like utf-8 um so like do you capitalize it or not if you capitalize it sometimes like if it wasn't the eight there it's like sort of like a run-on sentence just a bunch of capital letters all next to each other and like you're supposed to know that these three are different than the r like it's a different word but if you have the underscores like that then it even if you don't have the eight there it separates the word UTF from result so I I think I want to try that style maybe in this project I could do a mass rename pull my classes but for now I'm sticking to this ugly lower snake case thing where methods and classes look the same and sometimes it's confusing um often it's not uh I think one thing that really helps is having highlighting so you can notice that my editor has an orange types or things that are kind of like types um it's not perfect like STD code is not highlighted here but it is here and here that's my fault but um and it's not perfect so like if I put a new line here then it doesn't highlight this as a type whereas if I did a capital letter then I could tweak my Editor to say oh if it starts with a capital letter then highlight it has a type instead of using heuristics like I think the main heuristic is is it followed by another identifier if it is highlighted as Orange but that doesn't always work because like for example here there's no identifier following it it's an asterisk so okay that's why so it's relying on the fact that it's a UTF uint 8T is just a known thing in Vim it's like a keyword obviously it's not like const but so that's why it's orange uh I don't know how this works I think oh this is because I made a rule regarding white space so if you have an asterisk and no white space on one side of the asterisk that thinks oh that's a pointer which means the thing right before the asterisk is a type I have a bunch of heuristics that I made for my Vim syntax highlighter thing but it ain't perfect as you can tell but if I made this Capital uh that would help a lot with the editor heuristics it would also help with my eyes uh and it would help with other people too because not everybody's using my Vim settings or has semantic highlighting and I'm using what I don't know what standard this was what C plus plus version was introduced in but I'm using some more recent C plus plus features such as start char32t which you can store um you know all Unicode code points so because it's a Unicode stuff char32t now there is a Char 8T in uh C plus plus 17 or 20. the problem is there's not great compiler support for Char HT going back like four years and I support compilers that are like four ish years old because this is an open source project I want to make it as easy to build as possible which means even if you have an old tool chain or you're on an old OS it should still build and run pretty well there's a squirrel on my roof so what Char 8 is uh Char 8 is a type Alias so either it is uh if charity is supported by the compiler then I use it otherwise it's just char this has caused a lot of issues mainly around casting uh because Char might be signed but charity I think is always unsigned [Music] now I could have just used Char everywhere probably I don't know I I need what I need to do is benchmark with and without charity so allegedly charity cannot like a pointer an array of charts he cannot Alias an array of int for example but array of char can Alias and array of ins well not an array of pointer there's like aliasing rules in C plus plus and compilers can do more optimizations if you're more relaxed so this is more lacks as far as Charity T is more lacks as far as the Alias removes but Char Char anything can be converted to char and chart yeah anything can be any pointer can convert to a chart pointer you can access it uh or like things like mem copy and that means the compiler once you get a chart pointer the compiler can't optimize as well so that's why I have chart 18 but I haven't measured that I probably should because it's a lot of programmer overhead having this charity thing back to the utf-8 module so here's the function signature first off inside the function is another function so sometimes I'll make helper functions in this case it's called byte and I implement it using a Lambda and there's no capture so it's just the helper thing I could have written it out of line like this uh get rid of them you know could have written it out of the line I don't know I just think it's nice if if a function is very specific to one other function I think it's nice to have a helper not a helper have the helper function inside um that way it can't accidentally be used outside or you don't think this is a general purpose function you know it's very specific to char 32t and chart I never use it outside this module so char 32t it's just in this file so I I don't know anyway that's a helper function to do a cast and you can see I use it a lot and that shortens up the code compared like I have a checked it's a checked cast makes the code a lot shorter here uh I use a binary literal and a ditch a separator digit separator is very nice you can see I often make the mistake of like you know I I look at this and I'm like oh okay the top three bits are set and everything else is here but no the top three bits are not set there's a fourth bit that's not sad so uh having the digit separators helps me notice issues like that uh you'll notice that this I'm just writing to an output pointer with no bounds checking um I don't know if that's a smart idea what is what I do asks should I ever use C pointers in C plus plus uh you mean they're C plus plus pointers too they're not like only C but yeah you should definitely use them I mean don't abuse them be smart they're a feature that is fundamental to the language so you should use them that's different than say default parameters which are not fundamental to the language or things like that like cost expert not really fundamental to the language super helpful but not fundamental C plus plus pointer is fundamental you should definitely use it I see people say never to use bear pointers in modern sequels Plus they're they have an opinion I think their opinion is wrong what would you use instead of a bear pointer I've seen some people advocate for STD optional of STD reference wrapper of whatever but that doesn't even do what all that a pointer does because you can't do Point arithmetic with this disaster and uh you can't treat it as an array and it looks hideous compared to endpointer now it does have some advantages like here x is initialized to a no pointer here X is not initialized to no pointer X is uninitialized that's one advantage but I mean come on another Advantage is that you can on undo the optionalness and now it's a required pointer but like it still looks hideous um not my thing unique pointer SharePoint or pretty much cover all the use cases the pointers no no Point arithmetic with a sharepointer is annoying pointer arithmetic with a unique pointer is impossible you can scroll down uh I might get some ugliness so here we have an anonymous namespace I use this instead of static because so an anonymous namespace is like static on each function so this is a function here it's hard to tell because of the attribute I don't know where the static would go with the attribute there but like you know there's a function I could have written static here instead of using the namespace the problem with the static is that it doesn't work on classes so if I want a helper class can use static and I want to tell the compiler this is only using this file so you can do more aggressive optimizations even without lto so I use an anonymous namespace for classes and for Cass instances I will use an anonymous namespace for functions as well instead of Sometimes using static Sometimes using anonymous namespace so you have a question which do you prefer for this project DC Sierra clang and why I use both when I'm developing I use clang because it builds significantly faster for my development cycle for runtime performance I found that GCC does a better job of optimizing my code so for release builds I prefer GCC however I did some recent profiling and clang might be better at some benchmarks uh I have not investigated that much though I'm going to stick with GCC for a while until I have a reason to switch so that's a anonymous namespace we have these warning things um so in my CI builds I have a ton of warnings enabled sometimes I want to turn off the warnings so I use these macros now I probably should so the reason I have the warning disabled is there's a warning issued for always in line on some older versions of GCC um actually I think newer versions of GCC would also want on this the always inline thing is kind of weird with GCC even though it's in a GCC attribute sometimes it warns when it when GCC thinks it can't inline something even though it does inline it it will still report a warning I don't know so this turns that off what I really should do is instead of sprinkling this everywhere I should make a macro for this attribute which kind of defeats the point of the attribute but like I would want probably want this to happen on msvc as well so I should make an always inline uh macro but I don't touch this code much it's not super Pharma sensitive um so I'm mostly dealing with ASCII files this code is not run for ASCII code so it's just for the you know weird non-esquey stuff so it's not performance critical so here it's kind of hard to see because it's split up in the multiple line that's a clang format thing I use Clank format on this code base uh not always the best formatting decisions but uh here the function is called decode utf-8 inline and it's return type is decode utfa results so this is a class uh some people would call this a struct so if I go to the header file there's a decode utf8 result it's declared with the struct keyword but according to the standard structure classes classes or classes it's all called class they're called class types so I use the term class uh even though it's declare destruct it doesn't have methods and stuff but yeah it's a little helper struck to return bunch of different things there was a discussion on Reddit somebody looked at my code and gave me pointers on how to improve the design of this struct they identified some issues like if ice if okay is false then I shouldn't use code point for example uh or there's like something about size is always one I think oh no size either one or zero if okay is false and that can be confusing because if size is zero you might you might think if if you fail to decode size tells you how much to skip but that doesn't always tell you that because sometimes it's zero there's there's some design issues with this code uh and that's fine as long as the code works you know it it can trip up new users of this code or when you refactor users of the code might miss some Miss some gotcha so a better design would help but it works for now so yeah we're turning that struct here's another helper function this is also very specific to this decoding code asking if it's a continuation byte here we're treating the data as U and eight T's now this is what I was talking about earlier with charity how so padded string view this class is basically an array of Char Eights which means for compilers that support charity it's unsigned but compilers that don't support charity it could be signed or unsigned um but we're doing a bunch of bit math and stuff so like this comparison if if I use Char 8 or Char here this comparison might always be true because uh Char's range would be negative eight zero hacks two positive seven F hex to avoid all that stuff I force it to be unsigned with this cast now if I can guarantee a newer compiler and Char 8 or charity support then I can clean that up a bit but you know people plus ain't perfect so here just doing a bunch of checks for different code sequences we're using a designated initializer here to return destructs all in one go so another way you would do this so This Is A C plus plus 20 feature but it is supported by compilers for a long time at least the gnu compilers have supported this Syntax for a long time because uh cease has a syntax so uh you could write the code like this and then it would be standard C plus plus 17. but I like doing this style because if I forget one of these initializers then a compiler will tell me hey there's a missing initializer here now some people like that feature of the compiler warning about missing missing initializers and other people say the whole point of this is that you can omit things it'll be zeroed out I can understand both use cases I've dealt with some lower level Linux apis where designated initializers are the way to go and you want to zero init everything that you didn't specify because there's like 50 fields but in my code base if I do a designated initializer I mostly want to have everything specified so I want the compiler to tell me if I'm missing something I don't think it matters too much in this case uh especially because this code is pretty well tested here's another thing I do commonly well there's static assert I use that not too much but it's a super handy feature um did I also have yeah there was also back here back here was a cubo.js assert I glossed over this so this is kind of like the standard assert macro but one it's capitalized so you know it's a macro and two I I can customize the format and one thing that's kind of important is uh let's say let's say we'd make it fail when the assertion fails says internal check failed in the function name and then there's the message so first off I get to customize the assertion message it's not up to the standard it's not up to standard Library implementation what it looks like I can make it consistent a cost across platforms so that's nice also I've produced this message saying quickly JS crashed please report this bug here and then I have a link to where users can report the crash and also you see it says illegal instruction coordinates so if you run this code in a debugger uh see where it stopped oh I don't have debug in film I have to rebuild with debug and film please wait okay three run when in the debugger an assertion fails you see it brings you right to the assertion if I used C assert and do the same thing well first off you see the the formats different um but if I rerun it in the debugger the message tells me where it crashed but the debugger doesn't stop in the right spot I'm in P thread kill implementation you have to go up the stack a bunch and then you get to the code so the way I did that was in my assertion macro this is for Windows this is for Linux Mac and this is we can't detect it correctly so what uh C assert does is it calls STD abort you can see in the stack Trace assert uh fail is calling a board I don't call a board I call built-in trap and what building trap does it ins it adds let me show uh let me show the code again qjs assert thank you so if we disassemble this Assemble you see uh here's so here's the call to print the things so it says report assertion failure and then there's this instruction ud2 so on Intel ud2 is the crash instruction built-in trap will generate this instruction and that causes the debugger to stop right here not in some function somewhere it stops right here also it's very very lean um it's not like 50 instructions of random stuff it's just one two byte instruction it's very compact aside from the cost of the if and calling this thing assert is pretty small in terms of code size well my assert is compared to the standard assert which is a bit more well actually I think standard cert is actually it just is without this instruction this would be a jump but I think the debug ability is worth it having it jump right to the assertion message and then you can immediately print variables and stuff and on so that's on Linux and on Mac with msvc uses debug break which gives you the same same thing in the Microsoft debugger uh so I wanted to point out that I'm making some Boolean variables uh now I could have crammed these conditions inside the if like this and I think this reads okay but I think this is better whoops where uh the if just says are the bytes okay and then the details of what the bytes okay mean as right here uh I I use this style a lot where I name my conditions even if it's just even if it was just one thing I will sometimes well often write in this style where I will name the condition and then do if the condition so this is just an example of that this is uh quite unfortunate some of this is uh so I mentioned I use Clank format and sometimes it does a bad job formatting things or probably could have been written better like this uh you could line up the conditions see now it's more clear there's a condition and then the result condition and then result and then the fallback maybe I should uh tell claim format to stop playing format off applying full metal one problem with telling clang format to stop is you have these ugly comments which distracts from the code Maybe that ugliness is fine I don't know that's a problem with automated code for matter sometimes they're stupid but here's another example where I do the bite okay thing I think one of these cases oh yeah so here I have the bite to okay I can reuse the bite oh sorry bite one okay I can reuse that in the failure case to indicate how much did we successfully decode if if you were able to decode byte 1 and byte zero this code assumes byte zero is decoded then you can skip two bytes otherwise if byte went okay is false you can only Skip One I may have written that a different way before but I settled on this so there's multiple purposes for having these Boolean variables here we have a narrow cast so narrow cast is like a checked static cast so character count is an INT and uh we want to get it to be size t I don't know why but I guess for the comparison so the compiler warns that this comparison is between a size t and an INT if you omit the cast so we got a cast to shut the compiler up um so I have this narrow casting which is a checked cast so it just does a runtime comparison it doesn't always do runtime comparison so if the size if it happens at size T is I mean it's not going to be short let's say size T is 16 bytes and inches 32 bytes or something then this oh no no no no wait no that okay if if you're converting from assigned to unsigned that would always do a check because it might be negative but in other cases uh narrow casts might optimize to nothing no check just just a conversion uh where often it will be a runtime check so basically anytime I get a conversion error conversion compiler warning he'll throw on an Arrow cast because I'd rather be told when something is broken rather than it being silently silently convert it to garbage numbers like 4 billion whatever how is the narrow cast implemented that's a good question let's see uh so if you ignore this debug stuff for a minute we check if we're in range and if we're not in range then we report an error so then the question is what does in range do in range there is a standard in range function that I could have used unfortunately uh I'm an older compilers and older compilers don't have standard in range so I you made my own so it's just this algorithm I use a bunch of if const experts and that's the thing which um like not checked at runtime if it doesn't need to be so for example if you're casting from an inch to an inch those types are the same the in and out types are the same so we could just say it's always in range let's say in is uh u8 and out is a u64. I don't have code explicitly to say that's always in range uh instead I just rely on compiler optimizations signed equals signed yeah that would be this case I just rely on the compiler to optimizer uh which I didn't check if it always does it but it should check that it always doesn't always optimizes anyway it's just a few cases this took a while to implement correctly because rules of C plus plus conversions and stuff and it would be nice to have a safe hint library to do that for me however those things are bloated template messes so I mean this is kind of a bloated template mess but I have not seen any compile time or runtime issues with in range now there is this debug stuff what is this so this is kind of like my assert macro thing but a bit smarter so um let's bring that back and let's say narrow cast let's say this was uh negative uh let's say I put negative so it's a negative character character count that won't fit in a size t so if you build and we get a number not in range message and this line points to right here so the message error message that gets printed shows the call to narrow cast it doesn't show the internals of narrow cast so if you look at the internals of narrow cast um this is for if the compilers don't support the feature I'm about to explain so if you don't have a supported compiler then it'll fall back to kind of like just an assert inside of a function so it'll the error message would Point here inside of narrow cast h but if you do have a compiler that supports standard Source location then instead we'll use this thing now this is like a new C plus 20 feature where if you use standard Source location and you use it as a in the default initializer of a parameter then the source location is for the caller not this function so uh the way Source location works is internally when you say currents you get it does this built it well on GCC and clang it's a built-in file built-in function built in line so if we just look at line uh and if we wrote so let me kill this debug stuff just so it's clear so instead of saying caller if we said line is built in line and this line variable isn't initialized to line 14. of narrow cast H this line variable would be for this case it'd be line 147. and same with the file it's it's of the caller not the Kali for this specific case of a default function parameter thingy uh so I take advantage of that and I don't want in release builds I don't want the overhead of passing uh two pointers and an INT every time I call an Arrow cast so I uh turn off this feature in release builds so it's only debugging that I get good line stuff what is the comma this comma is supposed to be there but if I write the comma there then if this code gets deleted by the preprocessor then I have a trailing comma C plus plus doesn't like the trailing comma So to avoid that issue I have to write the comma first that's goofy but that's equals plus for you moving on um I have this comment says to do you can see it's highlighted because Vim by default highlights the word to do and once I discovered that I started using to do to indicate things now there's this parenthesized thing I didn't used to do that 15 years ago uh I used to do comments like this but then I joined a company and at the company they had the convention of putting the person's name in parentheses I found that helpful to figure out who to ask for questions now this information is stored in Source control so it's not actually they're all Alternatives even if you don't have the name they're Alternatives but another use case I've found for this kind of thing is you can put like a GitHub issue number there or like you know jira ticket number whatever task system you use and that's super handy I may have a feature in mind I'm writing some code I'm not ready to implement that feature but I know I'm pretty sure that I will need to change this piece of code in order to add that feature so I'll put a comment saying to do and then the task number for that feature and then maybe I'll put a message uh explaining stuff let me see if I have examples of that well here's here's an example with a task number because that parser options from a config or CLI options so this is like I If if somebody wants to work on this feature they could just look for the task member and then they'll find this code and they're like no oh this is the code they were talking about now I would like to also include a link in the task I would want to include a link to the code but the problem is the code often moves around and maybe it doesn't remain up to date whereas the comment is more likely to be up to date this is kind of like a bookmark in the code you can reference later so this is a performance thing I should do make a copy of the string which is quite dumb but interestingly this has never been a performance issue that I've seen maybe my benchmarks aren't comprehensive enough but this is I mean it's a rare case this is only called when you have a line in your code that has non-ascii and we will only run this code on that when you modify that one line that doesn't happen often so what's my preferred MAX Line width I don't have a preference I'm used to I used to use 60 and the reason I used 60 was on the particular machine I was using it was a laptop and I did a vertical split like this I had 60 columns on the left and 60 columns on the right it's not the case right now um so that was nice because I could have all my code like there was no weird wrapping thing now I use 80 because that's just the clang format default for uh Google style I just picked the Google style whatever so the cling format uh default for Google is 80 columns so I just use 80. I'm not too picky about it like in the the rust Port of this code base it was I think 100 is rust formats column width so I just did 100 no problem at the current font size at the current resolution um this this line here is 80 columns so if I do so that's 80 X's it's actually that column is 81. so I have uh room for 80 83 X's on this terminal yeah that's that's probably small for most people I don't see too much of an issue with it physically the font is huge like to my eyes for you it's small because you may have noticed this this viewport is not like a widescreen this is a 9x8 monitor that nine eight aspect ratio monitor it's you can imagine it's two 1440p displays you know like a normal 1440 plea display 69. put two of those together and then turn it that that's this screen so uh much more Square I'm happy to use this font size uh I did that last night but it's harder for y'all to see so I'm fine with this hard limit 80 no I don't do hard limits I just let the code formatter do it and if the code for matter does something very stupid I'll just turn it off I could uh we could say rip grab let's look for 81 things in search for test so here's an example this is a data table so I turn off formatting to get the alignment um and I exceed there's 81 columns I'll exceed it occasionally here's another data table where yeah I'm going Beyond 80. so I mean for this kind of case that's fine to scroll I guess especially because it still fits on an 80 wide screen it's just there's a bunch of blank space at the beginning but if it's normal code I would I would word rapid but like it's not a hard rule or anything it's calm little bit important nowadays I use 120. I don't think it's super important aside from preventing the code from just getting hell along with the automatic code formatter you know like this line foreign columns that's ridiculous to have it all one line uh and I think this looks better when it's wrapped I think that's often the case things just look a little better when you align it 120 is good pretty much every div has Ultra wide monitor nowaday not me not a lot of people I know most people I think are just on normal wide just 16 9 16 10 monitors I certainly I'm not on 16 9 or wider any other thing else in this file I should comment on so one thing you'll notice is that I always put the types there was one exception where I use Auto well actually so lambda's helper functions I'll use Auto and apparently here I wrote Auto I don't like that decode utfa uh I've been trying to not use Auto mostly when I'm lazy I'll use Auto but I want to refactor away from that stole the auto in this file um see I'll put the types I find that helps a lot I'm also using the classic style of initialization there's so many ways to initialize things now like you can do auto the problem with this is you don't know what size T I want to say that count to size t so what you could do is put size t 0 there but then like here if you if you did that style here you would just say Auto Stop so there's no type so now sometimes you put the site sometimes you don't um some people do curly style I tried this for a while I just hated it um now for this is for primitive types I'm not completely consistent with my initialization so this is for primitive types um and like if I'm calling a function I'll use equals two uh but what's a what's a module that has more complex types um oh like uh this guy so we're constructing a new padded string we're not taking an existing pad of string and copying it um with a string view thing and we're copying the data into a new padded string so we'll keep allocating here so here I have I'm using the parentheses Style um now if I tried using equals this won't compile this would work if it was STD string but it won't compile for me whoops I broke something over here oh so this doesn't compile it says no conversion from string view to padded string and the reason is The Constructor that takes a string view is marked explicit so I will Mark all my classes Constructors with explicit except for well here they're deleted uh except for things like move Constructors or um yeah I think that's pretty much it now sometimes I will I will have a Constructor that I want to Mark as implicit so this is a default Constructor and normally I would write explicit put this back put rights explicit here on this Constructor but I'm using initializers and here's a case where I do want the zero initialization theft that I talked about earlier for this class in particular I want the default initialization and if you do that and your constructor's mark explicit the compiler will say no you said explicit you're supposed to initialize that explicitly like this and it's complained about a bunch of other cases but like you said explicit so you better put the thing there because this is an array that contains two things so we put one there explicit so that's uh this Constructor call this is also marked implicit I don't think I need that maybe I do am I getting this wrong if I write explicit there oh yeah so you I think you need to put the type name for it to not complain yeah you need to put the type name otherwise it complains so to in the interest of shorter syntax and not having all these empty things I made this implicit and this implicit but most of my classes all the constructors are explicit uh and this is an issue I had with the rust code actually so when I ported this code to rust as an experiment code like this I had trouble with uh in the rust code I had to explicitly write out all the things let me dig that up so this is the same code but Port it to rust and I had to write the empties explicitly and I had to write all this stuff if we prepare them side by side so we have this code versus this code put the whole call there you can see the the C plus plus code like I saw it from formatting differences this code is a bit more compact I mean maybe you could argue legibility suffers because I don't know that this is a diagnostic message org info but the white claim format formats things it would format it poorly but anyway we have to put these empties here uh in the rust code because the array if you say you want an array of three things you'd better provide three things it won't zero fill for you maybe there's a way to do that rust I don't know couldn't I dot dot default defaults I could but that still would be syntax noise but that would Eddie Bear I guess you're saying I could do something like this uh I've not seen that syntax but it's still more verbose than I would want I try to avoid parentheses Syntax for initialize because the vexing thing I uh where was that utf-8 uh for this so you're referring to the most vexing parse thing I've never run into that or if I have I've like easily worked around it composer decent nowadays at telling you about the vaccine parse issue so I just haven't run into it I don't even remember how you're supposed to hit the issue is it if this thing looks like a type or something I don't know I don't I don't hit that issue you ran into most vexing person in this VC error message is the worst you avoid it all together um uh clang seems to do a good job so I I mostly develop with playing maybe you could try Clank CL if you're on Windows isn't the most affecting Parts when you use empty parens well that's not vexing I mean that is an issue where oh it even warns oh it says vaccine parts that that's an issue but there's a case where if you put stuff there it's still thinks it's a function declaration not a variable declaration I don't remember the case though but you can see clang warned me on it so I guess let's look at the header file now for utf-8 utfa.h so just like in the CPL flip files we have the copper header at the top and then the full stuff on the bottom and we have an include guard I use include guards because if there's a standard way to do things and there's no downside really then I'll use a standard way so I have uh standard include guards now my include guard style has evolved over the years what I used to do was Project name and then like some like hash thing like a good or something I used to do a good I mean it didn't look this stupid but goods are weird and dumb and like what the heck does it mean so I just did basically this is this the include guards this matches the header file the include path so you would include it by saying pound include this thing foreign [Music] that's what I write here just all caps instead of hyphens or slashes I use underscores but you know this here has run and I have run into issues with this no never mind I'm not one thing I have is uh if I say Foo here it'll work because I don't have a macro called Foo but I can run my formatter tool thingy that says oh the file change if I load it you see it uh my formatter added fix my include cards for me so that's not Clank format that's a custom script I wrote but I have screwed this up enough times that I just wrote a script to fix it up for me script isn't perfect um it'd be nice if there's an open source tool for it maybe there is I thought I could just do well how about open source tools is you have to install them be nice if it was part of Clank format which is a tool I already use playing format 9 which I use now doesn't have this feature to add include cards or fix them up for you but maybe a newer version does I don't know that'd be nice um you tried to use include guards your folder structure is too deep and it got way too long well the include guards who cares how long they are I mean you don't see them all right oh my God I mean you see them at the very top of the five but like you just quickly skip past it so who cares I would more be worried about big paths for your pound includes not your include guards yeah if you have long paths maybe you should stop organizing things so much I used to uh uh not have these folders so it used to be like this where I had quicklink.js slash and then my stuff no folder organization but then I got on wieldy um uh so I have 250 files source files CPP and H so uh that that got out of hand eventually I mean I think I could deal with it but contributors get scared when they see a folder with 200 files in it so I split it up into separate folders um you can see here's the folders now not everything's in folders I probably should put all this stuff in folders I just haven't done it but you know I got a bunch of folders and each folder has a bunch of things but the folders are just the folder structure it's not a namespace structure uh I have cyclic dependencies between these things they're not separate libraries just for helping organize it's not doesn't affect runtime at all um we have includes app inside the include guard so the include guard it covers all the lines of code um so inside of that is includes for the header stuff I see a standard include so I for stand so there's two ways to include this stood to f h and cease to Def I prefer the C to Def One because that's just the C plus plus e way to do it and I like to prefix the things with stood cone colon because I hate myself or something anything not built in I will put an A's preview so either okay so if you don't see a prefix in front of something then either it's my code or it is built into C plus plus and if it's built into C plus plus you'd better know it there are some exceptions like uh where um like this guy the shirt q and u8 and stuff these are from the neon arm Neon intrinsics these are actually macros so you can't name space them but for for things in the global name space that are functions or variables whatever I will do the colon colon to say this is not my code and this is not built into C plus plus this is from the standard not standard this is from the neon Library see here too for types but I can't unfortunately do that for macros so and they didn't indicate that it's a macro with all caps uh which is dumb so I'm stuck with that here's a little formatting thing these should line up that's nice see here I I wanted everything to line up and I also violated the 80 column limit thing but I wanted to make them line up to make it easier to follow so yeah I line this this this used to be like this one anyway every time you look at code you might discover some little tweak you can make to make it better so I like to put the namespace and if it's a C library I'll put just colon colon like in this case that's just how we do it and the way you you need to include it with the c to Def otherwise it might not have the standard namespace stuff I know whether there was the whole iostream that H and iostream is there no sta deaf um so you're asking so this is the C version so the C1 came first then there's the C plus plus standard one and then why not this one uh I don't know well that's not for me to decide you know I can't do anything about that I don't know the history of the header names I'm guessing they wanted to indicate that it was a c include somehow and they didn't want to touch the original so doing this is off limits for adding stuff so I guess they just added a prefix maybe a same thing would have been like to standardize HPP then you don't have this weird C prefix thing but that ship has sailed now some people like to group their includes they might put all the standard ones first and then your your projects after or maybe you put your projects first and the standard ones after maybe you have like the standard ones and the third party libraries and then your things or whatever I don't group them I just sort the whole block and I have Clank format do it now if you have an if around some code like here then I'll put that in a separate paragraph like I'll put a blank line but normally we don't have conditional Clues so let's find something like like this guy uh well it's actually not including much other stuff but we have some standard includes up here and some standard occlude down here it's just sorted alphabetically so c o q for all my stuff and then you comes after Q so it's sort of this way uh yeah no no grouping I don't see the pointing grouping I I want the includes to be as out of the way as possible which is hard as C plus plus so I I don't want like to have to do extra maintenance or extra work when I'm dealing with includes I don't have to make sure I put blank lines in the right spots I always want to sort it and be done with it and the Sorting is just so um if I add an include I know where to put it I don't have to think oh should I group it with all the quicklit things or I don't have to think about it I just sort it I even have an editor thing so if I shuffle all these I just hit a thing on my editor and it sorts it for me now if a contributor comes along and doesn't have that keyword uh keyboard shortcut whatever than if they run the code formatter clang format will reorder them so sometimes I sort of manually manually like sometimes I use my keyboard shortcut to sort them sometimes these Clank from I don't know I'm not very consistent about them oh so one thing I didn't mention is why do I use this style so there's a bunch of different styles so I'm in the util folder so actually let me look at the CPP file so we're in the util folder so what I could have done is just include utf-8 instead of this or include narrow cast instead of all that that makes it hard to move code around because that whenever I move code around I can't just copy paste the includes that go with it I have to fix up the includes I don't like that style I do think putting the full quicklink.js in front is verbose I have thought of abbreviating this kilojs and also doing that for this namespace I don't know that looks weird to me now that I put it like that maybe I'm just not used to it and then obviously I would change the folder name uh as I said earlier I put everything inside the quick and J's namespace so once I once I include stuff namespace everything is in here now some files don't have this if they just have macros so like the assert well actually this cert does have it so if I have macros s you see there's no using names there's no namespace declaration here I just go right into the find so it defines macros are not inside of a namespace so I don't put them inside of a namespace because if you put if you point namespace whatever doesn't do anything because of the macro So to avoid misleading people into thinking the macros inside namespace I just don't put I try not to put my macros in a namespace so all this macro stuff is before a namespace uh quick and JS and then this one function for an assertion failure is inside of a name space so yeah here's the the utf-8 I had a file pretty small just a few functions I'm gonna type for the decode thing uh as far as blank lines go I don't know why the blank lines are organized this way just kind of arbitrary I don't think it looks good right here um maybe a better way to would be encode and decode whoops go together and then these LSP character things I guess go together and then there's like a count separate maybe the count goes here I don't know some people like um what's his name Stuart Lynch he likes to keep them all separate like this I think this looks my it's just too much negative space for me to handle um I prefer something like this but claim format you know is too too wide it's beyond 80 columns so claim format puts on line break there which I'm willing to accept I guess maybe something easier in my eyes would be this style where there's not as much it doesn't look like here it sort of looks like there's a blank line until you notice over here but here doesn't look like a blank line I don't know that doesn't bother me too much but there's blank lines every line that's just like ah my eyes go sideways how to manage include header to improve compile time went to use four declares should I avoid for declare and only use them for cyclic include or should I use them everything when possible I tend to forward declare when possible I mean here I'm not I think I could forward a clear Pat of string view right I think I could forward a clear this I would forward a clear this but this isn't a type this is a type Alias so that gets annoying I have thought of implementing my own though my own string view just for compiled time purposes because the string view templates are the standard ones are slow like everything in the standard Library if you notice a problem then forwarded declaring can really help like there was a library there is a library I'm using called Cindy Json like for example this file I have I'm using this simsy Json thing um using some of their templates and stuff for some helper functions I'm not using any of the types I'm just referencing the types so I could forward declared so that's what I do I made a forward declaration header which is kind of like iOS FWD um and I kind of did a crappy for declaration their thing is complicated because they use like inline name spaces and stuff well namespace aliases and stuff and the logic is kind of complicated so this actually isn't being used on x86 for x86 I just pound include the header but for arm um there this implementation of 70 Json simple enough that I could do the four declarations right so I did them arm and it did improve compile times quite a bit uh especially without Capricorn headers with pre-combat headers I think I include this in my precon headers set just because this header sucks but I would like to kick that out because it's only a few files using it using same djson but yeah it's when you notice a compile time issue you should forward declare um just know if you're giving a gun forward to Claire third party things that can be fragile I have considered having this MD Json team like contributing a patch system to Json to add send me Jason forward to their headers so anybody can use their four declarations that would be nice kind of like iOS FWD but I haven't done that yet I should do that help everybody with compile times um there's one thing I wanted to point out so we looked at some code let me undo all that undo uh we looked at some code for in range and I mentioned that there's a standard in range uh C plus plus 20 I think so often I will encounter something where I would like to use a standard Library version of something but it's not available so I gotta make my own or maybe it is available but its performance sucks or something there's a bug I need to work around I've needed to work around bugs in standard Library things before either performance bugs or like actual functionality books so I will try to keep the same interface as the standard version of that function I'll even keep the same name which is easy because I have the same naming convention um I'll just make my own version and I'll put most of those things in the port file here's an example so numeric limits there's a standard numeric limits but in some compilers there's a bug in numeric limits of all things so I have my own limits.h I also have my own type traits at age oh yeah make signed and un Make unsigned also has a bug in some compilers so I wrote my own limits H and type trades h uh work around older versions of lib C plus plus not supporting stoodmeric hret despite corresponding everyday amp so yeah there's a bug where it doesn't work with Charity so I wrote my own numeric limits uh I didn't write my full implementation I leveraged the standard implementation and just worked around the bug with a specialization in this case I think it's the same with type trades I just use the standard make unsigned and then added a workaround so I uh yeah I do this in a bunch of spots like you see this port folder has a lot of stuff in it now that's not the portfolter is an only standard thing I have like oh there's that Force inline attribute I should use that I was mentioning at the beginning of the Stream uh so I have like portability across different compilers kind of things not just standard Library stuff or like Constable is like a language feature C plus plus 20 but if you don't have it const expert works just fine for my use case so market cost expert um or like some Cindy stuff I have some Cindy wrappers or process stuff if you need to process apis that is uh I put that in Port as well so Port isn't just standard Library stuff but I do have some standard Library stuff like SD thread for example wrote my own SD thread that works the same as the standard one except it's mine now the reason I wrote my own standard thread is binary size so I care about binary size for my program uh maybe I can talk about some of the tricks I do later another day but uh when porting to Windows uh I used um GCC on Windows as I mentioned earlier performance is the reason I like GCC and one issue with GCC and mingw is that their standard thread uses P thread and their P thread library is big Once you pull one thing out of it like the standard thread or the standard mutex you know it pulls in the P thread thread and P threaded mutex and that polls and everything else you get all their thread local storage wrappers and P thread at fork or whatever I don't know what stuff they have so I didn't want all that because that really bloated by binary size it wasn't just that I think this the P thread used some other things and that contribute to Binary blow so uh profiling of the binary told me that if I got rid of P thread my binary size would be cut down by I don't know how much actually whoops all right uh let's see if I wrote down how much it improved see one problem with renaming of files is it kind of breaks git log I think there's a git log follow to fix them oh look there's a type I want to commit message Bill sizing so Implement our own standards right on Windows so we got a uh build size changes Implement our own standard thread meet text and conditioner classes depending on reducing reply would have five bits but this data doesn't show that it reduced the size at all see it reduced by like a kilobyte two kilobytes am I stupid foreign maybe I copy paste the wrong data or maybe I was wrong oh okay this is actually there were two bugs that were causing binary bloat one of them is stood file system path and another was P thread if I got rid of both then I got a huge size reduction 38 percent but I think one or the other didn't reduce it I had to get both so that's kind of misleading um so yeah I wrote my own and this isn't anything fancy this is just the threads of interface over uh well I do mutex condition variable and stuff too but like sit thread is on Windows it's wait for a single object For Thread join starting a thread is begin 3DX you know the normal apis it's nothing there's no logic really there's just a few assertions and you know from handle management and just a convenient wrapper I mean looking here you see I do use if defs or different OS stuff um most of the time if I platform specific code I'll just leave it all in one file uh but if it gets unwieldy if it's a lot of code or if there's a lot of conditional includes I mean here's there's quite a bit of a conditional Clues but you know if there's a lot uh then like this file is only 250 lines long so if I split it up into the posix and the windows I mean they're like 120 lines each that's not really a a win that's not drastically different sizes so I'll keep it all in the same file sometimes I'll so sometimes I'll wrap the if around the entire function and I'll do that usually if I have a bunch of functions that need to be wrapped uh the conditional and sometimes I'll put the ifs inside the function I'm not really consistent about which way I pick I don't have a standard there that's just what I feel at the time but yeah they're just ugly portability stuff so some other files like uh configloader no change your technical thoughts okay not config so I have three files here each of these is platform specific because these were you know 300 lines 300 lines 250 lines so these were like big each on their own so if I put them all in one file it'd be annoying also the the data structures used are very different like the you know class members are very different so I just thought the code isn't it's not like the the functions are going to be very similar between the three platforms so you can like look between them no they're just drastically different implementations here so they're separate files I think this is the only case where I make separate files though but one thing I do is I will allow you to compile all the files on all platforms so this is I notify CBP this would only build on Linux um but we compile it anyway or you can compile it on Windows for example the reason I do that is to keep the build system simple so I could just add this if in the file one that indicates to you as a reader hey this is this is platform specific but um it also means the build system could just list all the files it I don't have to write some complicated oh if your windows do this if you're whatever do that kind of logic in the build system I just list the files keeps it simpler I'd rather run C plus plus codes and see me code did I write my own STD file system path a good question so regarding this commit what did I do okay so here I was using file system path as a hacky way to convert from utf-16 to utf-8 so I just wrote a proper function for that job um here same thing uh yeah that's uh that's what I was using it for maybe in the past I was using it for other things that I cleaned up I would like to use this file system okay the only reason okay the reason I used file system was because on Windows it's available with compilers that are several years old on Linux and Mac that's not the case so I I only used Sid file system in Windows specific code pads so that was stuff like here this is um the windows path canonicalizer so it's specific to Windows paths so I could use stood file system because it was available and um I think originally this code did use did file system more but then I was like I got rid of that so it was just like this piece I also used it for like uh like a recursive delete of a folder or whatever oh here's another reason I don't use the file system it was buggy on GCC Aid that was annoying to fix so like I will use the file system if we don't have a better option so well in this case I don't I don't think we need this code path so I used to not have this Windows code path for making a tempter so it used to be just here's the unixie way to make a temporary directory and then I have the standard file system link but uh then I don't know why but I added a Windows version to the specific code and then I don't need this the final system version anymore you know any old code base we're gonna have dead code uh here's another example creating a directory um I had a reason for let's see um where are we windows show allow Creator to color to detect already exists oh I needed better air handling and the standard file system did give me good error codes I need to detect if you try to create a directory but it already exists I need to know if it's a directory I don't exist error that's why I wrote my custom code path uh I could just use Sid file system it's a file system I don't like too many problems with it there's binary bloke problems it was crashy on older gccs there's bad error reporting so I use it sparingly I think I used to use it a lot more but I've been replacing with Windows apis on Windows and Linux apis on Linux and stuff but I don't have my own Sid file system path abstraction my code isn't dealing much with paths so I could just use string I probably should make a path abstraction though but you know there's a cost to abstraction like that one is probably a lot of code I mean not a lot but yeah do I use exceptions in quickland.js no I do have a custom exceptions thing called try catch stack which uses such up long jump and I you know I have like a it's a wrapper around seja because it's a way to it's not just a way to do such a long jump you also can transfer data like an exception object kind of thing you could also Nest it and stuff so I have that uh but it's not General exceptions like C plus plus exceptions the reason I do it this way is so that I can so one issue with C plus plus exceptions is that it will basically inject code for every function I mean there's a bunch of different things that happen when you enable exceptions basically this is better for my code base usually the people who want exceptions want to avoid things like memory leaks or lock leaks I don't use this code when there's locks involved so I won't forget to unlock something if there's an exception whereas like with standard exceptions if you have a lock guard and you throw an exception while the lock is held when the runtime raises the exception and Bubbles up the stack it'll say oh there's a lockout here let me unlock it for you and then keep throwing that's all automatic I don't need that behavior so I don't need the overhead involved with a runtime keeping track of all this stuff so I I wrote my own thing that skips around all that exception overhead it also reduces binary size because the unwind tables do take up a lot of space does the try catch stack thing online stack or is it just jump and doesn't call destructors it does not call destructors it's actually undefined Behavior the try catch stack actually in folks undefined behavior in practice that means it may or may not call destructors so on Linux it does not call disruptors on Windows msvc it does and a Windows GCC it doesn't so it really depends on the compiler and stuff that's another reason I use GCC because it doesn't call destructors it's it's undefined Behavior set jumping or long jumping across frames that have destructors that's undefined Behavior but in practice does what I want have you tried other error handling thingies like system errors that expected I've tried System error it's garbage I've tried a similar thing to stood expected and I use it in a few spots um mostly the configuration stuff because we need to like load config files and there's various errors that can happen if you try to load we can have I made a result class which is kind of like stood expected except there's multiple error types so here's the what comes out so you asked to watch and load you get this out or you get an error which is one of these two so this is like a variance I guess stood variant so it can give you a canonicalized path i o error because it couldn't canonicalize or it failed to read the file or you could just do one error type if it's just one error I don't think I have those here oh there's like um like the canonical ions function can return a chemicalized error read file function returns a read file error but if you call both of these you might get either error so that's what these guys down here do so it's like stood expected I use different error handling strategies for different things of course do you use any Dynamic static analysis tools for your C plus plus code like libacion I do test with asan and ubisan regularly in CI I also test with multiple different compilers and when you test with multiple different compilers you expose different quirks well the exposed quirks of the compilers but you also you know you have multiple angles of looking at your code and that catches issues that's one benefit of having an open source mindset is if you are open to a bunch of different compilers and you can possibly catch more issues uh each compiler will have its own static analysis stuff in it like warnings like the vexing Parts I showed you I use that that's static analysis I don't use clang tidy or any other like more sophisticated stack analysis tools I found they're just too noisy and don't really help I try it occasionally I try Clank tidy once every year or so so I have noticed issues where like uh I mean this that maybe I have code like this where I have a case and then some code and then another case and some code and the two blocks of code are identical clang tidy will point that out and usually it's just like oh the code is working like here the code is working correctly I could just do the fall through to simplify the code but I mean the Clank tidy check is telling you hey there might be a bug because you copy paste the code so uh it would be helpful if it caught bugs but I've not caught any bugs of Clank tiny but I haven't used that much so I don't know but for like shortening code that kind of stuff's helpful CI does check with a bunch of different compilers uh sometimes it's not W error Zone um earlier when I changed compile flags and rebuild you saw there was some warnings that's in a library I don't err on all thank you all warnings just on warnings in my code because third party libraries will have warnings that I can't easily fix or I don't want to fix also on I only do warnings as errors on GCC and clang I don't do a Microsoft's compiler and that's because I tried setting it up and I thought it was working on CI but it wasn't working but then I kept introducing new warnings and by the time I noticed that oh there's still All These Warnings my warnings as everything isn't working I was like hey I don't I don't care but I should go back and uh fix the fix the warnings everything on msvc and fix all the warnings I should do that I just haven't gone around to it so I don't normally develop with msvc if I did I would notice warnings and try to fix them okay so I think we'll wrap up that session I will uh probably do another one of these and discuss different parts of the code base or different things maybe I'll get more in depth I didn't really talk about classes for example I didn't talk about templates uh I didn't talk about my workflow uh debugging cycle kind of things there's plenty of things I wanted to cover that I didn't get to I don't know we'll see depends on what people ask and what I'm interested in talking about
Info
Channel: strager
Views: 233,806
Rating: undefined out of 5
Keywords: cpp, cplusplus, c++, code review, coding style, clean code, code quality, vim, vi, compiler, low level, quick-lint-js, linter, debugging, performance, cxx, code style, editor, comments, todo, language features, language subset, Twitch, portability, cross-platform, open source, ex-Facebook
Id: W8-G_PL6p-0
Channel Id: undefined
Length: 100min 39sec (6039 seconds)
Published: Tue Jan 24 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.