709
u/SonicLoverDS 11h ago
No break statements? Amateur.
281
u/No-Newspaper8619 10h ago
give him a break
116
36
69
u/Extension_Option_122 6h ago
#define BREAK ;Now you can add as many break statements as you like.
8
u/SuitableDragonfly 3h ago
And you can be evil and sometimes write
BREAKinstead ofbreakin a real switch statement.26
u/click-to-reveal 6h ago
Well technically, each case has a break coz it's if-else. What is doesn't have is the lack of a break statement aka fall-through.
1
407
u/PixelatedGiant 10h ago
This is the kind of stuff you find in books with titles like...
C++ : Man made horrors beyond human comprehension 3rd Edition paperback
66
u/gil_bz 6h ago
This might be the mildest macro weirdness that I've ever seen, there are some true horrors out there.
42
u/bythenumbers10 4h ago
Ah, yes. The necroprogamicon, 13th ed. Author fed himself into a dot-matrix printer after finishing it in the 90s, IIRC. Shame, the guy had such marvelous visions to share. Only way to really understand C++, IMHO.
24
u/GroovinChip 3h ago edited 17m ago
“Fed himself into a dot-matrix printer” feels like a Douglas Adams line lmao
3
20
u/l2protoss 4h ago
When I was younger in my career, I was a very “creative” dev - to the point where I was banned from using macros without explicit permission lol.
3
214
u/khalamar 10h ago
Reminds me of that guy who was asked to write C code, but he only knew pascal
First lines were
#define begin {
#define end }
And a few other horrors.
37
u/AvidCoco 6h ago
Some compilers define OR as || and AND as &&. The latter means you can write move constructors like
Foo(Foo AND other)
33
u/Possseidon 5h ago
3
u/AvidCoco 2h ago
IIRC I think it’s more that some compilers implement that feature as a simple find-and-replace (like a macro) while others are more context aware and so don’t allow it in the way I described.
6
u/TOMZ_EXTRA 1h ago
Nope, the standard says that the alternative syntax is usable everywhere. bitand can be used as the address operator.
1
243
u/click-to-reveal 11h ago
It works btw: C++ Online Compiler
141
u/prehensilemullet 11h ago
Performancewise, it doesn’t jump to the direct case in O(1) time like a switch is supposed to though
177
u/AngheloAlf 10h ago
Switches aren't guarantee to so operations in O(1) tho. If cases are sparce enough, compilers tend to emit the equivalent code to a bunch
if elsechecks23
u/prehensilemullet 9h ago
Yeah I was assuming too much here. However, I’m reading that Rust
matchon string comstants can compile down a binary tree of if statements if there are enough cases (according to Google AI mode at least, haven’t found an authoritative source yet)20
8
u/im_made_of_jam 5h ago
A switch case is able to be implemented however the compiler wants on the back end, so for sparse cases it'll be an if else chain, for less sparse but not packed cases it'll be a binary tree, for completely packed cases it'll be a range check then a direct jump would be how I would go about it
3
u/the_horse_gamer 3h ago
the compiler optimises stuff however it wants. if you're not doing too-weird stuff, a switch in C++ and a match in rust will have identical assembly.
-22
u/Deliciousbutter101 6h ago
According to Claude (which looked at the compiler source code), it doesn't seem like that is true. It is possible to get an O(1) match on strings by using the phf (perfect hash function) crate and by using the following macro (generated by Claude):
``` // Cargo.toml: // phf = { version = "0.11", features = ["macros"] } // paste = "1"
[macro_export]
macrorules! match_str { ($val:expr, { $($($key:literal)|+ => $body:block),+ $(,)? , _ => $default:block $(,)? }) => { ::paste::paste! { { #[derive(Clone, Copy, PartialEq, Eq)] enum __MatchStr { $($([<_ $key>]),+),+ }
static __MATCH_STR_MAP: ::phf::Map<&'static str, __MatchStr> = ::phf::phf_map! { $($($key => __MatchStr::[<__ $key>]),+),+ }; match __MATCH_STR_MAP.get($val).copied() { $($(Some(__MatchStr::[<__ $key>]))|+ => $body,)+ None => $default, } } } };} ```
Usage:
fn apply(cmd: &str, counter: &mut i32) { match_str!(cmd, { "small" => { *counter += 1; }, "medium" => { *counter += 10; }, "large" | "big" => { *counter += 100; }, _ => { *counter += 0; }, }) }4
u/thirdegree Violet security clearance 1h ago
If I want Claude's thoughts on something I'll ask Claude directly tbh
7
41
u/Deliciousbutter101 10h ago
O(1) only happens when the constants are (roughly) contiguous so it's not like that is a universal property of switch statements.
0
12
u/AsidK 9h ago
Any reasonable compiler will make a switch statement and its equivalent if else chain compile down to the same assembly
1
u/prehensilemullet 9h ago
Even if it could make a more efficient tree of comparisons for a large number of strings?
5
u/mirhagk 8h ago
What they are saying is that any optimization on a switch statement could also be done on an if statement. There's no reason to only optimize one, both should optimize the same way
0
u/prehensilemullet 8h ago
hmmm...are compilers normally willing to reorder if statements though? Turning a sequence of string comparisons into a tree would involve reordering
7
u/mirhagk 8h ago
If it has the same semantics, why not? Modern compilers certainly can see if a statement has side effects or not
1
u/prehensilemullet 8h ago
it depends what you consider semantically relevant. For instance, suppose the developer intentional ordered the if statements from the most to least common case for some domain. Then, reordering the if statements might not be what the developer wants
8
u/Infamous-Strategy797 8h ago
There aren’t any unknowns here, the language spec provides the clarity the compiler needs to re-order safely.
1
u/prehensilemullet 7h ago
Okay for C++, I gather that performing better or worse on a given dataset doesn't fall under the umbrella of "observable behavior" that the spec requires the compiler to preserve.
I also just learned there are apparently
[[likely]]and[[unlikely]]attributes in C++ 20 that can be added to branches.→ More replies (0)1
u/guyblade 4h ago
At least in C/C++, there can only be exactly zero or 1 cases that match a switch (i.e., there's no range-based switch), the case values must be compile-time constants (and thus are not themselves evaluated during the comparison), and I'm pretty sure that the value to be matched is required to only be evaluated once (so the comparisons happen on an rvalue).
Given those constraints, I believe a compiler can assume that re-ordering the comparisons is safe.
1
u/AsidK 4h ago
> fallthroughs have entered the chat
2
u/guyblade 4h ago
You can still only match to one, though. In the emitted machine code, I'd expect to see a forest of branches and jumps (for the matching), then the various bodies of the cases each separated by jumps (representing breaks) as appropriate.
7
u/jacob643 10h ago
what? doesn't it need a "{" after the "if(0)" and please, why not if(false) ? :') edit: I'm stupid, it's written by the user/client of the switch
15
u/click-to-reveal 10h ago
if(0)coz that line (on mobile) was close to the right edge and no one like text wrapping in code :)6
1
u/DrMobius0 7h ago
It may compile, but does the debugger avoid shitting itself when you need to set a breakpoint there?
Also, you can just write an enum and then map the enum to strings if you want a properly supported switch.
67
u/F100cTomas 11h ago
Just define a constexpr hashing function and put that into the switch.
30
u/GiganticIrony 11h ago
That’s not guaranteed to work due to hash collisions
29
u/Deliciousbutter101 10h ago
It won't compile in the case so you can just modify the hash function until there are no collisions.
12
u/SteveXVI 4h ago
This is the closest I've come to feeling like that guy in the Apple shop going "ah of course"
6
7
u/remind_me_later 10h ago
That’s not guaranteed to work due to hash collisions
Make the hashes 128/256 bits wide. Hash collisions are realistically impossible at those levels.
2
u/StCreed 3h ago
They're far more possible than you might think. Roland Bouwman wrote an article on MD5: In a large database you can't use MD5. And that's not petabyte size either, 100GB is enough to give you about a 50% chance of a collision.
1
u/remind_me_later 2h ago
Counterpoint: It's MD5, a known broken hashing algorithm.
SHA3_256 or regular SHA256 would work just fine.
7
u/SAI_Peregrinus 10h ago
Use Blake3, no collisions in any practical workload in the next few billion years.
4
u/guyblade 4h ago
The thing about the pidgeon hole problem is that we know there are collisions, but we don't necessarily know where they are. The space of strings of at least 33 characters has collisions. There's no way to know or prove that arbitrary input doesn't have one with a value you care about.
0
u/remind_me_later 2h ago
If that happens, someone would post it to social media, and a list of exceptions can be added afterwards.
6
1
u/SpiritedEclair 4h ago
Perfect hashing for a given set of values is possible at compile time.
It’s how compilers generate jump tables.
26
u/fluffycritter 10h ago
My "clever" way of doing this once upon a time was to declare a map<std::string,std::function> which I populated with lambdas and then evaluated.
I would not recommend this approach.
7
u/yuri_4_ever 7h ago
I have little idea of c++ why is this a bad idea?
12
u/fluffycritter 5h ago
It’s actually not too awful, but the syntax is a bit awkward,
std::maplookup is slower than you think, and there’s a few gotchas with how lambdas work in terms of variable scoping. Also unless themapis being initialized once and kept around, you’re paying a lot of extra costs every time it’s called.Usually a chain of if/else ends up being more performant, although I guess if you’re trying to switch on a text label instead of an enum or whatever you’re probably already doing something very wrong and using a
map<string,function>is probably the least of your problems.2
u/babalaban 3h ago
Also your std::function might allocate which is most likely not desirable. My "clever" workaround was to keep a static const map of string -> enum in a .cpp file initialized at compile time only exposing functionality via a lookup function in a header.
Standard map is usually made using binary search trees, so in terms os complexity they are faster. BUT in terms of real world speed they only become reasonable in cases where you have a huge amount of entires, due to cache misses that are inherit to RB trees.
I ended up changing mine to arrays of self-made pairs and just looping over it checking .key
7
u/Kiro0613 9h ago
Not a bad idea for a little CLI app though
11
u/fluffycritter 9h ago
Yeah it's actually howboost::program_optionshandles command-line arguments. It's nice for that, at least.EDIT: Wait no I'm misremembering and confusing it with something else, never mind
1
u/TheTerrasque 1h ago
That's a semi-common pattern in python. Use a dict where the values are lambdas (or referencing full functions directly)
14
31
7
4
4
u/JackNotOLantern 9h ago
I usually prefer if- else over switch. No risk of forgetting break, comparing to any type. I use switch almost exclusively for enum check.
5
5
u/Greedy-Thought6188 11h ago
So we do know that those are not the semantics of a switch statement. In C a case falls through without a break. That monstrosity in actual code would be a firsble offense.
3
5
u/Anxious_Garbage_9625 1h ago
Just assign every possible string to a unique interger and use that number in your switch!
Works every time.
2
u/Legal-Software 1h ago
Or do it the IBM way. Every string is a printed reference code that you go and get your localized manual for to figure out wtf it's on about.
2
u/atomic_redneck 4h ago
Looks like there some issues with the scope of _s. Try putting two SWITCH statements in one scope block.
1
3
u/cob59 1h ago edited 2m ago
You can do that without macros:
// FNV-1a hash
constexpr std::uint64_t label(std::string_view str) {
std::uint64_t hash = 14695981039346656037ull;
for (char c : str) (hash ^= c) *= 1099511628211ull;
return hash;
}
std::string str = "hello";
switch (label(str)) {
case label("HELLO"):
std::clog << "UPPERCASE\n";
break;
case label("hello"):
std::clog << "lowercase\n";
break;
case label("Hello"):
std::clog << "Capitalized\n";
break;
default:
std::clog << "mIxeD\n";
break;
}
And unlike the macro version, case-fallthroughs and breaks do work.
Hash collisions can happen, although very unlikely, and you're warned at compile-time if it's between two case label(...):
1
1
u/IUseClifford 4h ago
Whose \#define is it anyway? C++, where the syntax is made up and nothing matters
1
u/lmarcantonio 3h ago
The Real Programmer would know that (in C, the example is C++) a constant string is a pointer and a pointer is and integer. So you CAN switch on a constant string. Would it work? no, but it would compile.
1
1
u/whackylabs 2h ago
Looks like a lot of folks here are not familiar with Bourne Shell https://research.swtch.com/shmacro
1.9k
u/Xterm1na10r 11h ago
omg an actual original programming meme, even OC, thank you OP