Question:
A problem arose in creating a mini-library to simulate Threads on a PIC.
It would need to force the programmer to implement a function. I would like to know if there is any way to use preprocessors inside functions and only call it if the function is used.
It would look something like this:
void teste(){
#define _teste
}
int main(){
teste();
return 0;
}
#ifndef _teste
#error "Chame a funcao teste()"
#endif
Is it possible to do this somehow?
Edit: How can I return an informative error, in C, at compilation time due to the lack of something necessary for it to work, such as starting a timer for example, which does not generate a compilation error?
Answer:
A typical C compiler has these steps, in exactly this order:
- Pre-processing.
- Lexical, syntactic and semantic code analysis.
- Intermediate code generation (object code, those files with
.o
extension). - Linking.
- Executable code generation.
Let's say you start with this in main.c
:
void teste();
int main() {
teste();
}
Notice the prototype of the teste()
function there. It declares that the function exists somewhere, and that it is the linker's responsibility to find it. If it doesn't find it, it throws an error like this (assuming you're using GCC, but it's 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
The error message says that the teste
function was not found. It could be declared in a different file ( teste.c
for example):
#include <stdio.h>
void teste() {
printf("foo");
}
And then, when compiling the two sources together, the linker will find the teste()
function.
You can try to make the preprocessor work with this by making main
like this:
void teste();
int main() {
teste();
}
#ifndef _teste
# error Voce esqueceu de implementar o teste.
#endif
If the programmer does not define _teste
anywhere, this error will appear. However, this solution is incomplete as there is no guarantee that the teste()
function and the _teste
symbol appear together. For example, the verification is bypassed if in teste.c
there is this:
#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
At that point, the error persists even though the function has been implemented.
However, this doesn't work at all. The preprocessor processes the files separately, so even if the programmer puts the teste()
function and the _teste
symbol in another file, the error will still be raised.
You can try to remedy this with a #include
in main.c
:
#include "teste.inc"
void teste();
int main() {
teste();
}
#ifndef _teste
# error Voce esqueceu de implementar o teste.
#endif
Then, the programmer defines the function teste()
and the symbol _teste
within teste.inc
. However, this is not good programming practice because header files (files with a .h
extension) should not have an implementation. Although in this case, you're not using the header file exactly as a header, you're 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 for copying-and-pasting text, and in fact, it's a language completely separate from C. There is a language that is the preprocessor and a completely different language that is the compiler and both do not communicate except that the output of one is the input of the other. So it's not just possible in the preprocessor to know if the function has been declared, because the preprocessor doesn't even know what a function is and it just sees its code 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 preprocessor except that it uses the compiler's output as input) has nothing to allow for custom error messages.
We can think of a different approach. You can already provide a skeleton for the teste
function and drop #ifdef
. For example, if it's all 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 caveat that you are abusing the purpose of #include
, you can do this in main.c
:
#include "teste.inc"
void teste();
int main() {
teste();
}
And no teste.inc
:
#include <stdio.h>
void teste() {
# error Voce esqueceu de implementar o teste. Substitua essa mensagem pelo codigo.
}