Can I use __LINE__ or __FILE__ in inline function in C++? - c++

I faced a problem while implementing the logger.
First, I used to __LINE__ and __FILE__ with standard C Macro function like below
// global.h
..
namespace MyLogger {
class Logger
{
..
void _write(int _level, const char* _file, int _line, const char* _fmt, ...);
};
static Logger logger;
}; // namespace MyLogger
..
// avoid conflict..
#undef error
#undef trace
#define error(_MESSAGE_, ...) _write(LOG_LEVEL_ERROR, (const char*)__FILE__, (int)__LINE__, (const char*)_MESSAGE_, ##__VA_ARGS__)
#define info(_MESSAGE_, ...) _write(LOG_LEVEL_INFO, (const char*)__FILE__, (int)__LINE__, (const char*)_MESSAGE_, ##__VA_ARGS__)
#define trace(_MESSAGE_, ...) _write(LOG_LEVEL_TRACE, (const char*)__FILE__, (int)__LINE__, (const char*)_MESSAGE_, ##__VA_ARGS__)
..
// main.cpp
using namespace MyLogger;
// using like this
logger.error("- log error");
logger.info("- log info %s", "test 1");
..
In fact, it seems to work well.
However, the problem occurred while using openv.
error and trace seem to conflicted with error and trace in opencv.
error: expected primary-expression before ‘int’
[build] CV_EXPORTS CV_NORETURN void error(int _code, const String& _err, const char* _func, const char* _file, int _line);
So I'm thinking about other methods using inline functions, not macros.
// global.h
..
//void _write(int _level, const char* _file, int _line, const char* _fmt, ...);
static inline void error(/* Is it possible to toss __LINE__ or __FILE__ ? */);
static inline void info(/* .. */);
static inline void trace(/* .. */);
..
// main.cpp
logger::error("- log error");
logger::info("%d %c %f, 1, 'A', '0.1');
Can I know the line or file of the location where the log was output through a method other than a macro?

No, inline functions/methods won't work the same as macro in this context.
Macros are simply text replacements, that's why the __LINE__ and __FILE__ will give accurate results. Thus macros are your only choice; See if you can name them better to avoid conflicts.
However the inline functions are systematically compiled subunits. So the line number and the file names will always be same that of those functions where it resides.
Refer: Inline functions vs Preprocessor macros
C++20
As suggested in this useful comment, you may also consider using std::source_location which allows you to avoid the macro trickery via the help of standard library & language features.

There are 3 stages in which C++ becomes a running program.
Preprocessor
Compiler
Linker
__LINE__ and __FILE__ are processed by the preprocessor (stage 1). However, it's the compiler that decides about inlining (stage 2).
Other languages like C# have this in the compiler stage: [CallerMemberName], [CallerFilePath] and [CallerLineNumber] [MSDN] but I am don't think this exists in C++ at the moment.

How about this:
#define log_error(_MESSAGE_, ...) \
logger._write( \
LOG_LEVEL_ERROR, \
(const char*)__FILE__, \
(int)__LINE__, \
(const char*)_MESSAGE_, \
##__VA_ARGS__)
There is no much difference (in the sense of typed characters) between logger.error and log_error. The advantages are, that you use a macro (__FILE__, __LINE__ are utilized as desired) and have your logger class used only at one place (via the macro, in case you want to use a different logging mechanism), refactoring should be without any headaches.

Related

C/C++ aliasing functions to other function with different arguments

The following is some background as to what I want to do and why. The actual question is at the very bottom...
I have an API that has some format.
For instance
int f(void *str1, void *str2) {...}
I want to reimplement the system behind the API so that it can be a drop in replacement for the old system. However it turns out that str1 is now unnecessary and moreover doesn't make sense to have if you're aware that you're using my new system. Therefore, I want to be able to expose the underlying API that makes sense:
int f_internal(void *str2);
Right now I have code that looks like this:
#ifdef USE_INTERNAL
#define INTERNAL(sym) sym##_internal
#else
#define INTERNAL(sym) sym
#endif
extern "C" {
#ifndef USE_INTERNAL
int f(void *str1, void *str2){
return INTERNAL(f)(str2);
}
#endif
SOME_ATTRIBUTES
int
INTERNAL(f)(void *str2){
... // actual content
} EXPORT_FUNCTION_MACRO(INTERNAL(f), 1);
}
The effect is if I define USE_INTERNAL, a call to f(a) works, but I don't define it then we have to use the f(a, b).
The problem I am encountering is that EXPORT_FUNCTION_MACRO itself defines another function name but doesn't evaluate INTERNAL(F) first. This results in the message
dir: error: pasting ")" and "_" does not give a valid preprocessing token
INTERNAL(sym) \
--- NOTE--- EXPORT_FUNCTION_MACRO takes args 'sys' and 'n'
other_dir: note: in definition of macro ‘EXPORT_FUNCTION_MACRO’
void _example##sym##_##n(void) {} \
WHAT I WANT TO DO:
I want to have a compile flag that can change the number of arguments needed to call something.
Maybe something like (if it existed)
using f(a, b) = f_internal(a);
Any ideas?
This results in the message
To fix the message let macro arguments expand before concatenating them.
#define EXPORT_FUNCTION_MACRO_X(sys, n) EXPORT_FUNCTION_MACRO(sys, n)
EXPORT_FUNCTION_MACRO_X(INTERNAL(f), 1);
You could achieve that with a macro in the C part:
#define f(...) CONCAT(f, NUM(__VA_ARGS__, 2, 1)) (__VA_ARGS__)
#define CONCAT(X, Y) CC(X, Y)
#define CC(X, Y) X ## Y
#define NUM(X, Y, N, ...) N
#ifdef __cplusplus
extern "C"
#endif
void f_internal(void* str);
// specifically compiled for C or C++ anyway (and most likely inlined)
// -> no need for extern "C" for these:
void f1(void* str) { f_internal(str); }
void f2(void* unused, void* str) { f_internal(str); }
The macro f would select the correct function out of f1 and f2, which again would call f_internal with the correct argument. Works for both C and C++. If you prefer, you could still just provide two overloads for C++ separately and let only the C people deal with the macros.
I doubt one could call that 'elegant'; if you want to qualify as 'ugly' – up to you. But at least it works...
The following code behaves like this:
If USE_INTERNAL is defined, defines an inline (c++) / static (c) function int f(void* str).
Otherwise, defines an inline (c++) / static (c) function int f(void* str1, void* str2).
Both functions are trampolines to the actual (int f_internal(void* str)) function.
Note that since the functions are defined in the header, inline (c++) / static (c) is required to keep them from violating the ODR rule (I am a c++ person, so I don't know any way better than static to achieve this in c. If there is, please let me know).
#ifdef __cplusplus
extern "C"
#endif
int f_internal(void* str);
#ifdef USE_INTERNAL
#ifdef __cplusplus
inline
#else
static
#endif
int f(void* str1, void* str2) {
return f_internal(str2);
}
#else
#ifdef __cplusplus
inline
#else
static
#endif
int f(void* str) {
return f_internal(str);
}
#endif

using __FUNCSIG__ in a macro expands it to be empty

I am using an API which return int error codes and I decided to build a wrapper error class :
class Error
{
...
public:
Error(int ErrC, const char* UserMessage, const char* FileName, int LineNumber, const char* Function);
char * GetFunction();
...
}
I decided to venture into the world of macros and create a macro to instantiate the class for me :
#define API_ERROR(Code,MSG) API::Error(Code,MSG,__FILE__,__LINE__,__FUNCSIG__)
I then defined a Test function that is called by main
void TestFunc()
{
API::Error Error = API_ERROR(0,"Hello");
std::cout << Error.GetFunction();
}
Using the compiler option to output the post pre-processing results (Properties->C/C++->Preprocessor->Preprocess to a file) yielded
void TestFunc()
{
API::Error Error = API::Error(o,"Hello","...\\main.cpp",30,);
std::cout << Error.GetFunction();
}
I think this does not work because __FUNCSIG__ is only defined inside functions.
I have also tried
#define EMPTY()
#define DEFER(...) __VA_ARGS__ EMPTY()
#define API_ERROR(Code,MSG) API::Error(Code,MSG,__FILE__,__LINE__,DEFER(__FUNCSIG__))
But I think I misunderstood the author of the post.
Is there a way to make this work ?
I am using Visual Studio 2019 Community with the default MSVC++ compiler.
From this VS2019 predefined macro reference
The __FUNCSIG__ macro isn't expanded if you use the /EP or /P compiler option.
[Emphasis mine]
If you preprocess your source then the /EP or /P flags would be set, and the macro won't be expanded. It will only be expanded when actually building your source.
The __FUNCSIG__ macro will probably not be expanded by the preprocessor because it doesn't really know anything about C++ symbols. It might not even be a true preprocessor macro, and could be "expanded" (or replaced) at a later stage in compilation when C++ symbols are known.

easily throw away c++ call completely

I'm trying to implement logging which produce no overhead when not needed (i.e. no method call should be performed at all). I want NO overhead because it's low-latency code. I just added #define ENABLE_LOGS to my header class and now it looks like that (you can ignore details)
#pragma once
#include <string>
#include <fstream>
#define ENABLE_LOGS
namespace fastNative {
class Logger
{
public:
Logger(std::string name_, std::string fileName, bool append = false);
~Logger(void);
void Error(std::string message, ...);
void Debug(std::string message, ...);
void DebugConsole(std::string message, ...);
void Flush();
static Logger errorsLogger;
static Logger infoLogger;
private:
FILE* logFile;
bool debugEnabled;
};
}
Every time I need to use some method I should surround it like that:
#ifdef ENABLE_LOGS
logger.Debug("seq=%6d len=%4d", seq, length_);
#endif
It's error-phrone (i can forgot to surround) and makes code dirty. Can I fix my code somehow not to use #ifdef every time?
In C# I like Conditional I guess I need something like that for c++.
First of all it would make sense to have a look to see what's out there already. This is a common problem and many people will have solved it before. E.g., see stackoverflow question C++ logging framework suggestions, and Dr Dobbs A Highly Configurable Logging Framework In C++.
If you do roll your own, you should get some good ideas from having done this. There are several approaches I've used in the past. One is to make the statement itself conditionally defined
#ifdef ENABLE_LOGS
#define LOG(a,b,c) logger.Debug(a, b, c)
#else
#define LOG(a,b,c)
#endif
Another approach is to conditionally define the logging class itself. The non-logging version has everything as empty statements, and you rely on the compiler optimizing everything out.
#ifdef ENABLE_LOGS
class Logger
{
public:
Logger(std::string name_, std::string fileName, bool append = false);
~Logger(void);
void Error(std::string message, ...);
void Debug(std::string message, ...);
void DebugConsole(std::string message, ...);
void Flush();
static Logger errorsLogger;
static Logger infoLogger;
private:
FILE* logFile;
bool debugEnabled;
};
#else
class Logger
{
public:
Logger(std::string name_, std::string fileName, bool append = false) {}
~Logger(void) {}
void Error(std::string message, ...) {}
void Debug(std::string message, ...) {}
void DebugConsole(std::string message, ...) {}
void Flush() {}
};
#endif
You could put your Logger implementation for ENABLE_LOGS in a cpp file under control of the macro. One issue with this approach is that you would want to be sure to define the interface so the compiler could optimize everything out. So, e.g., use a C-string parameter type (const char*). In any case const std::string& is preferable to std::string (the latter ensures there's a string copy every time there's a call).
Finally if you go for the first approach, you should encapsulate everything in do() { ... } while(0) in order to ensure that you don't get bizarre behavior when you use your macro where a compound statement might be expected.
There is one way (the way llvm does) to do this using macros.
#ifdef ENABLE_LOGS
#define DEBUG(ARG) do { ARG; } while(0)
#else
#define DEBUG(ARG)
#endif
Then use it as:
DEBUG(logger.Debug("seq=%6d len=%4d", seq, length_););
What I often see, is to use the #define to actually define the log calls, eg:
#define LOG_DEBUG(msg) logger.Debug(msg);
But you want to wrap the defines in a block that enables or disables your logging:
#ifdef ENABLE_LOGS
#define LOG_DEBUG(msg) logger.Debug(msg);
#else
#define LOG_DEBUG(msg)
#endif
You can call LOG_DEBUG anywhere in your code. If the logging is disabled, calling LOG_DEBUG ends up as a blank line in your final code.
A nice old trick is:
#ifdef ENABLE_LOGS
#define LOG Logger.Debug
#else
#define LOG (void)sizeof
#endif
Then the code:
LOG("seq=%6d len=%4d", seq, length_);
will expand to:
Logger.Debug("seq=%6d len=%4d", seq, length_);
that does the log. Or:
(void)sizeof("seq=%6d len=%4d", seq, length_);
that does absolutely nothing. It doesn't even evaluate the function arguments!!!
The trick is that the first version uses the comma as argument separator in a function call. In the second version, however, it is a unevaluated comma operator.
However, some compilers may give spurious warnings about unreachable code.
You could put the #ifdef inside the body of the individual functions. This avoid the code duplication issue in TooTone's answer.
Example:
void fastNative::Logger::Debug(std::string message, ...)
{
#ifdef ENABLE_LOGS
// do the actual logging
#endif
}
If ENABLE_LOGS isn't defined, this function doesn't do anything. What I would suggest is you pass a const char* instead of std::string to these method. That way, if ENABLE_LOGS is not defined, you wouldn't have to rely on the compiler to not create redundant std::string objects.

Custom assertions in a c++ unit tester

I am working on a c++ unit tester(mostly as practice) and had some questions about my implementation.
I wanted to have the ability to overload my custom assertions so I decided to implement them as functions that I wrapped in a namespace.
My current implementation is as follows:
Tester.h
#include <string>
#define INFO __FILE__, __LINE__
namespace Tester
{
void Assert(char const* input, char const* file_path, int line_number, std::string error_message);
...more overloaded Asserts and some Log functions...
}
And when I call the function:
#include "Tester.h"
...code...
Tester::Assert(false, INFO, "Some message");
...code...
This works but I´m not sure that the 'INFO' macro is good practice.
I welcome all suggestions and pointers about this implementation, and feel free to tell me if it makes no sense and I should be doing something completely different ;)
INFO as a macro seems perfectly fine. An alternative, which I prefer is to have a macro
#define TEST_ASSERT(condition, message) Tester::Asssert(condition, __FILE__, __LINE__, message)
or something of that sort.
If you have different number of arguments, something like this may work:
#define TEST_ASSERT(condition, ...) Tester::Asssert(condition, __FILE__, __LINE__, __VA_ARGS__)
Assuming your compiler is new enough, that is.

How do I tell gcc to relax its restrictions on typecasting when calling a C function from C++?

I'm trying to use Cmockery to mock C functions called from C++ code. Because the SUT is in C++, my tests need to be in C++.
When I use the Cmockery expect_string() macro like this:
expect_string(mock_function, url, "Foo");
I get:
my_tests.cpp: In function ‘void test_some_stuff(void**)’:
my_tests.cpp:72: error: invalid conversion from ‘void*’ to ‘const char*’
my_tests.cpp:72: error: initializing argument 5 of ‘void _expect_string(const char*, const char*, const char*, int, const char*, int)’
I see in cmockery.h that expect_string is defined:
#define expect_string(function, parameter, string) \
expect_string_count(function, parameter, string, 1)
#define expect_string_count(function, parameter, string, count) \
_expect_string(#function, #parameter, __FILE__, __LINE__, (void*)string, \
count)
And here's the prototype for _expect_string (from cmockery.h):
void _expect_string(
const char* const function, const char* const parameter,
const char* const file, const int line, const char* string,
const int count);
I believe the problem is that I'm compiling C code as C++, so the C++ compiler is objecting to (void*)string in the expect_string_count macro being passed as the const char* string parameter to the _expect_string() function.
I've already used extern "C" around the cmockery.h include in my_tests.cpp like this:
extern "C" {
#include <cmockery.h>
}
...in order to get around name-mangling problems. (See "How do I compile and link C++ code with compiled C code?")
Is there a command-line option or some other means of telling g++ how to relax its restrictions on typecasting from my test's C++ code to the C function in cmockery.c?
This is the command I'm currently using to build my_tests.cpp:
g++ -m32 -I ../cmockery-0.1.2 -c my_tests.cpp -o $(obj_dir)/my_tests.o
I undersatand it's not your code, but it looks like the easier way is probably to fix cmockery.h by removing this cast to (void*) (may be putting some section active only for C++ using #ifdef __cplusplus).
that could even be put in your code, just redefining the expect_string_count macro
#ifdef __cplusplus
#undef expect_string_count
#define expect_string_count(function, parameter, string, count) \
_expect_string(#function, #parameter, __FILE__, __LINE__, string, \
count)
#endif
I don't think there's an option for this at the compiler level. You might be able to work around this (I assume you want to avoid modifying the CMockery sources because of comments I've read in another of your questions) by having wrapper header for CMockery that does something like the following to make it play nicely in both C and C++:
#ifndef MY_CMOCKERY_H
#define MY_CMOCKERY_H
/*
A wrapper for cmockery.h that makes it C++ friendly while keeping things
the same for plain-old C
*/
#if __cplusplus
extern "C" {
#endif
#include "cmockery.h"
#if __cplusplus
}
#endif
#if __cplusplus
// fixup CMockery stuff that breaks in C++
#undef expect_string_count
#define expect_string_count(function, parameter, string, count) \
_expect_string(#function, #parameter, __FILE__, __LINE__, (char*)string, \
count)
#endif
#endif /* MY_CMOCKERY_H */
An additional benefit is that now you've got a place that you can put any other hacks/fixes for CMockery under C++ you come across (hopefully not too many of them).
If you're up for modifying the CMockery stuff that's probably where it really belongs - maybe the maintainers would accept your patch? (I dunno).