Here's what I do for my massively nested switch statements in my Intel x86-64 disassembler (which has five or six nested levels of switch statements)
1. Each nested switch goes in a separate function. A case statement in the first switch calls a function with another switch in it (if necessary - not all instructions nest all the way down).
2. The Intel instructions have this really tedious-to-enter prefix pattern for the SSE/AVX instructions:
The instruction's 66, F3, and F2 prefix(es) can be combined to change the instruction mnemonic entirely (in addition to the typical three opcode bytes).
Due to how the Intel manual's opcode map is laid out (the 66/F3/F2 prefixes are arranged in rows), in the 0F38xx and 0F3Axx switch tables, I do this (this is technically C# but the syntax is nearly identical):
Instead of switch once on the opcodeByte3 and then switch again on the SimdType, making one big 256-entry switch followed by 256x 4-entry switch functions (which would have no longer lined up nicely with the Intel manual), I rearrange the bits. I still switch twice, but the adjusted arrangement is more readable.
opcodeByte3 = reader.ReadByte();
// Encode the switch to make cases line up more exactly with the manual.
int row = opcodeByte3 & 0xF0;
case SimdType.None: break;
case SimdType.S_66: row += 1; break;
case SimdType.S_F3: row += 2; break;
case SimdType.S_F2: row += 3; break;
case SimdType.F266: row += 4; break;
// This switch ignores the low 3 bits in order to map directly to rows.
// Case statements are be the leftmost column of each page.
case 0x00: return FindOpcode0F38_Row0_None();
case 0x01: return FindOpcode0F38_Row0_66();
case 0x10: return FindOpcode0F38_Row1_None();
case 0x11: return FindOpcode0F38_Row1_66();
case 0x21: return FindOpcode0F38_Row2_66();
case 0x31: return FindOpcode0F38_Row3_66();
case 0x41: return FindOpcode0F38_Row4_66();
case 0x51: return FindOpcode0F38_Row5_66();
case 0x71: return FindOpcode0F38_Row7_66();
case 0x81: return FindOpcode0F38_Row8_66();
case 0x91: return FindOpcode0F38_Row9_66();
case 0xA1: return FindOpcode0F38_RowA_66();
case 0xB1: return FindOpcode0F38_RowB_66();
case 0xD1: return FindOpcode0F38_RowD_66();
case 0xF0: return FindOpcode0F38_RowF_None();
case 0xF1: return FindOpcode0F38_RowF_66();
case 0xF2: return FindOpcode0F38_RowF_F3();
case 0xF3: return FindOpcode0F38_RowF_F2();
case 0xF4: return FindOpcode0F38_RowF_66F2();
Each individual row function looks like this:
private bool FindOpcode0F38_Row0_None()
case 0x00: return Valid(Opcode.PSHUFB, Pq, Qq);
case 0x01: return Valid(Opcode.PHADDW, Pq, Qq);
case 0x02: return Valid(Opcode.PHADDD, Pq, Qq);
case 0x03: return Valid(Opcode.PHADDSW, Pq, Qq);
case 0x04: return Valid(Opcode.PMADDUBSW, Pq, Qq);
case 0x05: return Valid(Opcode.PHSUBW, Pq, Qq);
case 0x06: return Valid(Opcode.PHSUBD, Pq, Qq);
case 0x07: return Valid(Opcode.PHSUBSW, Pq, Qq);
case 0x08: return ValidSimd(Opcode.PSIGNB, Pq, Qq);
case 0x09: return ValidSimd(Opcode.PSIGNW, Pq, Qq);
case 0x0A: return ValidSimd(Opcode.PSIGND, Pq, Qq);
case 0x0B: return ValidSimd(Opcode.PMULHRSW, Pq, Qq);
private bool FindOpcode0F38_Row0_66()
case 0x00: return ValidSV13(Opcode.VPSHUFB, Vx, Hx, Wx);
case 0x01: return ValidSV13(Opcode.VPHADDW, Vx, Hx, Wx);
case 0x02: return ValidSV13(Opcode.VPHADDD, Vx, Hx, Wx);
case 0x03: return ValidSV13(Opcode.VPHADDSW, Vx, Hx, Wx);
case 0x04: return ValidSV13(Opcode.VPMADDUBSW, Vx, Hx, Wx);
case 0x05: return ValidSV13(Opcode.VPHSUBW, Vx, Hx, Wx);
case 0x06: return ValidSV13(Opcode.VPHSUBD, Vx, Hx, Wx);
case 0x07: return ValidSV13(Opcode.VPHSUBSW, Vx, Hx, Wx);
case 0x08: return ValidSV13(Opcode.VPSIGNB, Vx, Hx, Wx);
case 0x09: return ValidSV13(Opcode.VPSIGNW, Vx, Hx, Wx);
case 0x0A: return ValidSV13(Opcode.VPSIGND, Vx, Hx, Wx);
case 0x0B: return ValidSV13(Opcode.VPMULHRSW, Vx, Hx, Wx);
case 0x0C: return ValidVexOnly(Opcode.VPERMILPS, Vx, Hx, Wx);
case 0x0D: return ValidVexOnly(Opcode.VPERMILPD, Vx, Hx, Wx);
case 0x0E: return ValidVexOnly(Opcode.VTESTPS, Vx, Wx);
case 0x0F: return ValidVexOnly(Opcode.VTESTPD, Vx, Wx);
So when talking about copy-on-write, and how applications share the dll as read memory, and create a new page of memory when writing to the dll, how does that translate to the actual content of the dll? Is that like separating logic flow from variables?
DLLs contain the code, and a small subset of the variables that the program will actually use. Most variables are created on the stack, or on the heap. Both stack and heap are created dynamically and are not shared between processes. The operating system gives new processes their initial stack when they start, and then the code inside the program is free to request a heap and more stacks any time it wants.
The variables inside a DLL are globals (static variables) and read-only globals.
I guess to clarify, what would be a real example of an action inside an application that would trigger the OS to copy and create a new page of memory in the DLL?
The way copy-on-write works is the OS marks certain pages (the typically-4096-byte-chunks I mentioned in my previous post) with special flags using the VirtualProtect function. The flags can be set to cause exceptions to fire whenever the process read/write/executes from memory within that page. For copy-on-write, only write accesses cause the exception. Read and Execute work normally without any changes, since those are safe to do on shared memory.
The exception handler can then handle this by creating a copy of that page BEFORE the write actually happens, change the page protection on the new copy, and then allowing the write to continue where it left off, which now accesses the new copy instead.
Quick Summary: Any attempted write operation (of any kind) to a shared page triggers copy-on-write for that page.
This means that even if the DLL doesn't need to be relocated, if it has any global variables that get modified, the page containing that variable will be copied the moment the program first writes to it.
This stuff about triggering an exception when a page is accessed is built into the processor itself, and the OS handles it. If you're interested, you can search around for "x86 protected mode" and "x86 virtual memory" for even lower-level details (specifically the "descriptor table" stuff). Other processor architectures often have similar support that works in kind of the same ways.
Fun tangental note: The same page access exception stuff is used to detect and handle stack overflows!
DLLs are created with a default base address. Every process that uses a DLL will try to load the DLL within its own address space at the default virtual address for the DLL. If multiple applications can load a DLL at its default virtual address, they can share the same physical pages for the DLL. If for some reason a process cannot load the DLL at the default address, it loads the DLL elsewhere. Copy-on-write protection forces some of the DLL's pages to be copied into different physical pages for this process, because the fixes for jump instructions are written within the DLL's pages, and they will be different for this process. If the code section contains many references to the data section, this can cause the entire code section to be copied to new physical pages.
Gory details: At compile time, DLLs and EXEs have some pointers which point to other areas within the EXE/DLL. These pointers are set as if the EXE/DLL were located at a specific position in memory (in the above MSDN quote, this is the "virtual default address" they mention).
Now, since EXEs and DLLs are all compiled separately, none of them know what the others' default virtual address is. So it's possible that when you go to start your process and load all your DLLs, some will have conflicting defaults (meaning, if it just loaded them all, some would overlap in memory). When this happens, it's first-come-first-serve, and DLLs that load later need to be put in a DIFFERENT position in memory instead. This requires "relocating" the DLL - all of those pointers that were created inside the DLL at compile time have to be updated to point to where the DLL is ACTUALLY loaded.
Since this involves updating those pointers, it can no longer be shared with different processes. So it makes a fresh copy of the DLL in that one process's memory so that it can relocate it without affecting any other processes.
TL;DR: DLL physical pages can be shared unless they are relocated, in which case a new set of physical pages is created for that case.
Scratch that, I was getting caught in an infinite loop because I was starting at the begin() of list instead off end(), so I would increment forward and then push back thus getting stuck if I'm correct. Now i start at the end() and -- and get my output of added x and y to my list.
Good, you found the infinite loop problem yourself!
Personally, I would not add new vertices to the vector that you're reading from. I would write results to a second vector instead, so that I could preserve the original vertices.
It still has problems. I don't think I should provide any more hints - you should be able to solve this, but the code you're writing indicates that you don't understand some pretty fundamental things about the programming and debugging process.
Read the code that you have written, line by line, think about what it does. Think about what is going into each variable, and how it's being used. ESPECIALLY consider how each line of code will affect the OTHER lines of code that are going to execute later.
Compare this with what you think it should be doing. If you don't know what it should be doing, that's what you need to figure out first. Just copy/pasting code in different arrangements and saying it doesn't work is not how programming works.
You should step through it line-by-line in a debugger, watch what it's doing, and see where it isn't doing what you expect it to do.
Learning a new language can be fun; occasionally you will find something neat that gives you a new idea of how you can solve a problem in other languages you already know.
As far as good/bad practices go, it's hard to know at first. Eventually you'll start to get some insight about what's good and what's bad after using different things yourself, and participating in discussions about particular ways of doing things. For now I suggest to just keep doing whatever works, and if you start running into headaches, try to find out if you're doing something wrong at that point. Usually it's not too hard to go back and fix mistakes. Most of our programming time is spent trying to decide how to do something in the first place. It's pretty easy to take a solved problem and implement it differently.
Sometimes when something feels hard to use, it's because the way it is meant to be used is different from how you're trying to use it (if you see people say "idiomatic", that basically means "the way we usually do it in this language/library"). I usually try looking for complex samples that demonstrate how things are supposed to be used. This kind of thing bit me hard when moving from WinForms to WPF.
I haven't used SFML. Last time I used allegro was during the DOS era, so I'm not sure what it's up to lately.
Don't clear your list between frames. Remove keys when you get the WM_KEYUP or when your application loses focus. Ignore redundant WM_KEYDOWN if your list already contains that key (you can use an flag array rather than a literal "list" that you add/remove from). Keyboard auto-repeat will only ever repeat the last key you started holding down.