Procurando nos protocolos de entrevista a posição do desenvolvedor, encontrei o seguinte problema: "Ofereça um código que imprima números em ordem decrescente de n a 0, sem usar operadores de comparação (ocultos ou explícitos) (a implementação da função de impressão não conta)". Apesar de essa tarefa não ter nada a ver comigo, ela me ocupou e decidi pensar em maneiras de resolvê-la (embora ainda não saiba quem e quando resolver qual tarefa precisaria desse método de otimização de código).
A primeira coisa que veio à mente foi tentar usar padrões. Assim
template<int n> void print() { printf("%d\n", n); print<n - 1>(); } template<> void print<0>() { printf("0\n"); } int main(int argc, char* argv[]) { print<N>(); return 0; }
O problema é que a programação é uma disciplina de engenharia, não oculta, e se "alguma coisa" acontecer no sistema, "ela" deve ser descrita e os recursos devem ser alocados para ela, e neste caso, uma vez que o desenvolvimento de modelos ocorre no estágio de compilação, há restrições ao aninhamento de tais construções (e é bom e correto, e graças a Deus), e o compilador emitiu com razão "o erro fatal C1202: contexto recursivo de dependência de função ou tipo muito complexo" com tamanho N maior 2000.
A próxima coisa que veio à mente foi usar o método clássico com ponteiros para funções.
typedef void(*PrintFunc)(int); void printZero(int); void printNumber(int n); PrintFunc g_functors[] = {printZero, printNumber}; void printZero(int) { printf("0\n"); } void printNumber(int n) { printf("%d\n", n--); g_functors[!!n](n); } int main(int argc, char* argv[]) { printNumber(N); return 0; }
Mas, mesmo assim, as restrições impostas a nós pela lei da natureza se fizeram sentir, e como o aninhamento de chamadas de função é limitado pelo tamanho da pilha, um "estouro de pilha" legítimo foi obtido com um valor de N> 4630.
Nesse lugar, decepção e raiva tomaram conta de mim completamente e percebi que, para uma solução completa para esse problema, você não deve evitar nada, incluindo os truques mais sujos. O problema é que, para certos valores de N, precisamos transferir o controle para as seções necessárias do código. E isso não é problema quando temos declarações condicionais à nossa disposição, mas quando elas não estão lá, temos que recorrer à bruxaria. Nos tempos antigos, isso poderia ser resolvido usando o método goto, mas desde então caçadores de bruxas e outros combatentes de dragões limitaram severamente sua funcionalidade (e isso também é bom e correto) e gostaríamos de escrever algo assim
g_functors[0] = [](int){print("0\n"); goto end; } g_functors[1] = [](int n){print("%d\n", n); goto begin; } begin: g_functors[!!n](n--); end:
mas não podemos.
Portanto, embora eu tenha sido proibido de praticar magia negra, nesse caso, decidi abrir uma exceção adicionando um pouco de magia ao exemplo anterior.
void printZero(int); void printNumber(int n); PrintFunc g_functors[] = {printZero, printNumber}; void printZero(int) { printf("0\n"); throw 0; } void printNumber(int n) { printf("%d\n", n); } int main(int argc, char* argv[]) { int n = N; try{ begin: g_functors[!!n](n--); goto begin; } catch (...){} return 0; }
E isso resolveu completamente o problema (para qualquer N).
PS: Ficarei feliz em saber sobre outros métodos para resolver este problema