Modern C and What We Can Learn From It - Luca Sas [ ACCU 2021 ]
Вставка
- Опубліковано 7 кві 2021
- #Programming #Cpp #AccuConf
Slides: accu.org/conf-previous/2021/s...
ACCU Website: www.accu.org
ACCU Conference Website: conference.accu.org
ACCU Twitter: @ACCUConf
Streamed & Edited By Digital Medium Ltd: events.digital-medium.co.uk
------
C is often perceived as an antiquated language that is mostly used for legacy purposes, but many people still prefer coding in C or in a subset of C++ that is very close to C. This is sometimes labeled "Modern C" and the ideas behind it are becoming more popular alongside other paradigms such as Data Oriented Design (DOD), Zero Is Initialization (ZII) and Centralized Memory Management (CMM). In fact, many new systems programming languages, such as Rust, Go and Zig, also embody a lot of similar ideas as Modern C showcasing a high degree of interest in these paradigms. In this talk we will explore how programming looks like with this different approach to C and how it addresses matters such as API design, error handling and resource management as well as what are the benefits and costs of these techniques. By the end we will gain a better understanding of how these ideas can help improve the quality of our code, what new languages have adopted similar concepts and to what extent, and what lessons we can learn from this in order to improve our own existing codebases and styles of coding.
------
Luca Sas
Luca Sas is a Core Systems Engineer at Creative Assembly who has been coding for almost a decade and is passionate about system architecture and low level programming. Some of his previous work includes mobile apps with some of the biggest NGOs in Romania and video game development. In Romania he was a national champion at programming contests and olympiads where he is now a judge. He enjoys attending conferences and talking to developers about their experience and learning about ways to improve software design as well as mentoring new programmers and giving talks. Previously he gave talks at programming events in Romania and the University of Leeds, and ACCU 2019.
------
Future Conferences:
ACCU 2022 Spring Conference, Bristol (UK), Marriott City Centre:
2022-04-05 to 2022-04-09.
------------------------- - Наука та технологія
"There is more to C than meets the eye"
Nice.
I write C every day as an embedded developer. It's funny to me that some people think C is some ancient obscure language. C is not going anywhere for a long time.
well, it is ancient
obscure? besides the variable declaration syntax, not really
although having a 7 year standard cycle is a bit too long imo
I love c
people should experiment with writing modular build systems for C. I would bet there are tons of different ways you could implement them.
@@kuhluhOG you do realize that us embedded developer write C99 when we're lucky, right? because most compilers are C90+extensions, some are C99+extensions. And the "slow" update cycle may be a sign that the language is mature and doesn't need new cool features/is stable/[insert another big advantage of C]
gcc for arm is probably C11 now but how many are using C11 features, really?
Exactly. I have been using C for the last 24 years. All my embedded work is in C. At Apple we used C exclusively for all the systems work including the OS, the Apple car and all its systems.
2:07 A refresh on C
4:04 Comments
4:18 Variables and structs
5:02 Primitive types
6:18 Functions
7:28 C++ is not C
8:11 Struct initialization
12:16 Struct initialization: sokol gfx
13:52 Modern C
14:54 C11 Additions
15:18 C11 Additions: static asserts
15:40 C11 Additions: _Generic and overloading
16:30 C11 Additions (cont)
17:06 Awesome macros
17:32 Awesome macros: defer
20:20 API Design: Math
21:18 API Design: Math in Modern C
23:18 API Design: Error handling
24:15 API Design: Error handling in Modern C
30:33 Generic APIs in C
31:40 Generic APIs in C: Dynamic Arrays
33:56 Generic APIs in C: Map
34:24 Libraries in C
37:06 When writing libraries
38:30 Memory Management
40:14 Temporary allocators
41:31 API Design: Modern C
42:15 String handling in C
42:54 Avoid libc
43:30 Replacing libc functionnality: printf
46:30 Case study: std::string
49:10 String handling in Modern C
51:50 String handling in Old C
53:40 String handling in Modern C (cont)
54:35 String handling in Modern C: another awesome macro (iterator)
55:41 First optimization at CA
58:40 Conclusion
Thanks for taking the time and creating timestamps for this talk, very useful.
This is probably the best argument for C: Implement your own libraries.
The result: well defined, portable, benchmarked and optimized, *standard compliant* (ironically), source code.
He has eloquently voiced so many of my thoughts on C. The libc functions are RIDICULOUS. Also, strtok is pure comedy. Because of library design or build nightmares, C devs often have to re-implement very basic operations. If someone would design a sane, modern C std lib and a build system that could compete with cargo/npm/pip/etc... using C would not require any large tradeoffs from using other languages; and would have some very pronounced advantages, offering more control than any popular alternative. I did look into zig and rust; great languages, but those build times are not great. Idk if that's LLVM or just young compilers, but be prepared to get up and make yourself coffee every time you rename a variable.
Exactly, no one doing anything meaningful uses much of the standard C library. You have to be willing to build up your own more useful functions if you want to do anything non-trivial nowadays. But today’s culture is that of open-source dependencies where devs are used to being able to download a dependent open-source 3rd-party library to do anything non-trivial. Of course that doesn’t exist in C. But if you’re a dev worth your salt, then you just write your own shit when you need it, and it doesn’t take that long.
@@mensaswede4028 It only doesn't take that long if it's not much work in the first place. Sure, I would love to learn how to write a multicore async runtime, but I might as well save decades of my life by using one that has been heavily tested
Hi, glad you enjoyed my talk. Zig is actually developing systems meant to make build speeds very fast in debug mode which should solve the build-time issues you have. Another language worth looking into is Jai by game developer Jonathan Blow, tho the language is not yet public.
Please give V (vlang) a try!
Cargo for C would be so amazing.
Good talk.
I too find modern c to be really refreshing, and use it for personal projects. Also great to see sokol-gfx, which is a joy to use.
The biggest problem I see with C is its lack of standard library.
It makes the language inaccessible to people for quick "getting to know the language"-type of projects.
Often the only alternative to using the standard library is either writing your own libraries or searching for other peoples libraries.
But without significant experience you just can't judge the libraries you find properly, and certainly can't construct a good one.
So beginners will use the standard-library, which is in many places crappy and very insufficient.
Especially string handling, which is also probably one of the first thing people want to do with a language.
Imo the top priority for any C "comittee" or people interested in pushing the language should be to band together and create a new defacto standard library based on modern c which everyone new then can simply use to get some stuff done.
THIS ^^^^^^^^^^^^
I agree, my biggest gripe with C is that I always feel like I have to implement the universe from scratch for any project I want to do.
I completely agree. C with a well designed and useful standard library would be amazing. It doesn't need to be huge, but it should have the basic building blocks such as strings, vectors, maps, etc. I don't like OO, but sometimes I use C++ just because I love the STL.
@@gaius_marius maybe glib is one option, not ideal but it's what we have for now
People often argue that writing C code is quite dangerous because of buffer overflows and memory leaks, however there are tools which can help to detect the majority of those problems e.g. valgrind. I'll never go back writing a C program without valgrind.
Completely agree, and that is true for C++ as well.
As if there is any need to use unprotected manually managed buffers - there are so many good libraries out there and even just writing a simple wrapper-struct takes only a few minutes. You only use the lowest level stuff when you either have no clue or when you really need it (tight memory constraints, high performance, low latency - that kinda stuff).
-fsanitize=address with -lasan helps telling where in the code has corrupted stack
and debugging core dumps
This talk is great!, I was searching for a new language close to C, but this just reminded me the power of C. I love these ideas which keep the language updated.
Glad it was helpful!
The only real alternative to C/C++ is Rust.
@@NymezWoW or Zig
@@kuhluhOG or Jai
@@nephew_tom Jai is not available yet though ?
This dude and I are on the same wavelength. Well done!
one of the most useful talks on programming given in recent times
I'm currently writing a basic game engine for a graphical calculator in C and this talk couldn't have come at a better time ❤️
Good luck man!!
Great talk. Lots to unpack from this.
I hadn't seen this defer macro before. I like it.
This is the most informative presentation on C I've come across to date. Well done
Thank you for commenting! Pleased to hear that this was a helpful presentation.
Even using Zig as a modern C compiler is quite nice. Cross-compilation and a unified build system you can write in Zig is a good bonus if you want to avoid the stack of tooling a typical C project needs.
what else do you need except for a compiler and a shell/batch script file?
@@ggss2836 Astyle, valgrind, a profiler, gdb, man pages ...
See where I'm going ?
@@ggss2836 a C compiler, a static analysis tool like valgrind, a debugger like gdb, a build system like make, cmake, meson, etc, a version control system very likely git, a way to track your dependencies like git submodules and/or your system's package manager
This is INSANELY good!!!
I like this. It's being like 20 years that I haven't touched serious c code. This inspires me to get more into the modern stuff
Ditto - I haven't touched C since the 80s and K&R 1. It was all pretty painful back then - even the tooling was a challenge. Modern C with modern tooling, such as the new VS interactive debugger, seems a much more attractive proposition.
Lovely talk. defer and passing structs by value are really nice.
Me. I love C. Nothing better.
Fixing strings in C is actually hard, although representing strings as { data, size } makes a lot of sense, the problem is when it comes to interoperability with legacy APIs and external libraries, where zero-terminated strings are usually expected, in which case such a string view needs to be reallocated and appended with a zero just for the purpose of passing it to some external API. So it's a matter of preference, one can either have cheap string views / slices, or a natural (zero-terminated) string representation, but not both at the same time. So there's always some trade off.
Yes, good point!
Stupid question though: Cant we use a compatible_string that supports both null terminated via an extra pointer and the more handy {data, size} interface?
Yes, it complicates stuff and might not be worth it for all cases, but maybe for most of them?
@@grafgrantula6100 str is just storing that null terminated string in its data field
N calculating its length for the size
So for str u can just return
str->data to get back ur null terminated string
Hi, in regards to zero-terminated string API interoperability, just do what other langs do! You can have strategies where you copy the string to make it null terminated, or sometimes you can write your code such that you guarantee a string is null terminated. Also if you use a string_buffer rather than a string slice you can ensure the string_buffer always puts a null terminator at the end. A collection of strategies like this depending on the kind of program you write can certainly help and languages such as Zig are even better because the type system supports slices natively but also supports slices with sentinel values natively! So you can specify if a slice is meant to have a null terminator in the type system, pretty cool :D
You could have the underlying data just be a null-terminated string, represented by a { data, length } struct where the length is -1 to the underlying null-terminated string length. That way you get the more ergonomic interface where you have it, but you can also easily pass the string to legacy APIs and libs, as well as tap into the null-terminated C string *when you know what you are doing* by the convention that your string (or data pointer) is actually a null-terminated C string. All it costs is one convention and one extra byte per string, which is how C has it already anyway.
@@StianGudmundsenHiland and that is basically what C++ has been doing with strings since C++11.
Excellent talk!!! Thx a lot to Luca for sharing his knowledge with us ;)
Glad you liked it!
Pretty nice talk! Thanks and we need more of this.
Thank you for your comment!
Super Cool, thanks a lot!
Thank you for putting this video together and sharing your knowledge. This is an extremely helpful knowledge share for someone like me just learning C.
Glad it was helpful!
satisfying and sound assessment as well as interesting new features.
c is still one of the best languages around. it simple, incredible powerrfull and the fastest language there is. i have prigrammed hava since ot was called oak and now a bit c# and alm the others but c is still king. if you want to write code for a nuclear powerplabt, space probe or the kernel for your phone, c is the labguage to use.
Really interesting talk, thank you!
This is such an awesome talk
Great talk!
Nice talk. Love the defer macro
Excellent talk. changed my view of the c language.
As someone who is starting to learn C for the first time, this talk is very useful
Thanks, this talk was of great help. I learned a few new tricks.
Great to hear!
amazing talk!!!
Learned a lot, thanks for this! I would prefer to write my own game engine in C, but since I'm trying to get into the industry it seems I'm stuck with C++ since everyone uses it. 😢
excellent talk thanks
That "defer" macro was very cool 😎
(at 43:19) math.h is not for libc, it's for libm ! And with tgmath.h, the math library is effectively generic (over long double, double, and float), which is *AWESOME*, and I think also part of modern C. But point taken that libc has issues.
You don't need parentheses around a return value. It's return ; which can have parentheses around ,but return isn't a function. return(1)+2 returns the integer 3.
I am started a game framework in C few weeks back.
I have one .c file (I include .c files and not even bother with .h) for each module (App,physics2D,input, and renderer2D) and there basically its own library and they each have not dependencies on each other.
Its a good and fresh break when I work on it from C++ Unreal.
C took the /* ... */ comments from PL/I. Also, PL/I requires the parentheses for an expression being returned. The C statement syntax is just "return expression"... *but* you can always parenthesize an expression, so C will take "return x + y;" or "return (x + y);", and the people who developed Unix had previously worked on Multics, which was written in PL/I. C would thus be accepting of what someone coming from PL/I would do by force of habit.
The defer macro described @ 18:00 hides a change in the semantics of the continue statement. Furthermore, the end will not execute if you return from the code block.
Very nice!
Thanks!
All I use is C. It’s the fastest portable and most explicit language out there.
What version of C?
I'm trying to learn it thoroughly. I'm hearing that pointers are the way to go and that passing by reference is the preference ( I agree, passing by pointer / reference is the way to go) and yet on this video they avoid pointers. Hmm
True, personally C99 is my fav
I feel like passing by reference really depends on memory constraints and general size. On an arduino I would always pass by reference, but on my laptop it just depends. I always pass structs by reference but an int or smaller? Not really (unless i want to directly modify it). Just be sure to make it const so it can't be mutated if you have no intention of modifying the reference.@@Dazza_Doo
the presenters string library looks heavenly to work with lol!
- 8:45 C++ has designated initializers now and yes you can initialize structs using "uniform initialization" as well.
- 48:00 thankfully we have std::string_view now to avoid allocation hell.
std::string_view only in C++17 though, still not supported by a lot of embedded systems toolchains.
@@alefratat4018 then use something like ETL (Embedded Template Library)? It only requires C++03 and implements its own alternatives to std::string and std::string_view
C has remained my favorite language. It is very simple (i.e. not bloated) yet powerful and at the same time very pleasing to program (except string handling). Modern languages that try to be the new C/C++, like go, zig, and rust, don't capture the joyfullness of programing that C does and introduce a lot o friction and other quircks to use. I wish I could find a job using C.
Good stuff
Glad you enjoyed
defer macro is cool. However, unlike Go, it will not call the end function if you have an early return, break, or longjmp inside the block.
My impression is that many C programmers are borderline dogmatic about not using early returns, specifically because it makes the cleanup simpler without RAII.
Many are, yes. Some C style guides require it. Not everyone, though. In some circumstances, I find early returns dramatically clean up the code and avoid deeply nested if blocks.
There is a proposal to add PROPER defer to C. I'm not sure if a pure library implementation is possible. Google "A defer mechanism for C" (can't post links).
@@mettemafiamutter5384 That could be pretty nice. Thanks for the tip.
@@mettemafiamutter5384 to my knowledge C already has something similar, although it only accepts function pointers with void return and no parameters
18:44 This `defer` is footgun™ as it does not call the end function on return statement.
or break or goto probably
Such an information video. I just wanted to know that from where did you learn these C language techniques. Is there any book available?
excellent;
I program EXCLUSIVELY in C, both front and back end.
nice
Really nice talk, I especially like the error handling ideas. I'm no hardcore C programmer, but isn't avoiding passing by reference bad in the file load -> process -> pass to GPU example? The file data is copied when passed as a value to functions, so surely that's way less performant than using a pointer to the initial data, and passing that reference through the call pipeline? Or am I missing something? Does the compiler magically know what you're doing and switch to pointers before generating output?
the struct itself doesnt contain the file data it just contains a pointer to the file data
22:50
How does accessing floats in elements[2] work with struct padding involved; isn't it possible for padding to make the second float's offset more than the size of the first float? Having the elements array assumes that the floats are packed in memory, right?
right, padding would break it, but are there platforms which pad for an 8byte alignment?
My number 1 thing I wanted to be added to C is basically a copy and paste of Rust’s cargo system. Including libraries is the worst thing in C (Even in C++).
I would also want float2, float3, float4, double2……… and so on added to C (Like HLSL). So that we all have 1 base to build off of. Everyone going around implementing there own is not very good for compatibility.
The dynamic array implementation has problems on architectures with strict data alignment. The dynarray_info struct might place your array elements in a misaligned address, which can degrade performance or crash on some architectures like ARM. This can be resolved by combining the array with the info using a flexible array member, so the compiler can ensure proper alignment.
By flexible array member, you just mean a void pointer to the start of the array, rather than just assuming it follows after the info struct?
Good catch. But how would flexible array member work with type safety? They have: "#define dynarray(T) T*" which allow you to use index operator directly on the "vector" but also without casts. I imagine your flexible array member would be of type unsigned char.
In practice I doubt this will be a problem, i.e. sizeof(dynarray_info) will be a multiple of sizeof(max_align_t).
@@mettemafiamutter5384 not true on my architecture (actual code output, clang on Arm processor):
sizeof(dynarray_info) = 24
sizeof(max_align_t) = 32
@@mettemafiamutter5384 I access elements through the flexible array member. Sort of like this (neglecting error & bounds checking for now):
#define DYNARRAY(T) struct { dynarray_info info; T data[]; }
#define DYN_PUSH(A) (A)->data[(A)->info.size++]
size_t length = 100;
DYNARRAY(long double) *numbers = malloc(sizeof(*numbers) + (sizeof(*numbers->data) * length));
numbers->info = (dynarray_info){ 0, length, sizeof(*numbers->data) };
DYN_PUSH(numbers) = 3.14159;
Now sizeof(*numbers) == 32, with padding so data[] is properly aligned.
I suppose you could also union dynarray_info with multiple max_align_t to achieve proper alignment. I'll have to play with that.
I'd say use a microcomputer (the raspberry was the first) - one with easy access to an Assembler. You get to know the chip used (the architecture) and will never run into a problem with libraries as you understand what they rely on - Assembler. It's up and coming is Assembler. You run into problems with a x86 protected chip - which they all seem to be. Finally, we can make video games again!
As for shared pointer issues in C I think I found a way to solve it with custom mutices, I attached octal permissions to it and made it so only the thread declared as the owner can destroy the attached data and only if the mutex has no threads attached and no threads with permission to attach (so when the owning thread tries to revoke it's own permissions it will only succeed if it is either passing ownership to another thread or the mentioned conditions are cleared, in which case destruction is triggered, the mutex pointer is no longer valid then)
"prefer value to pointer" wow that's kinda new to me, wouldn't it be expensive if the value is big? i usually would not pass by value if the value size exceeds a pointer size
Yeah, I'm also puzzled by that. I think of it like this until I find out how it really works:
If it's reasonable to assume this struct can be stored in CPU registers, pass it by value.
So my uneducated guess is you could safely pass around 9 values of 32 bit size, probably more.
No idea what kind of hell breaks loose if you pass something really big, like contents of an image file by value. I expect nightmares.
It only really starts to become expensive if sizeof(type) > sizeof(void*) * 2 (and on some platforms up to 4).
The C++ standard library does a lot of passing by value (when this is the case) and it's not known that it's slow.
@Vironacorus For the aliasing issues, you can use restrict (but I get your point).
@@grafgrantula6100 contents of something like image data will be stored in an array. And the array var will only contain the pointer(will decay to it when passed).
So I don't see How is this even remotely possible.
It may be possible if u use thousands of ints in a struct for doing that but why would anyone do that.
It depends. If you have a good code-structure than the compiler will see what you are doing and optimise accordingly.
Say you are passing in a large struct by value, inside the function you are manipulating this local copy and then returning it, and at the calling-side you are not ever touching the original struct ever again: The compiler might very well re-use the old struct and never even create a copy.
I've been programming in C since 1980, but I was not aware of the spectacular things you can do with struct and union initializers. This is an excellent video, thanks!
Thank you for your comment which is much appreciated.
Developing in Code Blocks(2020) specifying the C99 standard to the compiler, it still forces struct to be written in the code:(
In which development environment are these innovations implemented?
I only use(ed) C for speed. When I see something like "Everything else gets set to 0" in struct initialization, I am thinking about more safe languages (scripting languages) that do things to protect you as a programmer and not trust the programmer with the data types. At least we know the C compiler will use the most efficient way to initialize all members to 0.... hopefully.
Scripting languages may protect your memory, but as soon as your project reaches a non-trivial size, you don't get much help with your types. The older I get, the more I appreciate static analysis. Maybe it's just incipient dementia, but the compiler does catch a lot of bugs for me these days...
Struct initialization has been part of c++ since c++14 i believe. Both in clang and gcc
They made it into the standard C++20. It might be possible that some compiler supported it before that.
47:05: It most likely doesn't allocate due to small buffer optimization
Great talk! I really liked the solutions for defer and strings. But it was really painful that you don't mention DLang as you talk about these features in Go, Rust and others (except mentioning it at the end). DLang had these features when all these other languages didn't even exist.
Great point!
Good point, I plan on giving a refresh on this talk soon and I will definitely mention Dlang too!
So good uwu
Note: Rust's error handling is also very built-in, the try keyword of Zig is the ? unary operator in Rust ^^
Very informative Thankyou
What will be a good GUI library which works in MacOs & Windows & Linux for C
Since you are a game developer you must be experienced & familiar with GUI libs (std applications development,Not Game) let us say something similar ‘look and feel’ Of Visual Studio Code IDE
Great talk, very good presentation, but PLEASE don't code C like this.
Pretty much none of the macros presented were safe macros, and I'm pretty sure I've seen a bug around. There's no need to use heavy macros like that, at this day an age compilers are sophisticated enough that using functions for this kind of stuff has zero overhead because it will auto inline them.
In fact its like that by changing to functions with the use of 'restrict' you might even get a boost, because right now the compiler has no guarantees w.r.t. aliasing.
C language is my mother tongue 👅
This idea of value oriented design is interesting. But for the embedded world on MCU, where C is still the main goto lang, it is not a good option where memory is so limited you dont want to pass cpy arguments.
Btw thank you for this great presentation, I learn new ideas of API design notably on strings handling and error handling. I must admit I was a big user of goto err; syntaxe. Maybe I will re-consider this.
Also I will dive into Zig, it looks promising... having error and build system built-in in the lang!
The value oriented design works well for the str (pointer+size pair), but not the holding buffer.
Where are your benchmarks and unit tests @:)
Does anyone have any further reading I can do on modern C? I'm very interested
few people get it C is infinite in scope you can do anything you want with it and in any shape you want the only limit is yourself
The _Generic() example is not a good one. Try adding a string comparison function char *mins(char *a, char *b) and see how things break. A better option would be:
#define min(a, b) _Generic((a), float: minf, int: mini, char *: mins)(a, b)
I did not understand how the defer macro works 😞
regarding vectors, it can do the trick, but what if you dynamically alocate at runtime? Defines are replaced at precompilation. Vector alocates on the heap, not on stack. To do the same job in c, it's quite a hassle..Also you have polymorphism in c++... Coming from embedded development using C, i see c++ as a much better language in some cases.
Honestly, polymorphism is not that great. Sure, it makes the code 'prettier' but also harder to follow or debug, IMHO.
modules, it is the reason why i gave up with C and C++ and moved to D
i just don't want to deal with headers / predeclaration etc etc, this is such a pain that wastes me so much precious time
i want some sort of modules in modern C, otherwise it'll always feel bad..
@@Hoowwwww there is a similar project by Jens Guesdt(he's on the ISO C board, co author of C11 standard), just search it.
Any reason why isize_t is used for things that should be unsigned (size_t)?
I have the same question
So that difference of two sizes(some fn can do that for some stuff) doesn't under flow and become a large positive number instead of the expected negative or positive
Maybe for negative indices like python has
I think it has something to do with undefined behavior, which weirdly enough would be a good thing in this case!
I heard that using "int" instead of "unsigned" even when you always expect a positive value can lead to speedups thanks to compiler optimizations. Basically, for 'unsigned's, the compiler has to handle the possible overflow which can sometimes add a couple instructions; while for 'int's overflow is undefined so the compiler may act as if it could never happen (and provide no guarantee on the result)
often signed values are used to tell the compiler "hey, these values are small and we will pass along only sane values. So there is no reason for you to do any bounds/overflow-checking".
But it can also be the other way around: since C99 integer division is rounded towards zero. This means dividing signed integers is slowed cause it needs to handle the negative case having a different rounding.
I got screwed by flash dying. Took me 8 years to figure out what to use next. I wanted something that wouldn't die on me when it takes me forever to build stuff. I chose C and JavaScript .
Great talk! I wonder why you declare structure types as:
typedef struct kv {...} kv;
instead of a simple:
struct kv {...};
in C if you dont use typedef like that you have to annotate the typename with the keyword struct everywhere, eg:
struct foo {};
void bar(struct foo f);
vs
typedef struct foo {} foo;
void bar(foo f);
So you don’t have to type out struct every time
It allows you to not have to write "struct kv" everywhere
so that we don't have to write struct every time we make a variable or pointer for struct kv, like kv *newvar; instad of struct kv *newvar;
Hello, just struct kv {...}; won't compile in C :(
I always miss ASM and compilers. Should be mandatory in any language discussion. Remember, compiling isn't interpreting. I prefer approaching language from how these machines handle strings, like gnu readline.
27:35 I hate adding prefixes and suffixes to specific names. Like "_t" to types in this case.
You might even have your first_split function return a pair of string views, instead of mutating its argument. Not really sure if it's more convenient.
typedef struct { str _0, _1; } str2;
str2 first_split(str, str);
str2 s = first_split(cstr("2021/03/12"), cstr("/"));
assert(!memcmp(s._0.data, "2021", s._0.size));
assert(!memcmp(s._1.data, "03/12", s._1.size));
31:47 "vector" is a bad name for a dynamically sized array.
I already have a lot of vector types in all my math libraries:
- hardware vector types for efficient math on multiple values at once
- linear algebra (vectors are just 1×n matrices)
- geometric algebra (vectors are just 1D multi vectors)
At 44:16 shouldn't `log("Cat: {cat}", c);` be `log("Cat: {cat}", &c);` ?
No u just pass c by value
Then the log function
Passes &c to the registered function(print cat, probably stored in a hashmap as type function pair)
Was the old function syntax even in the first standard C90
K&R C syntax has been in every standard from C89 through C11, but also declared deprecated the whole time.
Compilers will not complain about an old-style parameter list, but may warn if any parameter type or the return type is implicitly int, for example when declaring the main function as "main(argc, argv) char **argv; { ... }".
Hi there, sorry I’m a bit late to the party but I don’t think I completely understood why your str_pop_first_splitter function takes less allocations than strtok
I don’t really see why adding the null terminator at the end of the popped string is a bad thing in this scenario, does strtok need to realloc the tokenized string for that singular ‘/0’? Thank you
What I dont get is why dont they make a new C version that breaks backwards compatibility on some code patterns and adds all these common patterns to the language itself
you already answered the question yourself
Just the function overloading using _Generic is not that stable as in c++
C is only thing I can comprehend tbh, other languages are too big.
This guy is so funny. He is so proud he reinvented glib string features lol . Typical developer who does not investigate existing solutions before reinventing the deep dish.
C++ has C-Style struct initialization since C++20. What you are saying is not true anymore.
False
C++20 designated initializers are very limited
By that u can only do union
And non nested struct initialization and fields in the initializer should be in same order as they were in the struct data type
So u cannot initialize arrays using that
U cannot initialization nested structs
U cannot use any order of fields in that initializer
All of which u can do in C
So I think that C++ designated initializers not very useful
C's file I/O library has seemed extremely archaic. Whenever I tried to jump between reading and writing different files, it always messed up.
In fact, I/O in general is something that needs to be fixed. It's pretty much the only reason why I'd prefer to develop in C++. When I call I/O functions, I don't want to have to clear the input or output buffer manually, because it's confusing. Not to mention that it never tells you when it's messed up. It's a very silent failure when you mess up I/O in C.
Aside from that, the only major problems I've had have been with libraries that don't manage memory for you. I've been very quick to adjust to malloc() and free(), but SDL, for example, really forces you to read the documentation to find out if your program will leak memory. I've run Valgrind on my programs from a few months ago and found megabytes of unfreed memory, and none of it was from malloc().
Then again, I've had no idea about these special tricks you've put here. Generally, I manage memory and strings in C in a very simple way, which is "if I add a malloc() call, I add a free() at the exit points." I would explore that garbage-collection approach you've mentioned. It looks neat.
Where were your memory leaks ?
I use SDL2
For IO just use your operating system's API. In fact I would prefer to use it for memory management too. It's faster and gives you better control
You forgot to mention Scopes here.
I loved this video but, much of the things here, after getting a little familiar with rust, feels like rust has much easier way of doing all the things mentioned here. It feels rust is whole generations above C.
I still enjoyed this video, C is an important language that paved much of the way to this modern software world, and it's nice to see, the C guys are still doing nice things, inventing nice ways to solve problems, betting better able to use the language, and stuffs.
It is kind of annoying that C++ is just partially allowing struct-initialisition now. And every time it is prossed it was shut down for stupid reasons that basically boiled down to "nobody needs this".
Also C should start with giving us named parameters. that can be simulated with some macro-magic but really that should be part of the language.
And C++ std::string_view - most of the time i would say stay clear of that. There are way too many pitfalls with that, not the least are the standard-library functions that might still convert it to const std::string&.
Why are you so afraid of pointers?
26:10 Isn't this what monads are?
its something you can do with monads, but monads are a strictly more general concept.