Examples:
- Ben Deane - Using Types Effectively [0]
- Jason Turner - Rich Code for Tiny Computers: A Simple Commodore 64 Game in C++17 [1]
- Ben Deane & Jason Turner - constexpr ALL the Things! [2]
[0] https://www.youtube.com/watch?v=ojZbFIQSdl8
https://issues.dlang.org/show_bug.cgi?id=1578
It's a real shame because I prefer just about everything else about metaprogramming and compile-time evaluation in D over C++.
1) `semi::static_map` gives you the optional run-time look-up with the same API (`semi::static_map::get`). Looking up a global variable with a runtime key isn't straight-forward. This makes it particularly nice to use an API where a function might take either a compile-time or run-time key. I'm thinking a Font API with a `getFont` method, for example. The way you use the method is the same regardless if you are using a compile-time key or not.
2) `semi::static_map` also gives you control over the lifetime of the objects. For example, it's easy to delete all the values in the map at once. Again, this is not straight-forward to do with a bunch of globals.
3) the keys are also values themselves and it's straight-forward to evaluate them at run-time. Printing the name of a global variable, for example, requires all sorts of hackery.
4) you do not need to know all your keys in advance. Simply using getFont with a unique constexpr key will create a new global variable.
Edit: added point (4)
enum {
KEY_FOO,
KEY_BAR,
KEY_BLAH,
NUM_KEYS,
};
struct KeyInfo {
const char *name;
int info1;
float info2;
};
const static struct KeyInfo keyInfo[NUM_KEYS] = {
#define MAKE(x, y, z) [x] = { #x, y, z }
MAKE( KEY_FOO, 1, 1.0 ),
MAKE( KEY_BAR, 7, 3.5 ),
MAKE( KEY_BLAH, 42, 127.2 ),
#undef MAKE
};
void print_key(int key)
{
printf("%d's name is %s\n", key, keyInfo[key].name);
}
It's both low-tech and maintainable. It compiles super quick. If one doesn't like that one has to type KEY_FOO twice (in the enum and in the array definition), one can use X-macros or code generation. But personally I don't care.As stated in the talk, the main use case for a map like this is to cache objects which are expensive to load/compute (again something like `getFont` comes to mind). In these use-cases you would likely also need a check like this anyway: a global pointer object which you need to check if it's a nullptr or not before using it.
Interesting! Macro substitutions can expand to anything of course. I hadn't really considered adding lambda functions to that list.
The idea here is that finding the storage of the value is done at compile-time, but the values themselves are run-time values.
You could declare `static static_map<>::Value& get()` constexpr. (Actually you wouldn't need to; I think you could simply declare `semi::map<>::get()` to be constexpr) and I believe it would allow calling to look up a value to inlined at compile time.
However using it as an lvalue isn't possible with current c++ syntax. As a long time Lisp programmer this limitation continually trips me up.
Sorry about the backquotes; I know that HN doesn't use MD but it was the simplest way to denote code inline.
* no []= operator in custom maps
* no enum printing
* no backtrace
* other random desasters
At least they don't work without jumping through some hoops in all of your code. So for me c++ is about as fixable/appealing as Fortran.
https://stackoverflow.com/questions/3342726/c-print-out-enum...
https://stackoverflow.com/questions/691719/c-display-stack-t...
https://stackoverflow.com/questions/3581981/overloading-the-...