I'm trying to suppress -Wformat-nonliteral warnings. I have used attribute((format(printf with success elsewhere but the following example eludes me.
exceptions.hpp
class Exceptions {
...
static void fthrow(Thread* thread, const char* file, int line, Symbol* name,
const char* format, ...);
};
exceptions.cpp
__attribute__((format(printf, 5, 6)))
void Exceptions::fthrow(Thread* thread, const char* file, int line, Symbol* h_name, const char* format, ...) {
const int max_msg_size = 1024;
va_list ap;
va_start(ap, format);
char msg[max_msg_size];
vsnprintf(msg, max_msg_size, format, ap);
msg[max_msg_size-1] = '\0';
va_end(ap);
_throw_msg(thread, file, line, h_name, msg);
}
result
exceptions.cpp:229:16: error: format argument not a string type
__attribute__((format(printf, 5, 6)))
^ ~
exceptions.cpp:235:32: error: format string is not a string literal [-Werror,-Wformat-nonliteral]
vsnprintf(msg, max_msg_size, format, ap);
^~~~~~
Since it is a static, the exact index should be used, but I've tried 4, 5 and 6, 7 (if out by one) with similar fail.
The attribute should be set on the declaration of the function, not the definition.
class Exceptions {
...
static void fthrow(Thread* thread, const char* file, int line, Symbol* name,
const char* format, ...) __attribute__((format(printf, 5, 6)));
};
Related
I wrote a very simple test code to test vsnprintf, but in xcode and visual studio environment, the results are very different. The test code is as follows:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string.h>
#include <cstdarg>
void p(const char* fmt, ...)
{
static const int DefaultLength = 256;
char defaultBuf[DefaultLength] = { 0 };
va_list args;
va_start(args, fmt);
vsprintf(defaultBuf, fmt, args);
printf("%s\n", defaultBuf);
memset(defaultBuf, 0, sizeof(defaultBuf));
vsnprintf(defaultBuf, DefaultLength, fmt, args);
printf("%s\n", defaultBuf);
va_end(args);
}
int main(int argc, const char* argv[])
{
// if you uncomment this line(std::cout ...), it will crash at vsnprintf in xcode
std::cout << "Tests...!\n";
p("Create:%s(%d)", "I'm A String", 0x16);
return 0;
}
this is the output in visual studio :
Tests...!
Create:I'm A String(22)
Create:I'm A String(22)
This is normal and doesn't seem to be a problem. But the same code, I created a macos command line project, pasted this code in, something strange happened, when the code is executed to vsnprintf, it will start EXC_BAD_ACCESS directly.
What's more outrageous is that if I comment out the std::cout in the main function, it will not crash, but the output is wrong. So the question is, what is the reason for this difference, shouldn't these functions all be functions of the C standard library, and their behavior should be constrained by the standard library? Or is my usage wrong?
the output when i delete std::cout:
Create:I'm A String(22)
Create:\310\366\357\277\367(3112398)
Program ended with exit code: 0
If only one is right, is xcode right or visual studio is right, in the end I used is the latest xcode 14, visual studio 2022。
You must reinitialize the va_list between calls to vsprintf. Failure to do so is undefined behavior.
See https://en.cppreference.com/w/c/variadic/va_list :
If a va_list instance is created, passed to another function, and used via va_arg in that function, then any subsequent use in the calling function should be preceded by a call to va_end.
void p(const char* fmt, ...)
{
static const int DefaultLength = 256;
char defaultBuf[DefaultLength] = { 0 };
va_list args;
va_start(args, fmt);
vsprintf(defaultBuf, fmt, args);
va_end(args);
printf("%s\n", defaultBuf);
memset(defaultBuf, 0, sizeof(defaultBuf));
va_start(args, fmt);
vsnprintf(defaultBuf, DefaultLength, fmt, args);
va_end(args);
printf("%s\n", defaultBuf);
}
void p(const char* fmt, ...)
{
static const int DefaultLength = 256;
char defaultBuf[DefaultLength] = { 0 };
va_list args;
va_start(args, fmt);
va_list args_r;
va_copy(args_r, args);
vsprintf(defaultBuf, fmt, args);
printf("%s\n", defaultBuf);
memset(defaultBuf, 0, sizeof(defaultBuf));
vsnprintf(defaultBuf, DefaultLength, fmt, args_r);
printf("%s\n", defaultBuf);
va_end(args_r);
va_end(args);
}
I have a printf-style function that takes a variable number of arguments. Here is my starting point:
#include <stdio.h>
#include <stdarg.h>
void MyPrint (const char* fmt,...)
{
va_list arglist ;
va_start (arglist, fmt) ;
vprintf (fmt, arglist) ;
va_end (arglist) ;
}
int main()
{
MyPrint ("Hello, %s\n", "world") ;
}
This prints Hello, world as expected.
Now I want to make two changes. First, I want to check the format string using the format attribute of g++. So I declare the MyPrint function first (I have to declare it first, because for some reason g++ doesn't let you assign attributes to a function definition):
void MyPrint (const char* fmt,...) __attribute__ ((format (printf, 1, 2))) ;
Now if I try e.g. MyPrint ("Hello, %d\n", "world") ; I get a nice error message.
The second change I want to make is to use a variadic template parameter. Like this:
#include <utility> // for std::forward
template<typename...Params>
void MyPrint (Params&&... fmt)
{
printf (std::forward<Params> (fmt)...) ;
}
This works too. So I combine the two, by adding the format-checking attribute to the variadic function template with this forward declaration:
template<typename...Params>
void MyPrint (Params&&... fmt) __attribute__ ((format (printf, 1, 2))) ;
But now I get this error message (gcc 10.2):
<source>: In substitution of 'template<class ... Params> void MyPrint(Params&& ...) [with Params = {const char (&)[11], const char (&)[6]}]':
<source>:15:38: required from here
<source>:8:6: error:
'format' attribute argument 2 value '1' refers to parameter type
'const char (&)[11]'
This has got me completely baffled. Can anybody tell me what I'm doing wrong?
Here is the complete program:
#include <stdio.h>
#include <utility> // for std::forward
template<typename...Params>
void MyPrint (Params&&... fmt) __attribute__ ((format (printf, 1, 2))) ;
template<typename...Params>
void MyPrint (Params&&... fmt) // <-- Line 8
{
printf (std::forward<Params> (fmt)...) ;
}
int main()
{
MyPrint ("Hello, %s\n", "world") ; // <-- Line 15
}
You can make the first error go away by adding a fixed const char * argument as the format string and pointing the attribute to that.
template<typename...Params>
void MyPrint (const char * format, Params&&... fmt) __attribute__ ((format (printf, 1, 2))) ;
template<typename...Params>
void MyPrint (const char * format, Params&&... fmt) // <-- Line 9
{
printf (format, std::forward<Params> (fmt)...) ;
}
Which reveals another error:
test.cc:8:6: error: ‘format’ attribute argument 3 value ‘2’ does not refer to a variable argument list
8 | void MyPrint (const char * format, Params&&... fmt) // <-- Line 9
| ^~~~~~~
It seems that the attribute for checking the printf archetype relies on one const char * argument and a variable argument list and is not willing to work without them. So you have to give up either the C++ template magic or the compile-time format string checking.
aa.h
#ifndef __US_LOG_FILEA_H_
#define __US_LOG_FILEA_H_
namespace AA{
class A{
public:
A();
~A();
static A& Ins(){
static A obj;
return obj;
}
void do_p(const char *cat, int level, const char *Format ...); // ok
void do_p(const char *cat, int level, const char *Format, ...); // error
};
} // namespace AA
extern AA::A g_A;
#endif // __US_LOG_FILEA_H_
formatstr.cpp
void test()
{
g_A.do_p("global func", 2, "%s\n", str);
}
a.cfg:
<?xml version="1.0"?>
<def>
<function name="AA::A::do_p">
<noreturn>false</noreturn>
<leak-ignore/>
<formatstr type="printf"/>
<arg nr="3">
<formatstr/>
<not-uninit/>
</arg>
</function>
</def>
cppcheck --enbale-style --library=a.cfg formatstr.cpp
if void do_p(const char *cat, int level, const char *Format ...); cppcheck output:
warning: %s in format string (no. 1) requires 'char *' bu
t the argument type is 'std::string'. [invalidPrintfArgType_s]
g_A.do_p("global func", 2, "%s\n", str);
but do_p(const char *cat, int level, const char *Format,...); cppcheck output nothing
WHY?
The Cppcheck's version is 1.89.0.0
Thanks in advance.
WHY?
void do_p(const char *cat, int level, const char *Format ...); // ok
Presumably because cppcheck doesn't recognise const char *Format ... as a printf format and variadic arguments unless they are separated by comma, so you didn't get the error.
void do_p(const char *cat, int level, const char *Format, ...); // error
You configured cppcheck to check bad format / argument pairing, so this is where you should expect an error.
1st, modify the Cpp member function code to C-style function
2nd, remove overload funcions
The following code produces warning about wrong format specifier in all major compilers
std::wstring ws = L"some example string";
char buff[100];
sprintf(buff, "%s", ws.c_str());
The warning disappears if we use it in 'variadic' context (variadic template or va_list).
std::wstring ws = L"some example string";
void foo_va_list(const char* fmt, ...)
{
va_list argv;
va_start( argv, fmt );
char buff[1000];
vsprintf(buff, fmt, argv);
va_end(argv);
}
template<typename ... T>
void foo_variadic_template(const char* fmt, T && ... args)
{
char buff[100];
sprintf(buff, fmt, args...);
}
int main()
{
//this two should produce a warning but don't
foo_va_list("foo_va_list %s", ws.c_str());
foo_variadic_template("foo_variadic_template %s", ws.c_str());
char buff[100];
//this one produces warning as expected
sprintf(buff, "sprintf %s", ws.c_str());
}
The question is how to enable this warning in this context?
some compilers have an extension for the [s]printf functions and can "understand" the given format string and check the used format specifier against the used arguments.
Obviously these checks can not work if you hide this relation in your own implementation.
I have a class in my C++ code similar to the following:
class myClass
{
public:
void method1(int a, int b, std::string str);
};
Other classes can instantiate an object from myClass and call method1.
void caller()
{
obj1->method1(12, 4, "sample");
}
I want to log all the callers of myClass (function name, file name, line number). One possible solution is this:
class myClass
{
public:
method1(int a, int b, std::string str, const char *_function = __builtin_FUNCTION(), const char *_file = __builtin_FILE(), int _line = __builtin_LINE());
};
which is using __builtin_xxx as default arguments. This solution has multiple shortcomings:
It is an ugly solution
__builtin_xxx is only available in gcc version > 4.8
We have to add three default parameters to method1
IDE shows the default parameters on auto-completion that are not meant to be provided by the user!
Another solution is using __LINE__, __FILE__ and __func__ that is basically very similar to the previous solution. They are not defined outside of function scope, and they should be used like this:
void caller()
{
obj1->method1(12, 4, "sample", __func__, __FILE__, __LINE__);
}
Here is a working example for both solutions.
Is there any better solution to log the caller when the user calls method1 on myClass object. By a better solution I specifically mean not to change the method1's declaration by adding three more parameters!
Another ugly solution, but I'm using...
Use macros to automatically add __LINE__ __FILE__ ...etc. things into parameters.
For example
#define Method(param0,param1) Method(param0,param1,__LINE__)
It has a lot of problem, if you want macros work as normal function, you has to do a lot of things, and it still may not works.
I use it to help me log errors.
Looks like a duplicate of Print the file name, line number and function name of a calling function - C Prog
I'd pass the data to the function through parameters (maybe get the help of a macro)
int info(const char *fname, int lineno, const char *fxname, ...) { /* ... */ }
int debug(const char *fname, int lineno, const char *fxname, ...) { /* ... */ }
int error(const char *fname, int lineno, const char *fxname, ...) { /* ... */ }
And to call them
info(__FILE__, __LINE__, __func__, ...);
debug(__FILE__, __LINE__, __func__, ...);
error(__FILE__, __LINE__, __func__, ...);
Note: __func__ is C99; gcc, in mode C89 has __FUNCTION__