1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #define writeInt(x) _writeInt(#x,x) writeInt(mass); //and it will expand to: _writeInt("mass",mass); #define loadInt(x,json) x = _loadInt(#x,json) //expands to: mass = _loadInt("mass",json); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #define CLASS(p) class p\ {\ inline virtual std::string _GetClassName_() { return #p; } //which then could be used as CLASS(TestClass) void DoStuff(); //... }; //due to missing brace in code, it broke some VS functionalities and highlighting (which breaks on everything now, excuse me for highlighting). //I've got rid of this macro and added a factory (which is not related, see below), vector holding pair of string (class name) and class constructor. //but tbh, I still use simplified version of CLASS macro for ex. as: class BigDoor : Door { public: REGCLASS("BigDoor") // inline virtual std::string __GetClassName__() { return "BigDoor"; } //... }; // and so on.. |
1 2 3 4 5 | #define SCALL(x, c) if (x != nullptr) c //or #include <stdio.h> #define ASSERT(p,q) if(!p) { fprintf(stderr, "ERROR: \"" ## q ## "\" IN " __FILE__ " AT LINE %d!\n", __LINE__); *(int*)0=0; } |
Horrowind
Not too interesting, but funny:
1 #define private public
1 2 | #define class struct #define protected public |
1 2 3 | #define VAR_TYPE float // the type you want. #define VAR_NAME f32 // type name appended to the struct / function. #include "bound_array.cpp" |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // "main.cpp" #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <assert.h> #define CONCAT_(a,b) a##b #define CONCAT(a,b) CONCAT_(a,b) #define VAR_TYPE float #define VAR_NAME f32 #include "bound_array.cpp" #define VAR_TYPE int64_t #define VAR_NAME s64 #include "bound_array.cpp" int main() { Array_f32 eple = Array_f32_init(32); eple[31] = 91; printf("eple.size = %lu\neple[31] = %f\n", eple.size, eple[31]); Array_s64 pera = Array_s64_init(54); pera[24] = 3771; printf("pera.size = %lu\npera[24] = %li\n", pera.size, pera[24]); return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // "bound_array.cpp" #ifndef VAR_TYPE #error "VAR_TYPE is not defined." #endif #ifndef VAR_NAME #error "VAR_NAME is not defined." #endif #define ARRAY_TYPE CONCAT(Array_, VAR_NAME) struct ARRAY_TYPE{ uint64_t size; VAR_TYPE *element; VAR_TYPE &operator [](uint64_t index) { assert(index < size); return element[index]; } }; static ARRAY_TYPE CONCAT(ARRAY_TYPE, _init) (uint64_t size_in) { ARRAY_TYPE result; result.element = (VAR_TYPE *)malloc(size_in * sizeof(VAR_TYPE)); assert(result.element); result.size = size_in; return result; } #undef ARRAY_TYPE #undef VAR_TYPE #undef VAR_NAME |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | struct State{ int _pc_; // other variables that need to carry their value // over multiple resumes of the function call. }; #define DrYield(c,v) do{ state->_pc_ = c; return(v); resume_##c:; } while(false) #define DrReturn(v) do{ state->_pc_ = -1; return(v); } while(false) #define DrCase(c) case c: goto resume_##c void foo(State *s, void *new_memory, File_Data file){ switch (s->_pc_){ DrCase(1); // ... DrCase(5); } // ... code stuff ... for (begin();check();increment()){ if (need_memory_from_user()){ DrYield(1, NeedMemory); // do something with new_memory } if (need_file_from_user()){ DrYield(2, NeedFile); // do something with file } // ... code stuff ... } // ... code stuff ... DrReturn(state->success); } |
Mr4thDimention
My favorite macro trick is for making something that looks like a resumable function call, sort of like a really light weight coroutine. So you can write code that says "ahh at this point I would like to return to the user some info, but then when they call me I want to resume right after the return point".
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 struct State{ int _pc_; // other variables that need to carry their value // over multiple resumes of the function call. }; #define DrYield(c,v) do{ state->_pc_ = c; return(v); resume_##c:; } while(false) #define DrReturn(v) do{ state->_pc_ = -1; return(v); } while(false) #define DrCase(c) case c: goto resume_##c void foo(State *s, void *new_memory, File_Data file){ switch (s->_pc_){ DrCase(1); // ... DrCase(5); } // ... code stuff ... for (begin();check();increment()){ if (need_memory_from_user()){ DrYield(1, NeedMemory); // do something with new_memory } if (need_file_from_user()){ DrYield(2, NeedFile); // do something with file } // ... code stuff ... } // ... code stuff ... DrReturn(state->success); }
I did a video trying it out here if you want to see it in detail without using goto. The goto version is required if your resumable function might want to use switches itself though.
The only issue I don't like is having to list all the cases and giving each yield point a unique integer. I haven't found away around that using the goto version though.
1 2 3 4 5 6 7 8 9 10 11 12 | struct State{ void* _pc_; //... } #define DrYield(v) do{ state->_pc_ = &&resume_## __LINE__; return(v); resume_##__LINE__:; } while(false) #define DrReturn(v) do{ state->_pc_ = 0; return(v); } while(false) void foo(State *s, void *new_memory, File_Data file){ if(s->_pc_) goto *s->_pc_; //... } |
1 2 3 | switch = 2132 goto = 1913 improvement = 10% |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | // Token to String #define TO_STRING(x) #x // Static Assertion #define STATIC_ASSERT3(cond, msg) typedef char static_assertion_##msg[(!!(cond))*2-1] #define STATIC_ASSERT2(cond, line) STATIC_ASSERT3(cond, static_assertion_at_line_##line) #define STATIC_ASSERT1(cond, line) STATIC_ASSERT2(cond, line) #define STATIC_ASSERT(cond) STATIC_ASSERT1(cond, __LINE__) // *_of "operators" typedef ptrdiff_t isize; // Signed sizeof is more useful #define size_of(x) (isize)(sizeof(x)) // Prevents C++ arrays from being passed #define count_of(x) ((size_of(x)/size_of(0[x])) / ((isize)(!(size_of(x) % size_of(0[x]))))) #define offset_of(Type, element) ((isize)&(((Type *)0)->element)) // C-Only #define align_of(Type) offset_of(struct { char c; Type member; }, member) // Swap #define swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while (0) // Bit cast: Very similar to doing `*(T *)(&u)` but can be faster #define BIT_CAST(dest, source) do { \ STATIC_ASSERT(size_of(*(dest)) <= size_of(source)); \ memcpy((dest), &(source), size_of(*dest)); \ } while (0) // Cast - Easy to grep #define cast(Type) (Type) |
cmuratori
I am skeptical that these computed gotos are necessary to get the "improved" performance. For example, in MSVC you would just place an __assume(0) in the default: case of a switch and it would no longer emit the bounds-checking code, right? Has anyone done a more thorough analysis as to whether these are interesting?
- Casey