34 years? I have 10 years at a couple of FAANGs, and got $3M in stock, with maxed out 401k, etc. I am having thoughts about retiring early, maybe in 5 years. Long time Apple employees could definitely retire after 10 years. He most likely stayed there because he liked the job.
In stores yes, but on the Internet, including it in the price makes it easier to bump up prices. Showing the price without tariff allows you to easily compare before / after, and then when you see tariff added to your bottom line order (e.g. on Amazon) it should drive home the point that tariffs are a tax paid for by the consumers (which unfortunately lots of people still dont believe).
I see two items for $5, but when I add the imported one, suddenly it costs more — and Amazon didn’t tell me that ahead of time or give me any way to choose the one without tariffs on the grid/list view.
This makes tariffs more effective because they can’t bump the domestic price to match — while giving customers a negative chock each time they choose an importer for a product.
Right, but they'll underprice just below the floor price for the imported good, because why would businesses leave money on the table?
The choice for consumers won't be "choose between a $5 item and $15 item" it will be "choose between $13 and $15", like I mentioned above.
This doesn't work as easily if the sticker price for the imported good is $5 and the real price displayed at the end of the purchasing funnel. The local business will have to keep its sticker price at $5 to avoid losing customers when they initially compare goods or rely on customers to come back to them once they get faced with the tariff tax, which will also lose customers.
Sorry for the lazy question, but would you be able to share some links or pointers to where these guys are active? I've been out of the loop for a few decades but enjoyed the scene when I was a teenager.
As someone who has a file with similar hacks, I will say this: I am not a C++ fan, but if you find yourself writing C code where you simulate methods via structs with function pointers often, just use C++ as a basic "C with classes" at that point. You want methods anyway, you have to go through a pointer dereference to call the function, it's just not worth the code weirdness. If you have the grit to use structs with function pointers everywhere, you have the grit to stick to the simpler subset of C++.
I'm torn. The step from C to any c++ is big. Now if you want anybody to be able to use your code they need to be using c++ or you have to provide a C api anyway. On the other hand, manually implementing vtables is annoying. Ive been sticking to pure C and haven't been bothered enough to go back to any c++ yet (about 6 months on my current project). I mostly only miss templated containers so far.
It is more annoying to want to implement an optional function in a class and then have no simple way to check if that optional function is implemented in the object without, having to edit code that guards the call sites every time you add a derived class that implements it, or having to implement your own way of querying the object to know if it is supported.
Well, it would not be a class in that case, but a use case would be anything that is modular where you want to support a new function, but do not require all of your implementors to use it. The best example of this is the VFS. Here is documentation for the Linux VFS:
The vast majority of the function pointers in those structures are optional (even if not explicitly stated). To give a few common sense examples:
* If your filesystem does not support extended attributes, you would not implement .listxattr and instead set it to NULL.
* There are multiple ways of implementing read and write in file_operations. You have the basic read and write operations, and more efficient variants. You don’t need to implement the more efficient variants if you don’t want to implement them.
* The .bmap call is used to find out how the filesystem stores a file on a block device, which used to be used by the syslinux (and might still be). This obviously is incompatible with NFS (or any multidisk filesystem like ZFS) so it absolutely must be optional.
Then there are other options, like not supporting mmap, or not supporting creation/removal of subdirectories. That sounds absurd, but some FUSE filesystems, particularly those exporting a program’s statistics, don’t bother with either of those since they are not needed. I do not believe Linux sysfs allows users to make directories either.
I could continue, but this gives a few examples of why you might want to have optional functionality in a class-like interface.
By the way, I mentioned setting things you do not implement to NULL. This is done simply by not specifying them when using the structure initializer syntax. The compiler will zero unspecified members.
The point of the throwaway account’s comment was to say that you should use C++ class member functions instead of C function pointers in structures, but that is impossible to do in the general case in a sane way, since there is no way to leave a member function unimplemented and then check its status at runtime.
You need to use hacks to shoehorn C++ class member functions into this. In particular, you need stub functions. Then either, call them and have them either return a special error code or throw an exception, or use a custom query function that is implemented by derived classes that lets you find out if a function is a stub or not to allow you to skip calling it. Another idea would be to use thread local storage with setjmp()/longjmp(), which is probably the sanest way of doing this insane idea:
The idea that the simplest way of approximating what you can do with function pointers in C structures via C++ class member functions is to use TLS and setjmp/longjmp shows what a bad idea it is to use class member functions instead of function pointers for optional functions in the first place.
C++ was designed as "Typescript for C" for its time, because sometimes that is exactly the kind of code one needs to write, even if we discourage many of the classical patterns when better alternatives exist.
As for possible alternatives, here is a basic one without taking into consideration virtual mechanics, only to show the principles.
#include <concepts>
template <class T>
concept has_mmap = requires (T obj)
{
{ obj.mmap() } -> std::convertible_to<int>;
};
class VFS {
public:
VFS() = default;
virtual ~VFS() = default;
};
class ExampleFS : public VFS {
// mmap not available
};
class ExampleWithMMAP : public VFS {
public:
int mmap() {
return 0;
}
};
int main() {
ExampleFS fs;
ExampleWithMMAP fsWithMMAP;
/*
<source>: In function 'int main()':
<source>:33:19: error: 'class ExampleFS' has no member named 'mmap'
40 | return fs.mmap();
|
*/
if constexpr (has_mmap<ExampleFS>) {
return fs.mmap();
}
// ExampleWithMMAP has mmap(), just call it without issues
if constexpr (has_mmap<ExampleWithMMAP>) {
return fsWithMMAP.mmap();
}
// want to use the variable name instead of the type?
if constexpr (has_mmap<decltype(fsWithMMAP)>) {
return fsWithMMAP.mmap();
}
}
The same C example compiled in C++23 mode, https://godbolt.org/z/MWa7qqrK7
Everyone knows this. The original comment was saying not to do this (even in C++) and use C++ classes instead. I was making the point that is a bad idea. You seem to have not understood that.
1. It is not possible to add optional member functions (which would be pure virtual functions) to a C++ class base class and then check at runtime if they are unimplemented in the object (at least not without implementing some way to query the object, which is slow). If you say to handle this by having typeid checks at runtime, look at the VFS and then notice that you cannot implement this typeid check in advance, since you cannot add a typeid check for a derived class that did not even exist when you compiled your code. Thus, you still need to use structs of function pointers in C++. Maybe you can use C++ classes for some cases where structs of function pointers are used, but you would giving up the ability to implement optional functions in a sane way.
2. It ignores all of the things in C that are absent from C++. In particular, C++ refuses to support C’s variably modified types and variable length arrays, which are useful language features.
3. It ignores all of the things in C++ that you likely do not want, such as exceptions and RTTI. The requirement to typecast whenever you assign a void pointer to any other pointer is also ridiculous.
1. There are other approaches to this with templates and concepts, and as added bonus, stronger type checking.
Thankfully regarding 2., Google went the extra mile to pay for removing them from the Linux kernel, and they were made optional C11 onwards exactly because they are an attack vector.
3. It is called stronger type safety, ridiculous is the C community still approaching computers as if writing K&R C.
Not everything. See the features that C++ refuses to support. Beyond that, there is no compiler flag to stop requiring explicit casts of void pointers before assigning them.
Furthermore, less is more. You get faster build times with C because it does not support all of the features C++ has. Just because you can do it in C++ does not mean you should.
I used C++ for one of my first projects for a startup in health care and I really wish I had not. C++ made development a hellish experience as I spent most of it on fighting the compiler to be able to use every C++ language feature I could imagine and not enough on actual issues. It easily doubled development time since I spent most of it on things that only existed because C++ had overcomplicated everything (e.g. reference versus pointer, public versus private, shoehorning OOP into places it did not belong, operator overloading, templates, etcetera). This was during my initial attempt at graduate studies and after ruining a semester because of it (this had been intended to be a part time thing), I parted ways with the company. The C++ daemon went on to be the heart of the company, despite the lingering bugs.
I ended up fixing the remaining issues as a consultant years later, but eventually, I realized that everything would have been better had I not used C++ in the first place. There are times when I fantasize about rewriting it in C. One of these days, I might actually do that for the company for free if only to put an end to a mistake of my youth. Unfortunately, now that I have fixed the daemon, it has the advantage of being a mature, reliable codebase, so it is difficult to justify a rewrite.
That said, despite my complaints about the effect C++ had on development, I did a number of things right when architecting that daemon. The lingering bugs turned out to be trivial and it has scaled with the company for 13 years with no end in sight. When it finally is replaced, the reason will likely be that it did not support HA, rather than some inability to scale. My younger self had refrained from pursuing HA since it seemed infeasible to do within the spare time I had during a single semester.
I was making a narrow comment that you can turn on VLAs, off exceptions, etc., with C++ flags on publicly available compilers regardless of what the C++ language specification and the C++ experts say.
The difference in build times between identical code compiled with the C language or C++ language is probably negligible. Or at least dwarfed by using a better build system, a faster build machine, and/or some sort of build caching technology.
> Beyond that, there is no compiler flag to stop requiring explicit casts of void pointers before assigning them.
I believe that's true. And there are probably a few other ergonomic differences beyond this one. Has anyone proposed that as a feature flag for Clang and/or GCC? Open source C and C++ compiler devs don't have a lot of free time such that they peruse social media looking for things to do.
No comment on your anecdote other than to say I have heard versions of that story before but with other programs and in basically every other language. Including C.
I'm not saying you're wrong. I think a lot of your points are valid points. About taste. Which is fair and fine, but it's also true that the difference between C and C-style C++ are pretty minor, especially if someone knows how to enforce coding standards with clang-query wired up to CI or something like that.
What is particularly nice about that code is that a C compiler will realize that it has a buffer overflow. Adapting it for C++ will cause the C++ compiler to not notice the buffer overflow.
If you are going to be writing C, there is no reason to compile it as C++. Using C++ limits your ability to use newer features of C and exposes you to headaches like the ABI compatibility break of GCC 5.0 that was done for C++11. C has never had an ABI compatibility break caused by a revision of the language. Your suggestion that people should use C++ even when it is not what anyone wants befuddles me.
If you said this in a room with Linus Torvalds, I wonder if he would start cursing again.
There's a big issue with C++ classes -- or rather methods. You need to list the entire class definition to expose public methods. That includes private members and private methods. This breaks encapsulation, exposing implementation details and their dependencies in the public header. This in turn leads to looong compile times and needless recompilations.
Perhaps he wants to jump through hoops to avoid function pointers, even when doing that in C++ for optional functions like is done in the VFS requires gymnastics:
That is the closest I can get it to implementing an optional function via a C++ class member function instead of a function pointer. It is not only insane, but also masochistic in comparison to how it would be done via function pointers:
#define hc_task_yield(task)
do {
task->state = __LINE__;
return;
case __LINE__:;
} while (0)
That's just diabolical. I would not have thought to write "case __LINE__". In the case of a macro, using __LINE__ twice expands to the same value where the macro is used, even if the macro has newlines. It makes sense, but TIL.
> no commonly used high level language supports the coroutine call primitive
Shows how old this post is. In fact I remember reading it well over 10 years ago, maybe more like 20. archive.org says that it's at least as old as 2001. A great article.
I'm very excited to see he's published a new article on C++20 coroutines. I've read (or maybe skimmed...) a few introductions and not really got them, despite having used C# and Python coroutines a lot with no problems (even making changes to an async runtime for Python). Given how clear his C coroutine article is, I'm optimistic about the C++ article.
> So, after the course, I went away and studied on my own, and wrote the introduction to C++ coroutines that I’d have liked to see.
I've written C on-and-off for over 30 years (just various throw-away prototypes and OS/app interaction microbenchmarks) and it took a while + a web search to get it. Diabolical indeed. Edit: And makes sense in hindsight.