I've inherited some code that causes an amazing amount of compiler warnings and am not a strong enough C++ programmer to figure out how to address it properly. To wit:
log.h:
#include <stdarg.h>
#include <libgen.h>
#include <errno.h>
#include <string.h>
void log(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
#define error(fmt, ...) ow_log("ERROR[%s:%s()#%d]: " fmt, basename(__FILE__), __func__, __LINE__, ##__VA_ARGS__)
#define sys_error(fmt, ...) ow_log("ERROR[%s:%s()#%d]: System error: %s:" fmt, basename(__FILE__), __func__, __LINE__, strerror(errno), ##__VA_ARGS__)
log.cpp
#include <stdarg.h>
#include <stdio.h>
#include <pthread.h>
void log(const char* fmt, ...)
{
time_t t = time(NULL);
struct tm tm = *localtime(&t);
printf("%04d-%02d-%02d %02d:%02d:%02d ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
printf("\n");
fflush(stdout);
va_end(args);
}
Example use:
int args = 6;
log("Some string not explicitly typed with %i interpolated arguments", args);
error("Something went wrong here");
C++ programmers probably immediately see it, but I can't. What I get whenever this is used is:
log.h:11:79: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
#define error(fmt, ...) log("ERROR[%s:%s()#%d]: " fmt, basename(__FILE__), __func__, __LINE__, ##__VA_ARGS__)
^
It looks like the code has been this way for some time, and I don't see any terribly bad ill effects, but it makes reading the compiler output quite difficult (and seems actually wrong), so I tried to fix it. I tried some different casting strategies but nothing seems to work, I think I'm misunderstanding how macros work and all the builtins that it uses.
According to this page the prototype for basename() is
char *basename(char *path);
and the documentation says:
The basename() function may modify the string pointed to by path, and may return a pointer to static storage that may then be overwritten by a subsequent call to basename().
Therefore you should not pass a string literal to this function.
The function you should be using is
char * basename_r(const char *path, char *bname);
where bname is scratch storage for the function to use.
Related
I want to customize fprintf function for enable/disable Logging
My project use Makefile.
#define ENABLE_LOG
void log(fmt, ...){
#ifdef ENABLE_LOG
va_list vargs;
va_start(vargs, fmt);
fprintf(stderr, vargs);
va_end(vargs);
#endif
}
log("Test"); # build error
log("TEST %d", 1); # build error
How I can create custom log function that can enable/disable with ENABLE_LOG
You need to use vfprintf(). Also, I would make an empty macro that would avoid a call to an empty function.
#define ENABLE_LOG
#ifdef ENABLE_LOG
void log(const char *fmt, ...) {
va_list vargs;
va_start(vargs, fmt);
vfprintf(stderr, fmt, vargs);
va_end(vargs);
}
#else
#define log(fmt, ...)
#endif
Also, note that there is already a C++ function named log. You might consider a different name (especially if you use the dreaded using namespace std; - even though your code looks like pure C.)
I've been searching around on the web for a while on how to output a integer or optionally a float using OutputDebugString().
It would be easier if i could write my debug data straight to the console from my external executable using this command. However i only got it to work with a const char.
The solutions i found were outdated i tried copy and pasting the code straight from the web but didn't work. Even after modifying the code i couldn't get it to typecast correctly.
Is there anyone that could help me typecast something into the OutputDebugString as clean as possible, it's for debugging purposes only so i rather keep the code short and easily readable than having a more complex and clunky typecast IF that is possible. Many thanks!
Provided alternatively solution. The function below encapsulates OutputDebugString that can accept formatted arguments.
#include <vector>
#include <string>
void DbgMsg(const char * zcFormat, ...)
{
// initialize use of the variable argument array
va_list vaArgs;
va_start(vaArgs, zcFormat);
// reliably acquire the size
// from a copy of the variable argument array
// and a functionally reliable call to mock the formatting
va_list vaArgsCopy;
va_copy(vaArgsCopy, vaArgs);
const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy);
va_end(vaArgsCopy);
// return a formatted string without risking memory mismanagement
// and without assuming any compiler or platform specific behavior
std::vector<char> zc(iLen + 1);
std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
va_end(vaArgs);
std::string strText(zc.data(), iLen);
OutputDebugStringA(strText.c_str());
}
For example, the code below shows how to print an integer variable by OutputDebugString through DbgMsg().
int foo=12;
DbgMsg(" foo=%d", foo);
OutputDebugString can only take strings, if you want formatted output you will have to do that yourself before feeding it to OutputDebugString. If you are using MSVC I suggest that you use _CrtDbgReport or _CrtDbgReportW. With recent versions of MSVC that support variadic macros I use the following:
#if !defined(_RPTW)
#if defined(_DEBUG)
#define _RPTW(pszFmt, ...) _CrtDbgReportW(_CRT_WARN, NULL, __LINE__, NULL, (pszFmt), __VA_ARGS__)
#define _RPTW_(dest, fmt, ...) _CrtDbgReportW((dest), NULL, __LINE__, NULL, (pszFmt), __VA_ARGS__)
#else
#define _RPTW(pszFmt, ...)
#define _RPTW(dest, pszFmt)
#endif
#endif // #if !defined(_RPTW)
#if !defined(_RPTA)
#if defined(_DEBUG)
#define _RPTA(pszFmt, ...) _CrtDbgReport(_CRT_WARN, NULL, __LINE__, NULL, (pszFmt), __VA_ARGS__)
#define _RPTA_(dest, fmt, ...) _CrtDbgReport((dest), NULL, __LINE__, NULL, (pszFmt), __VA_ARGS__)
#else
#define _RPTA(pszFmt, ...)
#define _RPTA_(dest, pszFmt)
#endif
#endif // #if !defined(_RPTA)
#if !defined(_RPTT)
#if defined(_UNICODE)
#define _RPTT _RPTW
#define _RPTT_ _RPTW_
#else
#define _RPTT _RPTA
#define _RPTT_ _RPTA_
#endif
#endif // #if !defined(_RPTT)
The second forms allow providing a different level of report (_CRT_ASSERT or c_CRT_ERROR instead of _CRT_WARN)
I would recommend to use sprintf like that:
// issues with the int x, let's output and look at it in DebugView
char msg[255] = {0};
sprintf(msg, ">> Watch out x=%d\n", x);
OutputDebugString(msg);
Maybe I did not understand the question, but that's how I quickly dump integers. And to be honest I do not know these MACROS which SoronelHaetir listed but indeed you've got to format yourself. So I Hope this helps and is rather straight forward.
sprintf is an API provided by platform. I want to filter some format when it is used. My idea is:
#include <stdio.h>
int my_sprintf(...)
{
my_filter_function(...);
return ::sprintf(...);
}
#define sprintf my_sprintf
Then put these code in pch.
But I am still worrying it can't cover all usages, some one is in prebuilt library and not every project has a pch. Do you have any other idea?
Thanks. It's on windows.
You can't "overwrite" a built-in function. Furthermore, using a macro to replace its name results in your program having undefined behaviour.
So, don't even try to change the behaviour of the standard library. Really, that way madness lies.
Just call my_sprintf from your own code and let the platform do what it always did.
You want to use variadic functions.
Example:
int my_sprintf(char *buffer, char *fmt, ...)
{
int ret;
va_list args;
va_start(args, fmt);
/* insert your filter here */
/* you CAN NOT re-use a va_list variable after being used */
ret = vsprintf(buffer, fmt, args);
va_end(args);
return ret;
}
Note: You are not allowed to define a function / macro with the same name as a function from the standard library. It's undefined behaviour.
You have to replace all your calls to sprintf with your custom my_sprintf function.
You can use namespace concept to define functions with the same names
#include <stdio.h>
namespace myns
{
int sprintf(...)
{
my_filter_function(...);
return ::vsprintf(...);
}
}
than call
char buffer[256];
myns::sprintf(buffer, "Hello, %s!\n", "World");
There is a debug class that is used to print out debug statements based on a level. The higher the level, the more detailed messages you would get. The problem occurs when I am trying to do print out a uint64_t. On different platforms using printf style it could be either %li or %lli. I want it to become portable. However, based on this, I could include a PRId64 and it would adjust accordingly. The problem is because my messages are customized, it takes in a const char *. I am unable to do with with a message such as "Calling based on pts %"PRId64, mv.pts because it is expecting a char * where there isn't one. What are other approaches in solving this?
Inside the main code
#include "debug.h"
#include <inttypes.h>
#define P_DBG(level, format, ...) DBGPRINT(level, ("%-5d%s::%s() - " format "\n", __LINE__, str().c_str (), __FUNCTION__, __VA_ARGS__))
#define P_ALL(format, ...) P_DBG(P_ALWAYS, format, __VA_ARGS__)
class Something
{
Something::Print(uint64_t a) {
P_ALL("Calling based on %"PRId64 "\n", a)
}
Inside debug.h
class vdebugPrint {
class vdebugPrint(int ID, int level) : v_id(ID), v_level(level) {}
int Print(const char* formatString, ...) __attribute__((__format(printf,2,3)));
}
...
#define VPRINT3(level, msg) VdebugPrint(ID, level).Print msg
#define PRINT(level, msg) VPRINT##level(level,msg)
#define DBGPRINT(level, msg) PRINT(level,msg)
...
Inside debug.cpp
...
VdebugPrint::Print(const char* format, ...)
{
va_list argumentList;
va_start(argumentList, formatString);
vprintf(formatString, argumentList);
va_end(argumentList);
}
The exact error message is
error: expected ‘)’ before ‘PRId64’
AV_TRACE("Calling based on %"PRId64"\n", mv.pts);
^
/.../debug.h:140:94: note: in definition of macro ‘VPRINT3’
#define VPRINT3(level, msg) VdebugPrint(ID, level).DebugPrint msg
/.../debug.h:146:45: note: in expansion of macro ‘DBGPRINT’
#define DBGPRINT(level, msg) PRINT(level, msg)
^
/.../file.h:54:41: note: in expansion of macro ‘DBGPRINT’
#define P_PBD(level, format, args...) DBGPRINT(level, ("%-5d%s::%s() - " format "\n", __LINE__, str().c_str(), __FUNCTION__, ##args))
EDIT:
I've attempted Olaf suggestion to no avail. Here is what I've done. Everything else is the same except for the main code.
...
class Something
{
Something::Print(uint64_t a) {
const char* message = "Calling based on %";
size_t mlen = strlen(message);
char buf[mlen + strlen(PRId64) + 1];
strcpy(buf, message);
strcpy(buf + mlen, PRId64);
P_ALL(buf, a);
}
}
The error that I get is
error: ‘PRId64’ was not declared in this scope
char buf[mlen + strlen(PRId64) + 1];
^
EDIT2: So I fixed the problem with it not finding PRId64. I had to define __STDC_FORMAT_MACROS from here before include inttypes. This now solves the problem and it compiles!
The macros defined in inttypes.h are string literals with the proper printf type specifier. So you have to append this to the format string. Normal usage would be to use string literal concatenation.
If your format string is variable, you have to append the specifier to the string:
char buf[strlen[format] + strlen[PRId64] + 1];
strcpy(buff, format);
strcat(buff, PRId64);
Note this could be optimized using strcpy for both:
size_t flen = strlen(format);
char buf[flen + strlen[PRId64] + 1];
strcpy(buff, format);
strcpy(buff + flen, PRId64);
Caution: The format string has to be NUL-terminated!
Update:
The original answer was for C as that was one of the language-tags and the code looked more like C than C++. However, according to comments, you can use this also for C++11 (check your compiler's support), if you #define __STDC_FORMAT_MACROS before #include <inttypes.h>. (Note the include might appear in other headers, so you should define the macro as early as possibly in your file.)
You need spaces between the adjacent string literals and the macros for the expansion and concatenation to work. For example:
// Note the space on either side of PRId64
AV_TRACE("Calling based on %" PRId64 "\n", mv.pts);
(I don't know if this is required according to the standard, but it is required to make it work for some preprocessors I've used.)
I have a task at work to make the static library more secure , in order to do so I need to replace the formatted strings of printf for them to appear differently in the compiled static library , for this to happen it must be done in the pre processor stage .
What I did (and it actually works in visual studio) is the following ( and it is just a pseudo example ):
char * my_array[] = {"abcd", "a %d", " b %d %s "};
#define GENERIC_ARRAY(x) my_array[x]
#define VARIADIC_DEBUG_PRINT(...) DebugPrintFunction (__FILE__, __LINE__, __func__, __VA_ARGS__)
#define PRINT_BY_LEVEL(x) VARIADIC_DEBUG_PRINT x
#define REPLACE_STRING(x,...) PRINT_BY_LEVEL((GENERAL_LEVEL,GENERIC_ARRAY(__COUNTER__),__VA_ARGS__))
#define MY_PRINTF(x,...) REPLACE_STRING((void*)0,(void*)0,__VA_ARGS__)
All of this overhead is for me to trick the compiler to accept prints without any arguments except the string
So when testing it in my main.c I tried the following And it worked :
MY_PRINTF("Hello World");
MY_PRINTF("My Val %d", i);
MY_PRINTF("MY VAL %d My String %s", i, s);
But when switching to GCC, he does not like the format of the first print i.e :
MY_PRINTF("Hello World");
And throws me an compilation error :
error: expected expression before ')' token
Any ideas how may I trick the compiler and accept it ? or maybe better ideas how to rename the string safely after compilation ?
You can try something like :
#include <stdio.h>
#define PRINT(x, ...) printf(x, ##__VA_ARGS__)
int main (int argc, char *argv[]) {
PRINT("Hello\n");
PRINT("World %d\n", 42);
return 0;
}
It works with gcc 4.8 (not tried with earlier version, but it should work too)
Using ##__VA_ARGS__, you may try:
#define MY_PRINTF(x, ...) \
VARIADIC_DEBUG_PRINT(GENERAL_LEVEL, GENERIC_ARRAY(__COUNTER__), (void*)0, (void*)0, ##__VA_ARGS__)