A typical C compiler has these steps, exactly in that order:
- Preprocessing.
- Lexical, syntactic and semantic analysis of the code.
- Intermediate code generation (object code, these files with extension
.o
).
- Linkage.
- Generation of executable code.
Let’s assume you start with this on main.c
:
void teste();
int main() {
teste();
}
Observe the function prototype teste()
there. It declares that the function exists somewhere, and that it is the linker’s responsibility to find it. If it does not, it will throw such an error (assuming you are using GCC, but it will look something like some other compiler):
/home/blablabla/blablabla.o: In function `main':
main.c:(.text.startup+0x7): undefined reference to `teste'
collect2: error: ld returned 1 exit status
Error message says the function teste
was not found. It could be declared in a different file (teste.c
for example):
#include <stdio.h>
void teste() {
printf("foo");
}
And then, by compiling the two sources together, the linker will find the function teste()
.
You can try to get the preprocessor to work with this when doing the main
be like this:
void teste();
int main() {
teste();
}
#ifndef _teste
# error Voce esqueceu de implementar o teste.
#endif
If the programmer does not define _teste
nowhere, this error will appear. However, this solution is incomplete as there is no guarantee that the function teste()
and the symbol _teste
appear together. For example, the check is circumvented if the teste.c
have that:
#define _teste
// Esqueci de implementar a função aqui!
Another one that goes wrong is this:
#include <stdio.h>
void teste() {
printf("Foo");
}
// Esqueci de dar o #define _teste
In this, the error persists even if the function has been implemented.
However, this does not work at all. The preprocessor processes the files separately, so even if the programmer puts the function teste()
and the symbol _teste
in another file, still the error will be triggered.
You can try to remedy with a #include
in the main.c
:
#include "teste.inc"
void teste();
int main() {
teste();
}
#ifndef _teste
# error Voce esqueceu de implementar o teste.
#endif
And then, the programmer sets the function teste()
and the symbol _teste
within the teste.inc
. However, this is not good practice of programming because header files (files with extension .h
) should not be implemented. Although in this case, you are not using the header file exactly as header, but rather abusing the header definition to import an implementation (so we used a different extension, .inc
).
The problem is that the preprocessor is just a tool to copy-and-paste text and is actually a language entirely separate from C. There is a language that is pre-processorprocessor and a completely different one that is the compiler and both do not communicate except because the output of one is the input of the other. Thus, it is not possible only in the preprocessor to know if the function has been declared, because the preprocessor does not even know what a function is and it sees its code only as a bunch of text to be blindly copied to the output. On the other hand, the linker (which is also separate from the compiler and pre-processor except for the fact that it uses the compiler output as input) has nothing that allows customized error messages.
We can think of a different approach. You can already provide a skeleton for the function teste
and abandon the #ifdef
. For example, if everything is done in a single source code:
#include <stdio.h>
void teste();
int main() {
teste();
}
void teste() {
# error Voce esqueceu de implementar o teste. Substitua essa mensagem pelo codigo.
}
In multiple source code, with the exception that you are abusing the purpose of #include
, you can do it on main.c
:
#include "teste.inc"
void teste();
int main() {
teste();
}
And in the teste.inc
:
#include <stdio.h>
void teste() {
# error Voce esqueceu de implementar o teste. Substitua essa mensagem pelo codigo.
}
I’m not sure I understand your problem, but it seems to be an example of XY problem. That’s not the purpose of the preprocessor. If the programmer should implement a declared function somewhere and he does not, the result is a build error, which would already signal to the programmer that there is something very wrong.
– Victor Stafusa
The idea is only to allow you to run the code down only if a function has been called ? If it is clearly an XY problem
– Isac
Right, but I would like a more informative error to make it easier to use the library. I’ll take a look at the link. And the function does not generate a build error, it just messes up the code because it is the setup of an interruption.
– Daniel
Okay, I believe XY kkk is a problem. So I’ll try to be more specific ... How to return an informative error in C at compile time by the lack of something necessary for the operation that does not generate compile error?
– Daniel
I think my answer below should satisfy you. Tell me what you think of her.
– Victor Stafusa
Yeah. That’s what I needed to know. In fact then it means that I cannot intersperse the two languages in such a way that they are conditional on one to execute the other. I’ll try to implement your idea of the bug with the code and see if I can get something relevant.
– Daniel