Nah those are easy to name, just annoying, it's types which literally don't have an external / public name, like lambdas, or locally defined types.
#include <iostream>
auto createVoldemortType(int value) {
struct Voldemort {
int value;
};
return Voldemort{value};
}
int main() {
auto voldemort = createVoldemortType(7);
std::cout << voldemort.value << std::endl; // output: 7
}For many compilers, as soon as the parser sees a left curly brace, it pushes a symbol table onto a stack, and when it sees the corresponding right curly brace, it pops the symbol table off the stack, and "forgets" any declarations that were made inside that scope. That is so things like this work as expected.
{
int x = 0;
{
int x = 1;
printf("%d\n", x); // prints 1
}
printf("%d\n", x); // prints 0
}
But, in C++, using the auto keyword, declarations can escape their scope with auto. I'll change the C++ code that OP wrote. The C++ compiler has to correctly resolve cases like this, which means it can't just forget all the declarations within the scope of the function after the definition is done. #include <iostream>
auto createVoldemortType(int value) {
struct Voldemort {
int value;
};
return Voldemort{value};
}
struct Voldemort {
std::string value;
};
int main() {
auto voldemort = createVoldemortType(7);
std::cout << voldemort.value << std::endl; // output: 7
}At least that’s how I think the parsers work I‘m familiar with.
I've read a book about a BLISS compiler [1] that does this, but still uses a stack like I described [2]. It implements a hash table that used linked list nodes for collision. A new declaration adds a new name to the table, and it attaches the node to uses of the name in expressions of the syntax tree.
When a scope is exited, the declarations from that scope are removed from the symbol table, but because they're still attached to the syntax tree, they can't just be freed. They're added to a linked list of "purged" nodes, so that the information they contain can be used later during code generation, and then freed.
One-pass compilers don't have this problem; they really can just free the memory for reuse, because after they exit a scope, they've already generated the assembly or machine code from the high-level language.
However, I don't know what LLVM or GCC, or any other remotely modern compiler, does. I haven't read the code much.
[1]: https://en.wikipedia.org/wiki/The_Design_of_an_Optimizing_Co...
[2]: Actually, it intertwines the stack and the symbol table in a complicated way, so there's only one hash table, and multiple stacks within it. It's explained by a diagram they include on page 13. You can find a PDF of it here: https://kilthub.cmu.edu/articles/journal_contribution/The_de...
I don't see any code ther that does that. Is it implicitly passed?
I don't know C++, though I did know C somewhat well earlier.
return Voldemort{value};
I guess that does it.
Ha ha, that reminds me of that phrase of yore, "the quality without a name (qwan)" (google it), which was heavily bandied about years ago, during the heyday of C++ and the software patterns movement (which continued a lot in the days of Java, of course). James Coplien (IIRC) and others of that time come to mind.
https://en.m.wikipedia.org/wiki/The_Timeless_Way_of_Building
https://en.m.wikipedia.org/wiki/Pattern_language
https://en.m.wikipedia.org/wiki/Jim_Coplien
Though I read a fair amount about that stuff, a lot of of it went over my head, but later, I did understand some of the patterns, after reading the design patterns book, and trying out some of them.
The template method pattern is my favourite pattern, because I understand it more well than many of the others :), and also because it is the basis of software frameworks (inversion of control, aka the Hollywood principle - "don't call me, I'll call you"). Other patterns that I like and understand are the command pattern, the interpreter pattern, the chain of responsibility pattern, and flyweight pattern, to name a few. Builder and Factory, not so much. Singleton is straightforward, or is it really? impls matter :)
And I have written a few toy frameworks, which is fun to do and use.
I agree that you don't need auto pointers in C the way you do in C++. C++ type names can get so cumbersome...much easier to let the compiler figure it out for you.
#define SWAP(var1, var2) do { \
auto tmp = var1; \
var1 = var2; \
var2 = tmp; \
} while (0)
Previously, you would have needed a third macro for the type, or you would have needed to do a byteswap to be generic. const typeof(var1) tmp = var1;Consider this:
if (e)
SWAP(a, b);
else
something_else();
Currently, that expands to this, which is still valid code: if (e)
do {
// ...macro...
} while(0);
else
something_else();
If it were just wrapped in braces, the code would be parsed like this: // One-armed if-statement
if (e) {
// ...macro...
}
// Empty statement
;
// Another statement
else something_else();
It's incorrect syntax to start a statement with else, so the compiler will say something like "Unexpected 'else' at line N.""What's the best way to write a multi-statement macro?"
SWAP(…);
I think they obfuscate things unnecessarily. In C++ it's understandable because with templates you have a tendency to have massively complicated type names.
Everything else looks great to me though.