/* A convenient, efficient compile-time-structured menu system with no dynamic allocation. When programming for Arduino and things like that, I prefer not having to deal with dynamic allocation, although perhaps due to the stack my comfort with this is a bit of an illusion. But I would also like to be able to write a menu tree in a relatively convenient way, and without using an absurd amount of space — especially writable RAM space. ROM space is nearly free by comparison. A partial solution within the constraints of the C++ template system is to use compile-time Lisp lists, or something analogous, made out of template objects. This lets us represent each particular menu as a compile-time type; then we could reasonably I compiled this with g++ (Debian 4.7.2-5) 4.7.2 -g -O menu.cc -Wa,-adhlns=menu.lst, and although it more or less worked, I am somewhat disappointed in the resulting code. The run-time overhead of this abstraction is not actually zero, even with -O. But we do end up with this (abbreviated substantially, missing some instruction bytes, and c++filted from the real listing): 33 .LC0: .string " " 35 .LC1: .string "* " 37 .LC2: .string "foo" 39 .LC3: .string "bar" 41 .LC4: .string "baz" 43 .text ... 109 .L9: ... 132 .L16: ... 173 00c1 83ED01 subl $1, %ebp 175 00c4 0F84A000 je .L15 176 00ca EB4C jmp .L7 178 .L4: 186 00cc C6042400 movb $0, (%rsp) 187 00d0 BF000000 movl $.LC2, %edi 189 00d5 E8000000 call conc cat(nil_class, print) 191 00da 48894424 movq %rax, 24(%rsp) 192 00df 488B5424 movq 16(%rsp), %rdx 193 00e4 48895424 movq %rdx, 48(%rsp) 194 00e9 48894424 movq %rax, 56(%rsp) 195 00ee 4889C3 movq %rax, %rbx 198 00f1 BD010000 movl $1, %ebp 200 00f6 BE000000 movl $.LC0, %esi 202 .L5: 213 00fb BA020000 movl $2, %edx 214 0100 BF000000 movl std::cout, %edi 215 0105 E8000000 call std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) 222 010a 4885DB testq %rbx, %rbx 223 010d 0F8570FF jne .L16 224 0113 E94CFFFF jmp .L9 ... 226 .L7: 237 0118 BA020000 movl $2, %edx 238 011d BE000000 movl $.LC0, %esi 239 0122 BF000000 movl std::cout, %edi 240 0127 E8000000 call std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) 246 012c BA030000 movl $3, %edx 247 0131 BE000000 movl $.LC3, %esi 248 0136 BF000000 movl std::cout, %edi 249 013b E8000000 call std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) 251 0140 C644242E movb $10, 46(%rsp) 258 0145 BA010000 movl $1, %edx 259 014a 488D7424 leaq 46(%rsp), %rsi 260 014f BF000000 movl std::cout, %edi 261 0154 E8000000 call std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) 271 0159 BE000000 movl $.LC1, %esi 272 015e 83FD01 cmpl $1, %ebp 273 0161 744A je .L11 275 .L12: 276 0163 BE000000 movl $.LC0, %esi 277 0168 EB43 jmp .L11 279 .L15: 288 016a BA020000 movl $2, %edx 289 016f BE000000 movl $.LC1, %esi 290 0174 BF000000 movl std::cout, %edi 291 0179 E8000000 call std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) 297 017e BA030000 movl $3, %edx 298 0183 BE000000 movl $.LC3, %esi 299 0188 BF000000 movl std::cout, %edi 300 018d E8000000 call std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) 302 0192 C644242E movb $10, 46(%rsp) 309 0197 BA010000 movl $1, %edx 310 019c 488D7424 leaq 46(%rsp), %rsi 311 01a1 BF000000 movl std::cout, %edi 312 01a6 E8000000 call std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) 314 01ab EBB6 jmp .L12 316 .L11: 327 01ad BA020000 movl $2, %edx 328 01b2 BF000000 movl std::cout, %edi 329 01b7 E8000000 call std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) 335 01bc BA030000 movl $3, %edx 336 01c1 BE000000 movl $.LC4, %esi 337 01c6 BF000000 movl std::cout, %edi 338 01cb E8000000 call std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) 340 01d0 C644242F movb $10, 47(%rsp) 347 01d5 BA010000 movl $1, %edx 348 01da 488D7424 leaq 47(%rsp), %rsi 349 01df BF000000 movl std::cout, %edi 350 01e4 E8000000 call std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) 358 01e9 B8000000 movl $0, %eax 359 01ee 4883C448 addq $72, %rsp 362 01f2 5B popq %rbx 366 01f3 5D popq %rbp 370 01f4 C3 ret That is, the printing of "bar" (.LC3) and "baz" (.LC4) was inlined into main, but "foo" (.LC2) got put into a physically constructed `conc` object. Furthermore the printing of "bar" (.LC3) seems to have been inlined in two different places. I abandoned this approach and went with the weemenu.h approach demonstrated in weemenumain.cc. */ #include // atoi #include using std::cout; template class conc { Left left; Right right; public: conc(Left left_, Right right_) : left(left_), right(right_) { } int list(int idx) { return right.list(left.list(idx)); } template conc, Right2> y(Right2 right2) { return conc, Right2>(*this, right2); } }; template conc cat(Left left, Right right) { return conc(left, right); } struct nil_class { template conc y(Right right) { return cat(*this, right); } int list(int idx) { return idx; } }; nil_class nil() { static nil_class rv; return rv; } class print { const char * const string; public: print(const char * const string_) : string(string_) { } int list(int idx) { cout << (idx ? " " : "* ") << string << '\n'; return idx - 1; } }; int main(int argc, char **argv) { nil_class() .y(print("foo")) .y(print("bar")) .y(print("baz")) .list(argc > 1 ? atoi(argv[1]) : 1); return 0; }