C++ macro and default arguments in function - c++

Im trying to make a generic function to display error messages, with the possibility that the program should exit after the message has been displayed.
I want the function to show the source file and line at which the error occurred.
argument list:
1.char *desc //description of the error
2.char *file_name //source file from which the function has been called
3.u_int line //line at which the function has been called
4.bool bexit=false //true if the program should exit after displaying the error
5.int code=0 //exit code
Because of (4) and (5) i need to use default arguments in the function definition, since i don't want them to be specified unless the program should exit.
Because of (2) and (3) i need to use a macro that redirects to the raw function, like this one:
#define Error(desc, ???) _Error(desc,__FILE,__LINE__, ???)
The problem is that i don't see how those 2 elements should work together.
Example of how it should look like:
if(!RegisterClassEx(&wndcls))
Error("Failed to register class",true,1); //displays the error and exits with exit code 1
if(!p)
Error("Invalid pointer"); //displays the error and continues

You cannot overload macros in C99 -- you will need two different macros. With C11, there is some hope using _Generic.
I had developed something very similar -- a custom warning generator snippet for Visual Studio -- using macros. GNU GCC has some similar settings for compatibility with MSVS.
#define STR2(x) #x
#define STR1(x) STR2(x)
#define LOC __FILE__ “(“STR1(__LINE__)”) : Warning Msg: “
#define ERROR_BUILDER(x) printf(__FILE__ " (“STR1(__LINE__)”) : Error Msg: ” __FUNCTION__ ” requires ” #x)
The above lines take care of your arguments 1 to 3. Adding support for 4 would require inserting a exit() call within the macro. Also, create two different macro wrappers should you require to two different argument lists (the one with the default argument can delegate to the other macro).
#define ERROR_AND_EXIT(msg, cond, errorcode) ERROR_BUILDER(msg) if (cond) exit(errorcode)
#define ERROR_AND_CONT(msg) ERROR_BUILDER(msg)
I had put up a detailed description here (warning: that's my blog -- so consider it to be a shameless plug).

Related

What happens with undefined macro parameters once compiled?

In my VC++ project I use a macro for debugging and logging purpose.
calling:
Logger(LogLevel::Info, "logging started");
macro:
#ifdef DEBUG
#define Logger(level, input) \
{ \
cerr << "[" << level << "] " << input << endl; \
};
#else
#define Logger();
#endif
When compiling this I get the following warning (but it still compiles):
warning C4002: too many actual parameters for macro 'Logger'
I am wondering how the compiler handles this situation.
Are the macro parameters still used for compiling? E.g. will one be able to see the "logging started" string on reverse engeneering?
I am wondering how the compiler handles this situation. Are the macro parameters still used for compiling?
Macros are processed in the pre-processing stage. If the pre-processor is able to deal with the extra arguments used in the usage of the macro, the only thing it can do is drop those parameters in the macro expansion.
E.g. will one be able to see the "logging started" string on reverse engeneering?
No, the code processed by the compiler will not have that line at all.
If you have the option to change those lines of code, I would recommend changing the non-debug definition of the macro to be a noop expansion. E.g.:
#define Logger(level, input) (void)level;
As per the law, a macro function must declare as many parameters as you call it with.
So calling your macro
#define Logger()
as Logger(LogLevel::Info, "logging started") results in an error. MSVC probably allows it because it isn't standard-compliant. There's not much to reason further (which is my answer to the actual question).
You either
#define Logger(unused,unused2)
and leave the replacement part empty or
#define Logger(...)
and suffer the consequences of being able to call with any number of arguments. [Hint: First one is recommended.]

Capture __LINE__ and __FILE__ without #define

Trying to determine a "modern" implementation for the following C-style code:
#define logError(...) log(__FILE__, __LINE__, __VA_ARGS__)
Is is possible to capture this using variadic templates or something similar that does not rely on a #define ?
Desired use case:
logError( "Oh no! An error occurred!" );
Where __FILE__, and __LINE__ are captured under the hood, but reflect the file name and line number of where logError was called from.
Macros are indeed your only choice, at least until std::source_location makes it into the standard and fulfills your wish.
Actually the preprocessor is the only choice when you want to work with line numbers and filenames.
For the compiler it's not possible to use line numbers and filenames as arguments for function calls (or storing them in a variable).
In my company we had the exactly same issue with logging. We ended up with an external script scanning the source files and then building proper functions to call.

Using the word "SING" in a C enum errors to "expected an identifier"

In a header file I have the following enum:
namespace OBJ_VERBS {
enum { zero,
CUDDLE, EMBRACE, FLIP, GROPE, HUG,
KISS, LICK, NUDGE, PAT, PINCH,
POKE, PULL, RUB, SHAKE, SQUEEZE,
TAP, TUG, TURN, WAVE, PEER,
PET, CLENCH, CURSE, NUZZLE, SNAP,
STROKE, TWIRL, LEAN, GRIP, SMELL,
GRUNT, SQUEAL, SCOLD, GAZE, WIND,
SPIT, SPIN, DANCE, SING,
zTOTAL};
const int _MAX_ = int(OBJ_VERBS::zTOTAL - 1);
}
I get the following error: Error: expected an identifier
I tried searching the web to see if "SING" was a keyword, but it's not.
Any ideas?
I am betting you are including (directly or indirectly) math.h.
A little investigation reveals:
$ grep -r -w SING /usr/include/
/usr/include/math.h:#define SING 2
As it's #defined to 2, the enum attempts to use 2 as an enum member, which fails with the error given above.
Note that I'm (necessarily) guessing what's happening here based on the include files I have locally. It's possible you have something entirely different causing the issue, but the most likely culprit is a #define in an include file. See the grep I used above for how I found it.
This is why it's often a good idea to use (e.g.) OV_CUDDLE, OV_EMBRACE etc., to minimise collisions.
For what it's worth, the context of SING in math.h is:
/* Types of exceptions in the `type' field. */
# define DOMAIN 1
# define SING 2
# define OVERFLOW 3
# define UNDERFLOW 4
# define TLOSS 5
# define PLOSS 6
Of course, abligh most likely found the culprit. However, according to the C++ standard the identifier SING should be legal and hence available for you to use. The best way to test this is
#ifdef SING
# error 'SING' pre-#defined -- use only standard compatible headers.
#endif
If you only #include C++ standard headers (such as cmath) and C++ -compliable headers, this shouldn't happen. Those headers must only use identifiers which are not legal to use by the user. If the problem persists even then, you have encountered a bug/feature in the standard library implementation your using or the third-party library.
abligh was spot-on. I am using math.h, and here's what I found in there.
#define DOMAIN _DOMAIN
#define SING _SING
#define OVERFLOW _OVERFLOW
#define UNDERFLOW _UNDERFLOW
#define TLOSS _TLOSS
#define PLOSS _PLOSS
// Constant definitions for the exception type passed in the _exception struct
#define _DOMAIN 1 // Argument domain error
#define _SING 2 // Argument singularity
#define _OVERFLOW 3 // Overflow range error
#define _UNDERFLOW 4 // Underflow range error
#define _TLOSS 5 // Total loss of precision
#define _PLOSS 6 // Partial loss of precision
Thank you all for the extremely fast responses.

C++ Error "expected initialiser before '[macroname]'"

I'm trying to make a program run with the eclipse IDE but I get the above mentioned error.
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
tCRU_BUF_CHAIN_HEADER *CRU_BUF_Allocate_MsgBufChain ARG_LIST((UINT4 u4Size,UINT4 u4ValidOffsetValue));
[some more macros where this error comes]
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
is one of the errors, there comes the error:
"expected initialiser before 'ARG_LIST'"
To be accurate, there are 18 macros of the same type that give this error, in the moment i delete the "ARG_LIST" the error goes away, but because this isn't code that I created I don't want to delete this part.
I tried to find a solution in the net but couldn't find something and now I'm hoping someone here can help me.
If you need some more information I try to answer it as fast as possible.
I think you can safely delete the ARG_LIST part. Macros like ARG_LIST were used in old (1970s) versions of C++ where functions/methods didn't specify the parameters they took. For example, you declared a function like this:
tCRU_BUF_CHAIN_HEADER *CRU_BUF_Allocate_MsgBufChain();
And you could call it with any number of arguments.
Then, when full function signatures were added to the language, programmers defined macros to take advantage of type checking in compilers that supported it, but still make the code compatible with compilers that didn't support it:
#ifdef FULL_SIGNATURES_SUPPORTED
#define ARG_LIST(list) list
#else
#define ARG_LIST(list) ()
#endif
Nowadays all compilers support full signatures, so there's no point to use such macros.

Passing parameters to a no-op macro in C++

I am getting the following error message
error: '0' cannot be used as a function
when trying to compile the following line:
NOOP(0 != width);
NOOP is defined as follows:
#define NOOP (void)0
The source code is part of a SDK - so it should be okay. And I have found out that (void)0 actually is a valid way to descibe "no operation" in C++. But why would you want to pass a boolean parameter to a function which does nothing? And how do you get rid of the error message?
The MACRO is not defined with any parameters on it, so after the preprocessor replaces code, that statement ends up looking like this:
(void)0(0 != width);
Which confuses the compiler into thinking you are trying to use the "()" operator on 0. (i.e. using 0 as a function)
I recommend that you drop the "(0 != width)" (it is misleading) and just write NOOP;
"(void)0(0!=width);" is not valid C++, so it's not OK. (void)0; by itself doesn't do anything in C++, so can be used as a noop. Instead of your current define, I would use:
#define NOOP(X) (void)0
This tells the C++ preprocessor that there is a preprocessor function called NOOP that takes one parameter of any type, and replaces that entire function call with (void)0. So if you have a line of code that says NOOP("HELLO WORLD"), then the preprocessor replaces that entire thing with (void)0, which the C++ compiler proceeds to ignore.