A common mistake programmers seem to make is assuming that abstraction is about putting a layer of indirection (whether through function calls or data structures) into a program. Indirection is one of the more commonly used tools to implement abstractions, but just because there is now an extra layer of function calls in your program does not make it more understandable, or more maintainable, or a better fit to the domain. Those are better criteria for what makes abstractions effective than counting the number of layers of indirection.
Another thing programmers like to do is argue about programming languages and the abstractions (over machine language) they provide. Often you will hear "language A has feature X" and the refrain "language B does not have feature X and language B is good enough, so feature X is not important/is harmful." When iterated enough times, it is obvious that this becomes a sort of reductio ad assembler argument.
Is a sufficiently powerful assembler good enough? So what makes C better than assembler? Why is C special? And what is better than C?
It is productive to be able to both ask and answer the last question, which is why real metaprogramming is an invaluable thing to have.