Macro alternative for __FILE__, __LINE__, __func__ [duplicate] - c++

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.

Related

is there any way to get debug info like __FILE__ without using macro function in c/c++?

I'm writing a logger module, so I would like to print the debug infos like __FILE__ and __LINE__ in c/c++. So I used macro function like:
/// here's just an example
/// function str_format() returns a std::string in format like printf() in c
#define info(str, ...) (cout << str_format(str, ##__VA_ARGS__) << __FILE__ << ":" << __LINE__)
in most cases, it works fine. but 'define' just has more influence than I thought.
to give an example, if a member variable in c++ class needs initialize:
class test{
private:
int info;
public:
test(int a) : info(a) {}
};
this would cause an unexpected error, the compiler would take info as a macro function!
even the header file of logger(which is logger.h) is not included in the class test's file, as long as the third file include them both, and include 'logger.h' before 'test.h' would cause this problem! that's really annoying.
So, is there any way other than #undef (since the test.h has nothing to with logger.h) to solve this problem? or let's say how can I get debug info like FILE without using macro function?
As far as __FILE__ and __LINE__ is concerned, C++20 introduced std::source_location for this. There is no equivalent non-macro based solution in prior C++ standards.
As far as a non-macro logging solution overall, the most common approach involves implementing the logging function as a variadic template with a parameter pack, that ends up formatting the log message, from its parameters.
No, there is no other way. You'll have to either not use file information (really, who cares what your file is called? Most likely you actually care about the call stack at a problem location, which is what exceptions give you) or use better names than info. In fact the convention for macros is to use all caps, like INFO.

How to concatenate strings, integers and floating point numbers without using objects?

I'd like to add information in a crash dump file, in case my application crashes.
Therefore I've created a __try-__except clause:
__try
{
Do_Something();
}
__except (ShowCrashdumpInformation(_T(__FUNCTION__));
Instead of just __FUNCTION__, I'd like to add more information, but how can I do that?
The simpliest way is to use a CString, but this is blocked because of compiler error C2712 (Cannot use __try in functions that require object unwinding).
So, I'd like to use LPCTSTR strings (which are widely used in my application).
As a result it should look like (CString alternative):
CString temp; temp.Format(_T("Do_Something, int=[%d], float=[%f], string=[%s]), iParam, fParam, strParam);
Do anybody have an idea?
Thanks
By far the easiest solution is to simply sidestep the problem. Just forward the exact arguments, not converted, to a (template) function which does the actual writing to file. Since the __catch is not in the template function itself, but one level up the stack, you're safe.
You could use preprocessor macros to "stringify" the standard __LINE__ macro, and rely on the compiler adjacent string-literal concatenation.
Perhaps something like this:
#define STRx(x) #x
#define STR(x) STRx(x)
#define FILE_FUNCTION_LINE (__FILE__ ":" __FUNCTION__ ":" STR(__LINE__))
...
ShowCrashdumpInformation(_T(FILE_FUNCTION_LINE))
As long as you have literal values, you could use the STR macro to "stringify" them and then use adjacent string concatenation.
It's not possible using variables though, only using literal values.

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.

Get return type of function in macro (C++)

I have ASSERT(x) macro and I want to call return if it asserts (in release configuration).
To do this I need to know return type of function where I use this ASSERT. How to get it (I deal with C++03, LLVM GCC 4.2 compiler)?
My ASSERT macro:
#define ASSERT(x) \
if(!(x)) {
LOG ("ASSERT in %s: %d", __FILE__, __LINE__); \
return /*return_type()*/; \
}
PS: I tried return 0; - compiler shows error for void functions (and I didn't try it for complex returning types), if return; - error for non-void functions.
(Updated...)
I'll answer to werewindle, nyarlathotep and jdv-Jan de Vaan here. I use standard assert for debug configuration. But after beta-testing I still get crash reports from final customers, and in most cases I need to change my crashing functions:
ASSERT (_some_condition_);
if (!_some_condition_) // add this return
return _default_value_;
I understand, that my program may crash probably later (otherwise it will definitely crash in current function). Also I can't quit application because developing is for iPhone (apps may not quit programmatically there). So the easiest way would be to "auto return" when assertion failed.
You can't determine the return type of the surrounding function in a Macro; macros are expanded by the preprocessor, which doesn't have this kind of information about the surroundings where these macros occur; it is basically just "searching and replacing" the macros. You would have to write separate macros for each return type.
But why not exit the program (i.e. calling the exit function)? Just returning from a function doesn't seem like a very robust error handling. Failed assertions should after all only occur when something is terribly wrong (meaning the program is in a state it was not designed to handle), so it is best to quit the program as soon as possible.
There are no proper way to determine return type inside a function in C.
Also, if you somehow implement your variant of ASSERT it will lead to erroneous program behavior. Main idea of ASSERT: if it fails, then program is in undefined state and only proper way is to stop it now. I.e. by calling exit().
i think you can do this with a template function, that you call default(x) from within the macro.
template<class T> default<T>(T x) { return T(); }
that will work for everyting with a default constructor. I think you need to write a special macro for void.
I hope i got the template syntax right, my c++ is getting rusty.
You can't do that, the C/C++ preprocessor is pretty basic and it can't do any code analysis. At most what you can do is pass the return type to the macro.
But here's my opinion: you're using assertions the wrong way. They should only be used for sanity checks of the code (for errors than can only happen because of the programmer); if all assertions pass, you don't need to care about them, you don't need to log them.
And not only that, but (in general) you should employ the element of least surprise. Do you expect ASSERT to log something and then forcefully make the function return? I know I wouldn't. I either expect it to close the application completely (what the standard assert does) or let me decide what happens next (maybe I have some pointers to free).
Macros do not return values, as they are no functions per se. They substitute the source code where they are used, so you'd return in the function where the macro is used.
There is no way to get the return value from a macro.
You could just define another macro for your needs.
#define ASSERT(x) \
if(!(x)) { \
LOG ("ASSERT in %s: %d", __FILE__, __LINE__); \
ASSERT_DEFAULT_RETURN(); \
}
And then inside a function:
int foo(){
#ifdef ASSERT_DEFAULT_RETURN
#undef ASSERT_DEFAULT_RETURN
#endif
#define ASSERT_DEFAULT_RETURN() return 0
// ...
ASSERT(some_expression);
// ...
// cleanup
#undef ASSERT_DEFAULT_RETURN
}
Just do this
#define ASSERT(x, ret, type) \
if(!(x)){
LOG ("ASSERT in %s: %d", __FILE__, __LINE__); \
return (type) ret;
}
I believe you are trying to solve the wrong problem. You don't want your program to crash in case of assertions, you best improve your testing.
Having a 'return' in these assertions give you a false sense of security. Instead it hides your problems and causes unexpected behavior in your program so the bugs you do get are much more complex. A colleague of mine actually wrote a good blog post about it.
If you really would want it, you could try writing return {}; so it default constructs the value, or have an assert macro where you also provide the failure case. However I really don't recommend it!