📜 ⬆️ ⬇️

Macros with a variable number of parameters

Recently I had to deal with one Open Source project. It was necessary to deal with one mistake. The error was floating and manifested itself exclusively on the stand, after half an hour the slave. And even then not always. Therefore, it was decided to log certain areas of the code.

Therefore, a simple function was written:
void dbg(const char * AMsg); 
which recorded the string in the log. It soon turned out that such a function was not enough and it was rewritten in this form:
 void dbg(const char * AFmt, ...); 
those. now it uses the vfprintf () function to write a formatted string to the file. As the number of calls grew, I wanted to write two more parameters to the file, namely __LINE__ and __FILE__. I did not want to transfer these two parameters with each function call, so it was decided to write a macro wrapper over the dbg () function, which would accept a variable number of parameters, call the original function and pass the file name and line number to it by the first two parameters.

Googling, I found a rather beautiful solution - to describe the class and reload the operator () in it. I sketched a test project, tested a new macro and a modified function, and was pleased with the result. After that, I connected the files to the source project, h-file clicked “StdAfx.h” and clicked Build. And then I was deeply disappointed. It turned out that part of the project was written in pure C, which was meant by my classes.
')
Googling more, I found such a solution. But alas, I used the VC6 ...

And then the idea came to me - and if the macro is replaced by a function that takes the parameters __FILE__ and __LINE__, save them somewhere and return a pointer to the original dbg () function. And this function will read the saved parameters and write them to a file. It was decided to save the parameters in global variables. And the variables themselves, in order not to run into a rake when multi-threaded work, declare as __ declspec (thread). B that's what happened in the end:

h-file
 // File debug_info.h #ifndef __DEBUG_INFO_H #define __DEBUG_INFO_H //     ,   typedef void (* DbgFuncType)(const char * AFmt, ...); // ,   __FILE__  __LINE__,       #ifdef __cplusplus extern "C" #endif DbgFuncType DbgFuncRet(const char* AFile, int ALine); //    #define dbg DbgFuncRet(__FILE__, __LINE__) #endif 
cpp file
 // File debug_info.h #include "StdAfx.h" #include <stdio.h> #include <stdarg.h> __declspec(thread) char __File__[MAX_PATH]; __declspec(thread) int __Line__; void DbgFunc(const char * AFmt, ...) { FILE * log; if (fopen_s(&log, log_name, "a")) return; va_list args; va_start(args, AFmt); vfprintf(log, AFmt, args); fprintf(log, "File: \"%s\", Line: %d\n", __File__, __Line__); fclose(log); va_end(args); } DbgFuncType DbgFuncRet(const char* AFile, int ALine) { strcpy_s(__File__, MAX_PATH, AFile); __Line__ = ALine; return &DbgFunc; } 

That's all. The only thing I would like to dwell on is the declaration of the function DbgFuncRet ()
 #ifdef __cplusplus extern "C" #endif DbgFuncType DbgFuncRet(const char* AFile, int ALine); 
Because Since the header is included in both c and cpp files, the name translator will use different decorations for the function. And such an announcement tells the broadcaster to always use decoration C.

Source: https://habr.com/ru/post/138150/


All Articles