Missing warning about format specifier in variadic template context - c++

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.

Related

Is there any difference between the vsnprintf function under xcode and the vsnprintf of other platforms?

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);
}

C/C++ error: expected initializer before defined function

I am porting Einstein#Home (Radio Pulsar Edition) to Solaris and am stuck on erp_execinfo_plus.c that reports the error below.
builder#bertha:~/brp/src$ g++ -g -I/export/home/builder/brp/install/include/libxml2 -I/export/home/builder/brp/install/include/boinc -I/usr/gnu/include -I/export/home/builder/brp/install/include -DHAVE_INLINE -DBOINCIFIED -DUSE_CPU_RESAMP -DUSE_FFTW_FFT -DNDEBUG -DLOGLEVEL=info -ggdb3 -rdynamic -O3 -Wall -fprofile-use -c /export/home/builder/brp/src/erp_extract.c
/export/home/builder/brp/src/erp_extract.c:8:12: error: expected initializer before 'VPARAMS'
void fatal VPARAMS ((const char *format, ...)) {
^~~~~~~
/export/home/builder/brp/src/erp_extract.c:16:16: error: expected initializer before 'VPARAMS'
void non_fatal VPARAMS ((const char *format, ...)) {
^~~~~~~
I have tried numerous iterations to correct the issue by moving code blocks around, redefining them as a struct and overloading. I have even used gcc and the gcc/g++ preprocessor outputs to find the error to no avail.
gcc:
/export/home/builder/brp/src/erp_extract.c:16:16: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'VPARAMS'
NOTES:
The erp_extract.c file is the minimum source code to reproduce the error.
The #include is the default binutils sysdef.h
erp_extract.c
#include "sysdep.h"
static FILE *ostream; /* output stream */
void fatal(const char *, ...) ATTRIBUTE_PRINTF_1 ATTRIBUTE_NORETURN;
void non_fatal(const char *, ...) ATTRIBUTE_PRINTF_1;
void report(const char *, va_list) ATTRIBUTE_PRINTF(1,0);
void fatal VPARAMS ((const char *format, ...)) {
VA_OPEN (args, format);
VA_FIXEDARG (args, const char *, format);
report(format, args);
VA_CLOSE (args);
}
void non_fatal VPARAMS ((const char *format, ...)) {
VA_OPEN (args, format);
VA_FIXEDARG (args, const char *, format);
report(format, args);
VA_CLOSE (args);
}
void report(const char * format, va_list args)
{
vfprintf(ostream, format, args);
putc ('\n', ostream);
}

cppcheck format string on member functions of C++

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

Log(PCTSTR format,...) and Log(PCTSTR text): error C2668 ambiguous call to overloaded function

I have the following defined:
void LogMessage(PCTSTR text);
void LogMessage(PCTSTR format, ...);
If I just want to call the function with one parameter, I get the following error message:
Source.cpp(10): error C2668: 'Log' : ambiguous call to overloaded function
could be 'void Log(PCTSTR,...)' or 'void Log(PCTSTR)'
while trying to match the argument list '(const wchar_t [42])'
Is it possible to do a static_cast to explizite use the first version? Or haw can this be solved, except by renaming the first or second function?
How about the following? I haven't tested on VC++ (which seems to be your platform of choice) but hopefully the version you are using implements enough C++11 for this to work.
#include <iostream>
#include <cstdio>
#include <cstdarg>
void LogMessageWorker(char const* format, ...)
{
// 1k should be enough for anyone... ;)
char buf[1024] = { 0 };
// The version of vsnprint called should always null terminate correctly and doesn't
// strictly need the -1 but I believe that the implementation that is included with
// VC++ leaves a lot to be desired so you may need to slightly tweak this.
va_list args;
va_start (args, format);
vsnprintf (buf, sizeof (buf) - 1, format, args);
va_end (args);
std::cout << "LogMessage: " << buf << std::endl;
}
template <class... Arguments>
void LogMessage(char const* format, Arguments... arguments)
{
LogMessageWorker (format, std::forward<Arguments>(arguments)...);
}
void LogMessage(char const* text)
{
LogMessageWorker ("%s", text);
}
int main(int argc, char **argv)
{
LogMessage ("The test is starting...");
for (int i = 0; i < 3; i++)
LogMessage ("This is test #%d", i);
LogMessage ("This contains the % character and still it works (%d-%d-%d-%d)");
return 0;
}

On using __attribute__((format(printf,

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)));
};