20 ms·
Modern C for C++ peeps (2019)
- zabzonk 4y ago> theoretically RAII is about general resource management, not just about memory. But at least in my experience, it’s always about memory management only if you ignore things like iostreams, thread locks, etc.
- TillE 4y agoYeah, RAII helps you manage any type of OS handle with minimal effort. I can grab a mutex with std::unique_lock and not have to worry about releasing it manually, which means simpler code and fewer bugs.
- jstimpfle 4y agoThis is a mostly theoretical argument in my experience. It's quite easy to close the things that you need to close (if there are very many, something is wrong). RAII can improve "scripting" speed when putting a lot of automatic variables on the stack. However, a lot of those variables need to be moved to the heap when proofing out the code, and that consolidation isn't going well because RAII doesn't work with generic code (void-pointers, memcpy etc). You need to go all-in with RAII containers. Which causes a lot of boilerplate and increases compilation times.
- moloch-hai 4y agoYou appear to be using "generic" to mean "C-ish". That is not what the word means, in context, and there is no reason to do any of it anyway. "Proofing the code"? Does that mean anything at all? For RAII on heap objects, you have std::unique_ptr, and no boilerplate. Anyone failing to lean into RAII to manage resources is just choosing to write unreliable code. Or C, but I repeat myself.
- mgaunard 4y agoDue to language limitations, a lot of C programmers make all their variables shared pointers, which means they don't have to figure out how to do value semantics, moving and copying objects, especially in multithreaded environments. Interestingly, it seems Rust programmers do the same too, because otherwise programming in Rust is too hard.
- jstimpfle 4y ago> Due to language limitations, a lot of C programmers make all their variables shared pointers, which means they don't have to figure out how to do value semantics, I wouldn't approve. Make shared (i.e. ref-counted) pointers when actually needed. Writing C is best when not mimicking bigger languages (i.e. with GC), but when figuring out how a system can work optimally.
- mgaunard 4y agoYou might not approve, but the reality is that most C code is built like that.
- secondcoming 4y agoI assume you mean C++ here? In any case, in my experience this bad practice comes from Java devs who’ve moved to C++.
- moloch-hai 4y agoJava Disease is its own topic. But most shared pointer ownership in C is wholly ad hoc, without even reference counting.
- mgaunard 4y agoNo, I mean C. People don't do that in C++, since C++ provides ways to define value semantics (copy, move, assignment).
- tialaramex 4y ago
- EliRivers 4y ago"It's quite easy to close the things that you need to close" Very true. It is easy. Not difficult. That doesn't seem to stop enormous amounts of C++ code being created in which this is simply got wrong. Just because it's easy doesn't mean that huge numbers of programmers won't still get it wrong. They do. Regularly. If they adopt RAII conventions, they screw it up far less frequently. Those are the facts.
- jstimpfle 4y agoI prefer reducing complexity instead of hiding it in language cleverness. (Because hiding complexity doesn't make it go away). Try adopting ZII (Zero is initialization) and context managers (pooling, chunking, freeing in batches etc.), this can reduce the amount of boilerplate to a minimum. Writing generic code like that (down to the binary level) also helps improving many other metrics like executable size etc.
- EliRivers 4y agoThat's nice, and I'm sure your code is just lovely, but there's just one of you and millions of people writing C++ who can't do that but can just about manage some simple RAII.
- kllrnohj 4y ago> Because hiding complexity doesn't make it go away). Try adopting [..] context managers (pooling, chunking, freeing in batches etc.), this can reduce the amount of boilerplate to a minimum "don't hide complexity in small, single-purpose, composable containers, instead hide complexity in hulking behemoths which can consume months of refactorings & iterations" ? That's not simplifying your code to be generic. That's adopting a particular framework & coding style. Which yes, you have to do in C, but you're doing that because of language issues. You're not saving yourself from "language cleverness"
- moloch-hai 4y agoYeah, stopped reading after that. How wrong does he want to be? It is hard to tell what the point is of this article is. Yes, the C-ish subset of C++ differs from ISO C, in trivial details. That is irrelevant to C++ users, who may consult ISO C++ to discover the actual subset. They interact with any C only when they #include a header for a 3rd party C library, and then the relevant subset is what, exactly, appears in that header. There is really no legitimate reason to code C anymore: A C++ compiler is available for any modern development target. Confining yourself to any C subset (with or without ISO C marginalia) amounts to crawling when you can fly. To be more specific: nothing that can be done using C is unavailable to the C++ coder, but enormous power is available to the C++ coder and wholly unavailable in C.
- jstimpfle 4y agoFor me the legitimate reason to code C is to get stuff done, period. There is hardly a need for more (to do what I do -- systems in the broadest sense) and there's a lot of time saved by not thinking about 21 "safe" and "ergonomic" ways to write everything in C++ (that very often end up being not that safe, and unintelligible). Yes, there is the "subset" argument, only use what you need. But I don't buy that, I'm not like that, it doesn't work for me. Constraining myself makes things easier.
- glowingly 4y agoThis somewhat mirrors my own development. I started in C, and am still in it for systems/OS development at work. In UI development, we had older C toolkits, but are replacing them with newer toolkits that are C++. I started off trying to just do C business logic with some C++ glue code for the UI. Sticking to what's familiar. In the end, it was just easier to do everything properly. All C in areas that are C. All C++ in areas that are C++. Same with C#. Trying to mix them is just more work than necessary. At least I get to add C++ to my resume. There is a little joke at work, w.r.t. our existing codebases and code choices. They are career-driven choices, not technical choices. The result is a mess of a codebase, but I'm fully embracing that now.
- RcouF1uZ4gsC 4y ago
- wayne 4y agoWhile not trivial, the problem of remembering to free everything is slightly simpler when you don't have to worry about exceptions.
- pjmlp 4y agosetjmp/longjump/signals
- otabdeveloper4 4y agoYes, just put a "if (errno) goto err" after every function call in your program. (That was sarcasm, FYI. Obviously it's 2023 and nobody wants to do manual function calling boilerplate as if you're programming 1960's assembly.)
- mypalmike 4y ago*for some definition of nobody
- WalterBright 4y agoI used to do `if (errno) goto err;` a lot. I eventually realized that nested functions did the job much nicer: void err() { printf("oops, we failed"); } ... if (errno) return err(); This cleaned up a lot of my code. (The optimizer would of course inline the function, and the common tail merging would automatically convert it to the goto version in the optimizer. So this was cost-free.) Why C doesn't officially have nested functions is, well, that's C!
- mhh__ 4y agoThis is my one of my favourite patterns with nested functions - clean code, and avoid a ratsnest of 70s basic style GOTO. It starts with one goto, then one more, then another. The first goto is usually clever but then someone else arrives and hacks another one because they don't want to change the code too much.
- 4y ago
- cjfd 4y agoOne can do lots of things with RAII. I once wrote some classes to generate xml. I used constructors to write opening tags and destructors to write the corresponding closing tags....
- _dhruva 4y agoWe implemented a full blown profiler for win32 platform using RAII to track profiling data back in ~2000 https://bitbucket.org/dhruva/cramp https://bitbucket.org/dhruva/cramp
- Warwolt 4y agoA very good article. I think it's nice as a C++ dev to see how problems can be solved within the restraints of C, and how we might learn from those limitations where C++ might have strengths and where we might wanna lean on simpler C mechanisms.
- heywhatupboys 4y ago> Wrap your structs in a typedef NOOoooooo, please stop doing this. unless you are a library author who gets to define things like uint8_t, please do not do this > Use struct wrappers for strong typing even BIGGER no. This is completely misunderstanding what a typedef is and what it should be used for.
- fathyb 4y agoCould you please share why wrapping structs in a typedef is bad? I'm culpable of doing this quite often.
- spacechild1 4y agoIt is common practice and IMO not bad at all.
- enriquto 4y agoIt is largely considered bad and misleading style. Why would you ever hide that a variable is a srruct? Better to keep typedef only for basic types and for function pointers.
- detrites 4y agoHow is it hidden? Can't the inquiring mind simply examine the codebase to see it's a struct? And if they choose not to, isn't it on them if it turns out to be something other than what they assumed it was without looking?
- moloch-hai 4y agoIt is hidden because you have to "examine" somewhere else before you know. But it doesn't matter, because you don't need to know unless it is subject to invisible implicit conversions. What might you do differently, having "examined" the typedef?
- 4y ago
- pjmlp 4y agoYeah, if there isn't any opinion available other than using C, pity that security isn't part of modern C lexicon.
- epolanski 4y agohijacking: What is a good reference to learn the minimal amount of C++ to do codewars/leetcode, write minor programs? Would prefer something that keeps with modern C++ if possible.
- rramadass 4y ago"Minimal" and "C++" do not belong in the same sentence :-) That said, C++ Primer by Lippman, Lajoie, Moo is a good book for beginners. I think the latest edition covers upto C++11. You can then followup with Modern C++ Cookbook by Marius Bancila for newer features upto C++20.
- jasinjames 4y agoI haven't used C++ very much, but I found "A Tour of C++" to be a great introduction. It focuses on the core of C++ OOP semantics and is written by Bjarne Stroustrup himself. I like the book because it doesn't try to answer the question "which feature is the canonical way to solve problem X", since from what I understand, the "canonical" way to solve a problem in C++ changes quite frequently as new features are added. It was recommended to me by one of my professors a few years back for being short and accessible to a person who is looking to learn what makes C++ special, not how to program from scratch. EDIT: Reading the page again, it looks like the book does have some discussion of C++17 and C++20 features, but I focused on the first few chapters which really nail down the whole copy and move semantics feature as well as operator overloading.
- Jtsummers 4y agoWhen I went on a Modern C++ bender a year or two ago I went with Stroustrup's A Tour of C++ (2nd edition when I bought it, 3rd is out now). It was a good read, covered a lot of details quickly. The rest of my re-learning was using cppreference and reading through the list of standard types and functions/methods on them, trying to translate old (clunky, often subtly broken) C++03 code into modern C++. https://www.stroustrup.com/tour3.html https://www.stroustrup.com/tour3.html https://en.cppreference.com/w/ https://en.cppreference.com/w/
- mhh__ 4y agoRead leetcode solutions. If you can already code you'll pick it up in seconds.
- mrkeen 4y agoComments, typedef wrappers, initialising structs? C is great for what it is, but does this article really knock down the "C is a subset of C++" argument?
- rightbyte 4y ago"C is kinda a subset of C++" is close enought for any non language lawyer. The author names C99 feutures that has been in GNU C89 since whenever. I mean. C is whatever compiler you use. Some people just care too much about what ISO says.
- flohofwoe 4y agoIn general I agree that the C standard isn't really relevant for day to day work and that it matters more what compilers actually implement, but a C library author needs to follow the users, and that means at least supporting GCC, Clang and MSVC (and the MSVC C frontend follows the C standard much more strictly than GCC and Clang).
- rightbyte 4y agoYe I agree.
- kevin_thibedeau 4y agoMS can pound sand. They ignored C99 for over a decade. It's on them to support common extensions if they want to have a viable C compiler.
- codeflo 4y agoI appreciate the intent, C has become its own thing with its own set of conventions, and people still writing "C/C++" often reveal that they don't have a clue about either. I think many C programmers might disagree with large parts of this, however. Some things that caught my eye that might be controversial: > Wrap your structs in a typedef The only reason to do this that's mentioned is "annoyance", which I think is a weak reason if you have the kind of problem for which you're considering to use C. The author already mentions that it has a weird interaction with forward declarations. It's my understanding that many C codebases don't do this, simply to be more explicit about what's a struct and what's not, and to avoid typedef explosion. > the POSIX standard reserves the ‘_t’ postfix for its own typenames to prevent collisions with user types – make of that what you will ;) IMO, that's not something to casually dismiss with a smiley. If it's reserved, don't do it. > Typedef only creates a weak type alias not a proper new type (it’s really not much better than a preprocessor define), meaning there’s no warning when assigning to a different type from the same base type The point is somewhat valid, but the author seems a bit confused about terminology here. It's not "a different type from the same base type", instead, it's a different name for the same type. The comparison with the preprocessor is unwarranted. > Be (somewhat) afraid of pointers Why? The following rant about RAII doesn't really give that many clues. Handles can have a performance penalty vs pointers -- or be a performance benefit, this really depends on the details. Also, this is where it might have been helpful to go into different conventions about allocations, whether the caller or callee should allocate, etc.
- rightbyte 4y ago> people still writing "C/C++" often reveal that they don't have a clue about either. How about "Java/Groovy" or "C#/VB"? C++ and C are related enought to write that I think. Especially since it is common to mix the languages in projects.
- nextaccountic 4y agoI think you're right. For example, I think Android apps written in Java/Kotlin are better than apps written in web technologies
- 4y ago
- stareatgoats 4y agoPrevious discussions: https://news.ycombinator.com/item?id=27288145 https://news.ycombinator.com/item?id=27288145 (May 26, 2021 — 94 points, 21 comments) https://news.ycombinator.com/item?id=21093727 https://news.ycombinator.com/item?id=21093727 (September 27, 2019 — 102 points, 16 comments)
- jstimpfle 4y agoC hasn't seen a lot of relevant changes since C89 as far as I'm concerned. The most important for me is definitely declare-anywhere (which had been widely supported prior to standardization in C99), it removes most of the friction when quickly trying out new stuff. Here are some things I almost always do to improve ergonomics (some of this is debatable and none of this should be exposed in a library, at least prefix things) // common.h #pragma once #include <assert.h> #include <stddef.h> #include <string.h> #include <stdint.h> #include <inttypes.h> #define ARRAY_COUNT(a) ((int) (sizeof (a) / sizeof (a)[0])) #define STRUCT(name) typedef struct name name; struct name #define CONTAINER_OF(ptr, type, member) ((type *) ((char *) (ptr) - offsetof(type, member))) #define ALIGN(n) __declspec(align(n)) #define CACHE_ALIGN ALIGN(64) //XXX typedef uint8_t u8; typedef uint16_t u16; //... Each .c file includes "common.h" as the first thing. Each .h file has #pragma once at the beginning. ARRAY_COUNT(a) is used to get the capacity of a C array without using defines (which is brittle). Unfortunately it's imperfect because code breaks when you change arrays to dynamically allocated buffers and you forget to switch to using dynamic capacity values. This is one case where I'd like to see some standards update that allows us to improve safety. STRUCT(x) is used to declare struct x with typedef -- I've grown to hate tag namespaces for their boilerplate. I simply uppercase types, and the problem (that struct tags were invented to solve) is gone. Probably I'll make something like this very soon to get started static void *__xmalloc(size_t size); static inline void __xmalloc_array(size_t size, size_t count) { /* ... */ } #define xmalloc(type) ((type *) __xmalloc(sizeof (type))) #define xmalloc_array(type, count) ((type *) __xmalloc_array(sizeof (type), (count))) Later obviously more sophisticated allocation is needed, but the point here is how macros can be employed to improve safety and ergonomics. This is how I do polymorphism in C basically, not much more is needed than abstracting over size and maybe alignment for almost everything. In some cases, manually set up v-tables make sense from an architectural perspective. I also often make a very simple "logging" module that does basically printf logging but with \n automatically added and optionally printing out __FILE__ and __LINE__ but without requiring to re-type this all the time. Something like void __msgf(const char *file, int line, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (__debug) printf("At %s.%d: ", file, line); vprintf(fmt, ap); printf("\n"); va_end(ap); } #define msgf(...) __msgf(__FILE__, __LINE__, __VA_ARGS__) Other than that, I like to not think about the language but about what the machine is going to do. How to decrease size of working set and generally speed up the program. How to speed up compilation (don't expose all the internals in the .h files). Things like that. There are very few container data structures actually needed, most of the time it's just C-arrays, dynamically allocated buffers (pointer + capacity), and some simple queues / synchronization primitives. Also linked lists. A favorite of mine are chunk-lists. All of these are very simple to implement -- there is hardly a point of making them in a super-general library, it's ok to code them from scratch for all but the smallest projects.
- deleted 4y ago[deleted]
- stabbles 4y agoYou can have RAII with GCC extensions in C: char *buf __attribute__ ((__cleanup__(free_buf))) = malloc(1024); This calls free_buf(buf) when buf goes out of scope. I’ve actually seen projects use it in the wild to close files or free memory in branchy functions. [1] [1] https://github.com/containers/bubblewrap/blob/c54bbc6d7b78e7a45016efe0c4ac8af9d209aa20/utils.h#L165 https://github.com/containers/bubblewrap/blob/c54bbc6d7b78e7...
- moloch-hai 4y agoThat is only a small fraction of RAII. The trivial fraction. If malloc fails, real RAII gives you an exception, which you may catch somewhere convenient. Real RAII runs a constructor. If construction fails, you get an exception. Lacking those, it all has to be done and checked by hand on the spot, and is often wrong, because not tested.
- JonChesterfield 4y agoNot sure that's the part of C++ I'd heap praise on. For one thing, real RAII invokes the destructor, not a constructor. If your constructor throws, the destructor isn't going to be called. If your destructor throws, you're one passing exception away from std::terminate. Plus malloc doesn't throw, though new sometimes does. Or, construction could return an optional/maybe style thing that you branch on, and then you don't need goto (sorry, non-local come-from with pretty branding) in your base language.
- mgaunard 4y agoThe important part of RAII is that the constructor actually acquires the resource. The fact the OP insisted the important part of RAII is not that but running destructors shows he hasn't understood the most important concept in C++ programming.
- svvayy 4y agoThe destructor is the distinguishing feature of RAII. Lots of languages have constructors without RAII because they do not have deterministic destructors. Examples are Python and Java. Can you expound on what your understanding is?
- justinlloyd 4y agoOn the "no formal parameters function" - You can forward declare the function with the parameter list, and then define the function without a parameter list. This should (might) generate a compiler error/warning in very old compilers, but really it is an indicator that you are trying to use an older style of declaration with "modern" convention. If you have a forward declare you can then bring in different headers (or use a pre-processor block) depending on need that will redefine the function. The reason this ability exists, in all the different incantations you can use, is to ensure that very old-style C will still compile. You could also pop things off the stack manually inside a function all the time, even without a paramter list, popping off an "invisible" parameter also let you do things like return to a different part of the code than the invoking function. The compiler I worked on in 1985-ish supported forward declarations, which was "fancy and new" at the time. Insert obligatory, "I was there Gandalf, 3,000 years ago."
- mgaunard 4y agoMost of the introduction is inaccurate. C++ does its best to reintegrate all new features of C into C++ (not that C has anything much going on since 1999), and a lot of efforts are made to unify the languages. The only reason they're not unified is because there are a few irreductibles that are against it, but as written in the article, even Microsoft is in favour of just having C++ and no C. The creator of C++ himself is saying C++ is a better C and his goal was for C to cease to be and be replaced. The reasons C is not a strict subset of C++ is because some features of C are outrightly dangerous and were changed in C++ for good reason. Most of the new features of C these days are actually backported from C++, such as atomics. Also the whole thing about how in C you should think in terms of modules rather than classes also applies to C++. OOP remains a bad paradigm regardless of the language. And funnily enough some of the biggest C frameworks are dedicated to providing OOP in C (e.g. glib).
- asveikau 4y ago> in C this function takes any number of arguments ... I guess it’s a leftover ‘syntax pollution’ from old K&R style function declaration syntax). ... Instead in C, declare the parameter list explicitely as ‘void’ so that you actually get compiler errors when accidently passing arguments to ‘my_func()’ ... I thought that C99 got rid of the "implicitly allows any number of arguments" thing. Maybe I'm mistaken. The place where I used to see this was header files that might be used by a pre-ANSI compiler. They would omit arguments in the header declarations of functions. I even remember some X11 headers putting those arguments in an ifdef, so that if you had a decent compiler you'd get the checks, but it would still work on ancient compilers. It wasn't that the function bodies would have args missing and somehow use them. It was about declarations, the kind you see in header files.
- flohofwoe 4y agoC23 finally gets rid of it, along with a couple of other warts (like '= {0};' to zero-initialize a struct, instead '= {};' is allowed now).
- travisgriggs 4y agoI do a bit of embedded C, 32kB for combined heap and stack, that sort of thing. I wonder if the article author heralds from the same space. I pretty much found myself going yup yup yup for most of this. But I also know that when I move into higher powered/level domains, I don’t keep using C.
- JonChesterfield 4y agoAnyone know of a better blog/reference for why to use C over C++ today? I'm still writing some of my own stuff in C but honestly I think that's inertia. Freestanding C++ with the exceptions turned off would work fine.
- dataflow 4y agoI don't know of a reference, but C encourages to do more things at run time, whereas C++ encourages you to do more things at compile time. Simplest example of this is probably qsort() vs. std::sort(), but it extends far beyond this. So you end up with slightly slower code in C, but much faster compile times, and much smaller binaries. So if you want smaller binaries or faster compile times, you'll have an easier time keeping them in C. Of course you'll have an easier time keeping & introducing more bugs in C too, but that's another matter.
- pjmlp 4y agoThere are only two valid reasons, UNIX/POSIX kernels that naturally will hardly embrace anything else, and embedded toolchains that refuse to embrace modern times.
- rramadass 4y ago>why to use C over C++ today? Simplicity. You can keep the entire language in your head and everything is explicit with no "magic" involved. That said, you might want to start moving to C++ slowly, first as a "better C with user-defined types" and then to OOP/Generic/etc. programming goodness which gives you a much larger design space.
- JonChesterfield 4y agoThere's pretty much all the same compiler magic either way. At least C++ gives you placement new and std::launder, C gives you malloc and you cross your fingers that the compiler's alias analysis goes your way for now.
- WalterBright 4y ago> You can keep the entire language in your head Explain, without looking it up, exactly how macro expansion works. (Don't feel bad. I've written 3 implementations of the CPP, and still couldn't tell you how it works without carefully reading it again.)
- sfpotter 4y agoC’s advantages over C++ don’t have much to do with language features. I don’t know what the author was thinking here. Any diehard C++ user is not going to be swayed by “fewer features”. Why not write an article that gets into the actual strengths of C?
- stephan-cr 4y ago> Especially /W4 on MSVC will generate a lot of fairly pointless spam though, [...] I think the author meant /Wall.
- flohofwoe 4y agoI could have sworn /Wall is a fairly recent addition to MSVC, but according to godbolt it seems to be supported since at least VS2015.
- bgoated01 4y agoI'm in a job where I was taught modern C++ from the beginning, and I don't have the C89 familiarity that the author assumes here. Anyone aware of a good resource to learn C coming from a modern C++ background? I'm interested in learning it for hobby embedded systems projects.
- moloch-hai 4y agoThere is absolutely no need to confine yourself to C for embedded projects. C++ is supported everywhere by default.
- dang 4y agoDiscussed at the time: Modern C for C++ Peeps (2019) - https://news.ycombinator.com/item?id=27288145 https://news.ycombinator.com/item?id=27288145 - May 2021 (21 comments) Modern C for C++ Peeps - https://news.ycombinator.com/item?id=21093727 https://news.ycombinator.com/item?id=21093727 - Sept 2019 (16 comments)
- WalterBright 4y ago> integer types have much clearer names now: uint8_t, uint16_t, int32_t etc… (these are not built-in but defined in the stdint.h header) The problems: 1. the C Standard Library doesn't use them 2. integer promotions rules still pertain to unsigned char, unsigned short, etc. 3. more generally, the language semantics are not defined in terms of those types
- nayuki 4y agoCorrect. For example, if int is a 32-bit type (as it commonly is), then uint16_t will be promoted to signed int, and (uint16_t)0xFFFF * (uint16_t)0xFFFF will cause signed overflow and trigger undefined behavior.
- xigoi 4y agoAlso, printf doesn't support them.
- gomijacogeo 4y agostdint.h includes definitions for format strings for printf/scanf-type functions (e.g. PRId64, ...) for all the new types. Using them is a bit clunky (e.g. printf("%"PRId64"\n",n);), but you get used to it.
- WalterBright 4y agoTrue, but "a bit clunky" is an understatement for how awful they look. I've never seen them used in the wild. I don't use them either. What I do use (since D supports calling C printf) is have the compiler check the format string against the types of the arguments. This means it won't compile if there's a mismatch. Many C compilers do this, too.
- WalterBright 4y ago> Enable all warnings! The trouble I've run into with that is different compilers have different warnings, sometimes they are mutually contradictory, so it is not possible to have portable code that doesn't trigger warnings in one compiler or another. I.e. the general problem with warnings is the language gets balkanized into multiple competing and incompatible dialects. What should happen is to carefully examine the warnings, and adopt the best into the language Standard as errors. For example: if (a < b < c) That is pretty much 100% a bug in the code. Just make it illegal already. D does. For another: for (i = 0; i < 10; ++i); { ... } Long ago, an expert C programmer came to me once and said he'd spent all day trying to figure out why his loop only executed once. I pointed to the semicolon. He sighed. I put a warning for that in the compiler. I noticed that other compilers eventually did, too. In D, it's an error. A third example: In the Joint Fighter coding standard, it says don't use `l` as an integer suffixe, as in `124l`, because in many fonts it looks like `1241`. Solution: D does not allow `l` as a suffix. There's is no reason to support that suffix. No reason to put it in coding standard. Do the world a favor, just make it illegal. Done!
- johnisgood 4y agoUse proper fixed-width fonts! :P
- roland35 4y agol'd th0ught you wou1d say that!
- ndesaulniers 4y ago> sometimes they are mutually contradictory Right, there are definitely some C++ specific warnings for different language compat rules (around std::move IIRC) that are non-mutually-satisfyable. This article recommends -Weverything w/ Clang. Clang developers DO NOT ENDORSE the use of -Weverything.
- itsJustTrivial 4y agoThe third one must be satire and if not your support for it must be
- saboot 4y agoI'd like to ask HN for references explaining how to layout a large program in C, for someone used to thinking with OOP. For example for a moderately complex arcade game, how would I manage all the states, entities, and interactions? I can write any simple C program but this always trips me up.
- huijzer 4y agoBasically just like you would in OOP. Put related variables next to each other in a group and pass this group around. In OOP this is called a class and in classless languages such as C this is called a struct. The functions on this object can be organised in modules just like would be done in OOP. See also https://gamedev.stackexchange.com/a/172405 https://gamedev.stackexchange.com/a/172405.
- fmntf 4y agoThink of the Linux kernel. That's a huge project, organized in an object oriented fashion. Is there a difference between this->method(arg) and method(this, arg)?
- hot_gril 4y agoIt'd be more like typename_method(this, arg, &err) in C because there are no namespaces or exceptions. Also you'll have to manually call destructors. So yeah, it works fine, just more verbose cause it lacks some syntactic sugar. I prefer C for simple or high-performance programs, and I can see why people might want C++ for larger ones. Problem is some people make a mess pulling out all the stops in C++ OOP features.
- zabzonk 4y agodefine structures, define functions that take those structures as parameters. this is basically what OO is all about. but writing it in C++ will be easier.
- IChooseY0u 4y agoWhat's the point of this? Just use an int. typedef struct { int val; } meters_t;
- deleted 4y ago[deleted]
- 6equj5 4y agoTo me, the point of it is just to be more explicit and ease maintenance, like `typedef int meters_t`, but more extensible. Compare `int foo_len` to `meters_t foo_len`. Explicitness: Consider the ambiguousness of `int foo_len` when you're swapping between meters and feet (but use `int` for both). Ease maintenance: If you want to change the type you use to represent meters (say from `int` to `size_t`, since it might be wise to make it unsigned), compare going through the code and changing each meter-related instance of `int` vs. just changing the `meters_t` struct declaration. More extensible: Compared to `typedef int meters_t`, using a struct is useful if you ever have to add to the struct. (Maybe a second numeric member representing the conversion to some other unit of length or something.) For "meters", this doesn't really apply, but using a struct also prevents you from accidentally trying to do math with numeric types that you shouldn't do math with (like ID numbers or something): https://stackoverflow.com/a/18876104/9959012 https://stackoverflow.com/a/18876104/9959012 Also, you probably shouldn't use `_t` at the end since that should be reserved by the C standard and by POSIX: https://stackoverflow.com/a/231807/9959012 https://stackoverflow.com/a/231807/9959012
- hot_gril 4y agoWhere I work, the C++ style guide explicitly says not to alias int types like this cause it becomes annoying for others to keep looking up what each thing really is, and probably YAGNI. I agree with that recommendation. Any int variable storing meters is probably called "meters" already.
- lionkor 4y agoto avoid implicit conversion - you can implicitly convert between meters and ft if its like this: typedef int feet; typedef int meters; this sucks, because they are the same to the compiler. Making them structs makes them unique types, and you can no longer pass a feet value to a meters parameter, etc. Its incredibly useful
- andrewmcwatters 4y ago> C++ is also not a ‘replacement’ or a ‘successor’ to C, it’s a fork which slowly ‘devolved’ its C subset into a slightly different dialect of C without much hope that the two languages can ever be united again (which IMHO is a damn shame, because being able to mix C with a sane subset of C++ would be really useful for writing libraries). Isn't this just... wrong? I mean I write in a common subset of C and C++, starting from ANSI C or C99 if required, and then when I am later required to use C++ features, I try to isolate those portions, and expose them through C interfaces. Maybe the author meant something else, and I am misreading this.
- apatheticonion 4y agoLearning C after working on high level languages I really like the simplicity. Though, I wish there was a C that had a few features from high level languages - or maybe an enhanced C that would compile to C. Things like ECMAScript style modules, structurally evaluated type aliases to produce contract types, Go-like syntax sugar for associating functions with a struct without introducing classes, compile time type parameters (generics), and only have .c files - eliminating header files.
- hgs3 4y ago> Now you have a named struct bla_t which is typedef’ed to a type alias bla_t, and the named struct can be properly forward-declared. The article doesn't mention it, but C11 allows typedef redefinitions which means instead of forward declaring "struct bla_t" and referring to it with the struct keyword, you can instead forward declare it as "typedef struct bla_t bla_t" and refer to it _without_ the struct keyword. See N1360 [1] for details. [1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1360.htm https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1360.htm
- Gibbon1 4y agoReminds me I found something, perhaps it's yet another gcc thing but. You can define an opaque pointer in a header file thing_pubic.h typedef struct foo_s foo_t; // foo_t is opaque. And the actual def in a header file thing_private.h typedef struct foo_s { int foo; } foo_t; // def of foo_t If you include the first header file you get foo_t as an opaque type. If you include the second or _both_ headers you get the full type.
- Night_Thastus 4y agoRegarding "enable all warnings" Wall and Weverything aren't even close to all warnings. Not even a good percentage of them. They're useful, but many other warnings exist that can be very helpful.