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.
Related
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__
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;
}
There is a function like:
int kvm_vcpu_ioctl(int vcpu_fd,int type, ...)
{
int ret;
void *arg;
va_list ap;
va_start(ap, type);
arg = va_arg(ap, void *);
va_end(ap);
ret = ioctl(vcpu_fd, type, arg);
if (ret == -1)
ret = -errno;
return ret;
}
and when I want to call it but pass a macro as the argument like:
kvm_vcpu_ioctl(vcpus.fds[vcpu_id],KVM_NITRO_GET_SREGS,sregs);
in which defined macro is:
#define KVM_NITRO_GET_SREGS _IOR(KVMIO, 0xE9, struct kvm_sregs)
I get this error:
Invalid arguments '
Candidates are:
int kvm_vcpu_ioctl(int, int, ...)
I do not why?
_IOR is :
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
and _IOC is:
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
Your definition of KVM_NITRO_GET_SREGS "calls" _IOR with a third argument of struct kvm_sregs. From the definition of _IOR, it would appear that perhaps a sizeof(struct kvm_sregs) might be more appropriate. Or perhaps you have arguments out of order, since it appears the first argument to _IOR should possibly be a type, but I'm not sure what KVMIO expands to...
Look for other locations where _IOR is used in other drivers, and mimic those.
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)));
};
I am using Qt creator, and it generates this error:
"warning: format '%s' expects argument of type 'char*',
but argument 2 has type 'const void*' [-Wformat]"
The console app is working, but I am interested if there is way to avoid this error, just interested
#include <iostream>
#include <stdio.h>
using namespace std;
bool isOpen(FILE *file);
void print(const void *text);
#define FILE_IS_OPEN "The file is now open"
int main()
{
FILE *f;
f = fopen("This.txt", "w");
if(isOpen(f))
print(FILE_IS_OPEN);
fclose(f);
return 0;
}
bool isOpen(FILE *file) { return file != NULL ? true : false; }
void print(const void *text) { printf("%s\n", text); }
Simply change
void print(const void *text);
to
void print(const char *text);
The same applies in function definition.
Format specifier %s indicates that you're trying to output a string to console, so it requires you to give the address of the string which is nothing but char * here,
you can cast it to (char *)text and pass it to printf.
Also you can't output the contents of a void * pointer they have to any of the primitive data types.