I am trying to use variadic macros. I want PRINTF macro to be called from MY_TOP_PRINTF macro.
1.Either I am getting compilation error.
2.If I remove compilation error, it is only printing 1st argument.
My expected result is Inside TOP_PRINT dog here 4
#include <iostream>
#include <cstdio>
using namespace std;
#define PRINTF(str, ...) { \
fprintf(stderr, (str), ##__VA_ARGS__); \
}
#define MY_TOP_PRINTF(EXPRESSION, ...) { \
PRINTF("Inside TOP_PRINT ", EXPRESSION, __VA_ARGS__);\
}
int main()
{
int x = 4;
char str[255] = "dog here";
MY_TOP_PRINTF(str,x);
return 0;
}
Error:
hello_temp.cpp: In function ‘int main()’:
hello_temp.cpp:8:41: warning: too many arguments for format [-Wformat-extra-args]
fprintf(stderr, (str), ##__VA_ARGS__); \
^
hello_temp.cpp:13:5: note: in expansion of macro ‘PRINTF’
PRINTF("Inside TOP_PRINT", EXPRESSION, __VA_ARGS__);\
^
hello_temp.cpp:23:2: note: in expansion of macro ‘MY_TOP_PRINTF’
MY_TOP_PRINTF(str,x);
Related
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.
How can I write a macro that does different things based on the type of its arguments?
I have a macro that needs to handle an argument that can have one of two types.
#include <typeinfo>
enum class Coolness { kUndefined, kUncool, kCool };
enum class Tallness { kUndefined, kShort, kTall };
void MakePerson (Coolness coolness, Tallness tallness) {}
// Provide a way to make a person by only providing Coolness or Tallness.
#define MAKE_PERSON(x) \
({ \
if (typeid(x) == typeid(Coolness)) { \
MakePerson(((x)), Tallness::kUndefined); \
} else { \
MakePerson(Coolness::kUndefined, (x)); \
} \
})
int main()
{
MAKE_PERSON(Coolness::kUncool);
MAKE_PERSON(Tallness::kTall);
}
(We could use default arguments here, but in the real code we effectively must use a macro.)
The compiler throws an error on both calls in main:
main.cpp: In function ‘int main()’:
main.cpp:23:43: error: cannot convert ‘Coolness’ to ‘Tallness’ for argument ‘2’ to ‘void MakePerson(Coolness, Tallness)’
MakePerson(Coolness::kUndefined, (x)); \
^
main.cpp:29:3: note: in expansion of macro ‘MAKE_PERSON’
MAKE_PERSON(Coolness::kUncool);
^~~~~~~~~~~
main.cpp:21:45: error: cannot convert ‘Tallness’ to ‘Coolness’ for argument ‘1’ to ‘void MakePerson(Coolness, Tallness)’
MakePerson(((x)), Tallness::kUndefined); \
^
main.cpp:30:3: note: in expansion of macro ‘MAKE_PERSON’
MAKE_PERSON(Tallness::kTall);
^~~~~~~~~~~
(done on https://www.onlinegdb.com/online_c++_compiler)
We can't use __builtin_types_compatible_p as in this question because our compiler doesn't have that.
How can I write a macro that does different things based on the type of its arguments?
Use simple function overloading, don't try to make the macro smarter than it needs to be:
enum class Coolness { kUndefined, kUncool, kCool };
enum class Tallness { kUndefined, kShort, kTall };
void MakePerson (Coolness coolness, Tallness tallness)
{
...
}
inline void MakePerson (Coolness coolness)
{
MakePerson(coolness, Tallness::kUndefined);
}
inline void MakePerson (Tallness tallness)
{
MakePerson(Coolness::kUndefined, tallness);
}
#define MAKE_PERSON(x) \
{ \
// use __FILE__ and __LINE__ as needed... \
MakePerson(x); \
}
int main()
{
MAKE_PERSON(Coolness::kUncool);
MAKE_PERSON(Tallness::kTall);
}
Live Demo
Other suggestions are welcome, but what we eventually did was use a static_cast to tell the compiler what type the arguments are:
#include <typeinfo>
enum class Coolness { kUndefined, kUncool, kCool };
enum class Tallness { kUndefined, kShort, kTall };
void MakePerson (Coolness coolness, Tallness tallness) {}
// Provide a way to make a person by only providing Coolness or Tallness.
// Static cast is used because the compiler fails to typecheck the
// branches correctly without it.
#define MAKE_PERSON(x) \
({ \
if (typeid(x) == typeid(Coolness)) { \
MakePerson(static_cast<Coolness>((x)), Tallness::kUndefined); \
} else { \
MakePerson(Coolness::kUndefined, static_cast<Tallness>((x))); \
} \
})
int main()
{
MAKE_PERSON(Coolness::kUncool);
MAKE_PERSON(Tallness::kTall);
}
...Program finished with exit code 0
I am currently adapting a Windows C++ project to make it work on Linux.
I defined several macros to print formatted lines to a log file.
They are printf-like so I can write this:
WARN("%d::%s<", 42, "baz");
It's pretty easy to print something like:
[thread_id][WARN][/path/to/main.cpp:15][Fri 03/01/2019
10:38:54.408][this_value] 42::baz<
this_value is value of this or NULL if this is not defined (static function, extern "C" function).
My current code is:
#if defined(_WIN32) && !defined(__INTELLISENSE__)
#define SET_ZIS __if_exists (this) { zis = this; }
#else
#define SET_ZIS
#endif
#define _LOG(...) \
do \
{ \
void *zis = NULL; \
SET_ZIS \
GetLoggerInstance()->logMessage(__VA_ARGS__); \
} while(0)
#define LOG(...) _LOG(level, __FILE__, __LINE__, __func__, zis, __VA_ARGS__)
#define WARN(...) LOG(ILogger_level::LEVEL_WARN, __VA_ARGS__)
Is there a standard way to detect if this exists?
Maybe using std::is_* or a SFINAE trick ?
I use extern-ed "C" functions to construct objects ("this" is meaningless) and call members on instanciated objects ("this" is meaningful). "Constructors" are exported in a shared object and dynamically consumed by a C++ project. Doing it that way, I don't have to manage mangled names.
extern "C" int CreateMyClass(std::shared_ptr<MyClass> *newClass);
int CreateMyClass(std::shared_ptr<MyClass> *newClass)
{
RELAY("(%p)", newClass);
*newClass = std::make_shared<MyClass>(42, "baz");
return 0;
}
MyClass::MyClass(int a, char *b)
{
RELAY("(%d,%s)", a, b);
}
EDIT: Here's a simple test case:
#include <memory> /* For std::shared_ptr */
#define RELAY(...) printf("[%p][%s]\n", this, __func__)
class MyClass
{
public:
MyClass(int a, const char *b);
static void test();
};
extern "C" int CreateMyClass(std::shared_ptr<MyClass> *newClass);
int CreateMyClass(std::shared_ptr<MyClass> *newClass)
{
RELAY("(%p)", newClass);
*newClass = std::make_shared<MyClass>(42, "baz");
return 0;
}
MyClass::MyClass(int a, const char *b)
{
RELAY("(%d,%s)", a, b);
}
void MyClass::test()
{
RELAY("()");
printf("some work");
}
int main(int argc, char **argv)
{
std::shared_ptr<MyClass> newClass;
int ret = CreateMyClass(&newClass);
MyClass::test();
return ret;
}
g++ gives the following errors:
test.c: In function ‘int CreateMyClass(std::shared_ptr<MyClass>*)’:
test.c:2:41: error: invalid use of ‘this’ in non-member function
#define RELAY(...) printf("[%p][%s]\n", this, __func__)
^
test.c:14:3: note: in expansion of macro ‘RELAY’
RELAY("(%p)", newClass);
^~~~~
test.c: In static member function ‘static void MyClass::test()’:
test.c:2:41: error: ‘this’ is unavailable for static member functions
#define RELAY(...) printf("[%p][%s]\n", this, __func__)
^
test.c:26:3: note: in expansion of macro ‘RELAY’
RELAY("()");
^~~~~
CreateMyClass is not static ("non-member function"), so this is unavailable. Same thing for the static function.
The this reference only exists and always exists inside the non-static member functions of a c++ class/struct. It's a pointer to the memory address of the instance of the class a function is operating on. As far as logging is concerned, I'm not sure how you'd use that aside from digging through a memory dump, and I'm not 100% sure that the instance address would even be useful for that.
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.
using namespace std;
#ifdef DEBUG
#define debug(args...) {dbg,args; cerr<<endl;}
#else
#define debug(args...) // Just strip off all debug tokens
#endif
struct debugger
{
template<typename T> debugger& operator , (const T& v)
{
cerr<<v<<" ";
return *this;
}
} dbg;
int main(){
int a=1,b=2,c=3;
debugger(a,b,c);
}
I found this debug macro and I am trying to use this but this isn't working. I am getting following error:
ubuntu:~ g++ -DEBUG a.cpp -o a
a.cpp: In function ‘int main()’:
a.cpp:81:16: error: no matching function for call to ‘debugger::debugger(int&, int&, int&)’
a.cpp:81:16: note: candidates are:
a.cpp:62:8: note: debugger::debugger()
a.cpp:62:8: note: candidate expects 0 arguments, 3 provided
a.cpp:62:8: note: debugger::debugger(const debugger&)
a.cpp:62:8: note: candidate expects 1 argument, 3 provided
You can simply try by using:-
debug(a, b, c);
Also you have to change the command line -DDEBUG -- the -D is "define". Presently you're defining "EBUG".