Question
I have a method to wrap functions by replacing them with a macro so that I can log the call and the return code. Here's an example which works:
int rc;
int foo(int a, int b);
int bar(int a, char *b, int *c);
void LogRet(char *fn, char *file, char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08x\n", file, from, ln, fn, ret);
}
#define foo(args, ...) (rc = (foo)(args, ##__VA_ARGS__), LogRet("foo", __FILE__, __FUNCTION__, __LINE__, rc), rc)
#define bar(args, ...) (rc = (bar)(args, ##__VA_ARGS__), LogRet("bar", __FILE__, __FUNCTION__, __LINE__, rc), rc)
The macro for the function it substitutes calls the function and logs the function name and where it was called from as well as the return code. Wrapping any function uses the same syntax and just needs the function name replaced in the macro 3 times. What I would like to do is create wrapper macro where the macro redefine for foo would be something similar to:
#define foo(args, ...) WRAPPPER(foo)
I understand the basics of stringify and double stringify but I can't even get the WRAPPER macro to do the real function call. Ideally, I'd like to get it down to single WRAP(foo) statement. The reason is, I have around 100 or more functions I'd like to wrap and it would like to do it simple from one simple force include file. I'm coming to the conclusion that it's not possible but I thought I'd ask here before abandoning the idea. I'm using clang and vc++ if that makes any difference but it would be nice to have this on any compiler as I debug a lot of different systems.
Adaptation of Jonathan Leffler's answer
As I'm new here I wasn't sure if this should be a separate answer or and edit update. This is essentially Jonathan Leffler's answer. It's in a functional example. While the 2 functions it calls are pointless, the goal was to have a macro that could wrap any function with any arg list. My main use is in logging the use flow in a large code library that has a problem. Also, my original sample has one glaring weakness. By storing the return code in a global it is not thread safe without cumbersome preparation in TLS. The global is now removed and the macro no longer uses the sequence operator to return the value, it is preserved and returned by the logging function. Also, as Augurar pointed out and shown in Jonathan's example. If the macro is declared in the same file as the function declaration, it requires parentheses.
#include <stdio.h>
#include <string.h>
int foo(int a, int b);
int bar(int a, char *b, int *c);
#if defined (_DEBUG) || defined (DEBUG)
// Short version of __FILE__ without path requires runtime parsing
#define __SFILE__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#ifdef WIN32
#define WRAPPER(func, ...) LogRet(#func, __SFILE__, __FUNCTION__, __LINE__, (func)(__VA_ARGS__))
#else
#define WRAPPER(func, ...) LogRet(#func, __SFILE__, __func__, __LINE__, (func)(__VA_ARGS__))
#endif
inline int LogRet(const char *fn, const char *file, const char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08x\n", file, from, ln, fn, ret);
return ret;
}
#define foo(...) WRAPPER(foo, __VA_ARGS__)
#define bar(...) WRAPPER(bar, __VA_ARGS__)
#endif
int main(void)
{
int x = foo(1, 2);
bar(2, "doubled", &x);
return 0;
}
#ifdef foo
#undef foo
#undef bar
#endif
// If and only if the function definition is in the same file with the macros, you must either undefine the macros or
// parenthesize the function e.g. int (foo)(int a, int b) { ... }
int foo(int a, int b)
{
printf("%d + %d = %d\n", a, b, a + b);
return a + b;
}
int (bar)(int a, char *b, int *c)
{
printf("%d %s = %d\n", *c, b, a * *c);
return *c * a;
}
Release build output:
1 + 2 = 3
3 doubled = 6
Debug build output:
1 + 2 = 3
test.cpp.main.35: foo() ret:00000003
3 doubled = 6
test.cpp.main.36: bar() ret:00000006
The main benefit is not having to find every occurrence of foo() or bar() in the code to insert a debug print to log the call and result or whatever debug code you want to insert.
This code looks as if it meets your requirements:
#include <stdio.h>
int rc;
int foo(int a, int b);
int bar(int a, char *b, int *c);
extern void LogRet(const char *fn, const char *file, const char *from, int ln, int ret);
void LogRet(const char *fn, const char *file, const char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08x\n", file, from, ln, fn, ret);
}
#define foo(args, ...) (rc = (foo)(args, ##__VA_ARGS__), LogRet("foo", __FILE__, __FUNCTION__, __LINE__, rc), rc)
#define bar(args, ...) (rc = (bar)(args, ##__VA_ARGS__), LogRet("bar", __FILE__, __FUNCTION__, __LINE__, rc), rc)
extern void caller1(void);
void caller1(void)
{
int d;
int e = foo(1, 2);
int f = bar(3, "abc", &d);
printf("%s(): %d + %d + %d = %d\n", __func__, d, e, f, d + e + f);
}
#undef foo
#undef bar
#define WRAPPER(func, ...) ((rc = (func)(__VA_ARGS__)), LogRet(#func, __FILE__, __func__, __LINE__, rc), rc)
#define foo(...) WRAPPER(foo, __VA_ARGS__)
#define bar(...) WRAPPER(bar, __VA_ARGS__)
extern void caller2(void);
void caller2(void)
{
int d;
int e = foo(2, 3);
int f = bar(3, "abc", &d);
printf("%s(): %d + %d + %d = %d\n", __func__, d, e, f, d + e + f);
}
int (foo)(int a, int b)
{
return (a + b) % 3;
}
int (bar)(int a, char *b, int *c)
{
int d = b[a];
*c = d;
return a + d;
}
int main(void)
{
caller1();
caller2();
return 0;
}
Example output:
wrapper.c.caller1.23: foo() ret:00000000
wrapper.c.caller1.24: bar() ret:00000003
caller1(): 0 + 0 + 3 = 3
wrapper.c.caller2.41: foo() ret:00000002
wrapper.c.caller2.42: bar() ret:00000003
caller2(): 0 + 2 + 3 = 5
Pre-processed source code (excluding the output from #include <stdio.h>):
# 2 "wrapper.c" 2
int rc;
int foo(int a, int b);
int bar(int a, char *b, int *c);
extern void LogRet(const char *fn, const char *file, const char *from, int ln, int ret);
void LogRet(const char *fn, const char *file, const char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08x\n", file, from, ln, fn, ret);
}
extern void caller1(void);
void caller1(void)
{
int d;
int e = (rc = (foo)(1, 2), LogRet("foo", "wrapper.c", __FUNCTION__, 23, rc), rc);
int f = (rc = (bar)(3, "abc", &d), LogRet("bar", "wrapper.c", __FUNCTION__, 24, rc), rc);
printf("%s(): %d + %d + %d = %d\n", __func__, d, e, f, d + e + f);
}
# 36 "wrapper.c"
extern void caller2(void);
void caller2(void)
{
int d;
int e = ((rc = (foo)(2, 3)), LogRet("foo", "wrapper.c", __func__, 41, rc), rc);
int f = ((rc = (bar)(3, "abc", &d)), LogRet("bar", "wrapper.c", __func__, 42, rc), rc);
printf("%s(): %d + %d + %d = %d\n", __func__, d, e, f, d + e + f);
}
int (foo)(int a, int b)
{
return (a + b) % 3;
}
int (bar)(int a, char *b, int *c)
{
int d = b[a];
*c = d;
return a + d;
}
int main(void)
{
caller1();
caller2();
return 0;
}
Tested with GCC 4.8.2 on Mac OS X 10.9.2.
Related
I cannot seem to figure out why its doing this...
unsigned long drawing::font;
auto drawing::setup_fonts() -> void
{
drawing::font = g_surface->CreateFont_();
g_surface->SetFontGlyphSet(drawing::font, "Small Fonts", 8, 400, 0, 0, sdk::FONTFLAG_OUTLINE);
}
auto drawing::render_text(int x, int y, Color clr, const char* fmt, ...) -> void
{
va_list va_alist;
char buffer[1024] = { '\0' };
wchar_t str[1024] = { '\0' };
va_start(va_alist, fmt);
vsprintf(buffer, fmt, va_alist);
va_end(va_alist);
swprintf(str, 1024, L"%s", buffer);
g_surface->DrawSetTextPos(x, y);
g_surface->DrawSetTextFont(drawing::font);
g_surface->DrawSetTextColor(clr);
g_surface->DrawPrintText(str, wcslen(str));
}
it is giving me this error in both functions, second one is at the closing bracket and first error is this line
drawing::font = g_surface->CreateFont_();
namespace drawing def
namespace drawing
{
auto setup_fonts() -> void;
auto render_text(int x, int y, Color clr, const char* fmt, ...) -> void;
extern unsigned long font;
}
g_surface def
sdk::ISurface* g_surface;
extern sdk::ISurface* g_surface;
g_surface = get_interface<sdk::ISurface>("vguimatsurface.dll", VGUIMAT_SURFACE_INTERFACE_VERSION);
template <class T>
auto get_interface(const char* module, const char* name) -> T*
{
return reinterpret_cast<T*>(platform::get_interface(module, name));
}
and ISurface class. its telling me to add more info because of how big this class is so i removed most of it
class ISurface
{
public:
...
virtual vgui::HFont CreateFont_() = 0;
virtual bool SetFontGlyphSet(vgui::HFont font, const char* windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin = 0, int nRangeMax = 0) = 0;
...
};
in debugger I checked if g_surface was nullptr and its not.
I know little about __attribute__ and __cyg_profile_function_enter. Of course, they are both GNU C features.
I'm learning C++ to compile Nginx modules. I always try to convert the C code samples to C++.
Here is a simple sample of C:
#include <stdio.h>
int depth_ = -1;
#ifdef __GNUC__
void __cyg_profile_func_enter(void *, void *) __attribute__((no_instrument_function));
void _cyg_profile_func_enter(void *, void *) __attribute__((no_instrument_function));
#define sustainable(fn, caller) \
do {\
printf("%d, %s: fn = %p, caller = %p\n", depth_, __FUNCTION__, fn, caller); \
} while(0)
void __cyg_profile_func_enter(void *fn, void *caller){
printf("Enter:\n");
depth_++;
sustainable(fn, caller);
}
void __cyg_profile_func_exit(void *fn, void *caller){
printf("Exit:\n");
depth_--;
sustainable(fn, caller);
}
#endif
void sustain(){
depth_ = 100;
}
int main(){
depth_ = 10;
sustain();
//Suture suture;
//suture.sultry();
return 0;
}
sh$ gcc -finstrument-functions ...
It displays Enter: 101, __cyg_profile_func_enter: fn = 0x400645, caller = 0x4006ba and Exit: 100, __cyg_profile_func_exit: fn = 0x400645, caller = 0x4006ba circularly
This is the C++ code:
#include <iostream>
using namespace std;
int depth_ = -1;
#ifdef __GNUC__
extern "C" {
void __cyg_profile_func_enter(void *, void *) __attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *, void *) __attribute__((no_instrument_function));
#define sustainable(fn, caller) \
do{ \
printf("%d, %s: fn = %p, caller = %p\n", depth_, __FUNCTION__, fn, caller); \
} while(0)
void __cyg_profile_func_enter(void *fn, void *caller){
printf("Enter:\n");
depth_++;
sustainable(fn, caller);
}
void __cyg_profile_func_exit(void *fn, void *caller){
printf("Exit:\n");
depth_--;
sustainable(fn, caller);
}
}
#endif
void sustain(){
depth_ = 100;
}
class Suture
{
public:
void sultry(){
}
};
int main(int argc, char **argv){
sustain();
Suture suture;
suture.sultry;
}
Then I compile with
sh$ g++ -std=c++11 -finstrument-functions ....
It displays Enter: 2, __cyg_profile_func_enter: fn = 0x400925, caller = 0x40099b and Exit: 1, __cyg_profile_func_exit: fn = 0x400925, caller = 0x40099b circularly.
It's weird. Why the depth_ = 100 works with gcc but not with g++?
What function do you expect fn to be the address of? I'm not seeing function calls in main() that should take place after the assignment of depth_.
If you're seeing repeated calls at all, they must be due to library functions, which could well be performed before the depth_ = 100; assignment.
I've tried a lot of solutions, but none of them is working.
printf("Test : %d", 123);
std::cout << "Test" << 123 << std::endl;
...
Actually I've setup my project's debugger like this, I'm able to write in the console using OutputDebugStringA("Test"); but this function doesn't accept more than one parameter.
How can I print something like this : ("Test : %d", 123)?
Bogy's answer is on the right track but I don't like the allocation in GetWC which looks like a memory leak, and it uses unsafe functions, try this:
void Debug::Log(const wchar_t *format, ...)
{
wchar_t buffer[BUFFER_LENGTH];
va_list args;
va_start(args, format);
_vsnwprintf_s(buffer, BUFFER_LENGTH, _TRUNCATE, format, args);
va_end(args);
buffer[BUFFER_LENGTH - 1] = '\0'; //prevent buffer overflow
OutputDebugString(buffer);
}
Usage like:
Debug::Log(L"Hello %S\n", "sailor");
I found this code in a C++ training on Pluralsight that I am using for Windows 8 (you just say TRACE("Test : %d", 123); to use it):
#pragma once
#include <assert.h>
#define ASSERT assert
#ifdef _DEBUG
inline auto Trace(wchar_t const * format, ...) -> void
{
va_list args;
va_start(args, format);
wchar_t buffer[256];
ASSERT(-1 != _vsnwprintf_s(buffer, _countof(buffer) - 1, format, args));
va_end(args);
OutputDebugString(buffer);
}
#endif
struct Tracer
{
char const * m_filename;
unsigned m_line;
Tracer(char const * filename, unsigned const line) :
m_filename{ filename },
m_line{ line }
{
}
template <typename... Args>
auto operator()(wchar_t const * format, Args... args) const -> void
{
wchar_t buffer[256];
auto count = swprintf_s(buffer, L"%S(%d): ", m_filename, m_line);
ASSERT(-1 != count);
ASSERT(-1 != _snwprintf_s(buffer + count, _countof(buffer) - count, _countof(buffer) - count - 1, format, args...));
OutputDebugString(buffer);
}
};
#ifdef _DEBUG
#define TRACE Tracer(__FILE__, __LINE__)
#else
#define TRACE __noop
#endif
I've finally found this solution (here and here) :
Debug.cpp:
#pragma once
#include "pch.h"
#define BUFFER_LENGTH 1024
//char* to wchar_t*
const wchar_t *GetWC(const char *c)
{
const size_t cSize = strlen(c) + 1;
wchar_t* wc = new wchar_t[cSize];
mbstowcs(wc, c, cSize);
return wc;
}
void Debug::Log(const char *format, ...)
{
char buffer[BUFFER_LENGTH];
va_list args;
va_start(args, format);
vsnprintf(buffer, BUFFER_LENGTH, format, args);
va_end(args);
buffer[BUFFER_LENGTH - 1] = '\0'; //prevent buffer overflow
OutputDebugString(GetWC(buffer));
}
Debug.h:
class Debug{
public:
static void Log(const char *format, ...);
};
We can use it like printf function:
Debug::Log("Test : %d", 123);
I am porting an application on Mac OS X which was written for Windows.
In this application, there are many instances of _vscwprintf and _vscprintf.
This question helped me to implement _vsprintf on Mac OS X. But same technique for _vswprintf is not working.
Can anyone give the alternative of _vscwprintf on Mac OS X? Or there any equivalent method for this?
Microsoft describes the functions as returning the number of characters that would be used if the string were formatted — note that they are documented as not including the null terminator.
int _vscprintf(
const char *format,
va_list argptr
);
int _vscwprintf(
const wchar_t *format,
va_list argptr
);
Initial answer
These functions can, therefore, be emulated with vsprintf() and vswprintf():
int _vscprintf(const char *format, va_list argptr)
{
return(vsnprintf(0, 0, format, argptr));
}
int _vscwprintf(const wchar_t *format, va_list argptr)
{
return(vswprintf(0, 0, format, argptr));
}
It is up to you whether you remove the leading underscore; I would.
Note that the _vscwprintf() implementation above is flawed; see the code below.
vscprintf() and scprintf()
Apologies: I wrote vsprintf() where I needed to write vsnprintf() (now fixed in the code above); however, vswprintf() already has the safer interface with the buffer length, so there is no vsnwprintf(). There's a reason I prefer to test compile code before (or shortly after) posting it — it's been irksome not having the wherewithal to do so for a couple of days.
Here's an SSCCE for vscprintf() (and scprintf()):
#include <stdio.h>
#include <stdarg.h>
extern int vscprintf(const char *format, va_list argptr);
extern int scprintf(const char *format, ...);
int vscprintf(const char *format, va_list argptr)
{
return(vsnprintf(0, 0, format, argptr));
}
int scprintf(const char *format, ...)
{
va_list args;
va_start(args, format);
int rc = vscprintf(format, args);
va_end(args);
return rc;
}
int main(void)
{
int l = scprintf("%-8s %8d\n", "abc", 123);
if (l > 0)
{
char buffer[l+1];
int n = snprintf(buffer, sizeof(buffer), "%-8s %8d\n", "abc", 123);
printf("%d = %d: %s", l, n, buffer);
}
return 0;
}
Output:
18 = 18: abc 123
vscwprintf() and scwprintf()
It turns out to be harder to simulate _vscwprintf() because the vswprintf() function is not as helpful as the vsnprintf() function. Specifically, vswprintf() reports an error if the formatted string won't fit in the formatted space, whereas vsnprintf() reports the number of characters that would have been needed in the buffer if it was going to fit. Hence, you have to work by trial and error:
#include <stdio.h>
#include <stdarg.h>
#include <wchar.h>
extern int vscwprintf(const wchar_t *format, va_list argptr);
extern int scwprintf(const wchar_t *format, ...);
int vscwprintf(const wchar_t *format, va_list argptr)
{
// Unlike vsnprintf(), vswprintf() does not tell you how many
// characters would have been written if there was space enough in
// the buffer - it just reports an error when there is not enough
// space. Assume a moderately large machine so kilobytes of wchar_t
// on the stack is not a problem.
int buf_size = 1024;
while (buf_size < 1024 * 1024)
{
va_list args;
va_copy(args, argptr);
wchar_t buffer[buf_size];
int fmt_size = vswprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), format, args);
if (fmt_size >= 0)
return fmt_size;
buf_size *= 2;
}
return -1;
}
int scwprintf(const wchar_t *format, ...)
{
va_list args;
va_start(args, format);
int rc = vscwprintf(format, args);
va_end(args);
return rc;
}
int main(void)
{
int l = scwprintf(L"%-8ls %8d\n", L"abc", 123);
if (l > 0)
{
wchar_t buffer[l+1];
int n = swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), L"%-8ls %8d\n", L"abc", 123);
wprintf(L"%d = %d: %ls", l, n, buffer);
}
return 0;
}
When run, this produces the output
18 = 18: abc 123
(the same as before).
Tested on Mac OS X 10.8.3 using GCC 4.7.3 (which was built on Mac OS X 10.7.5, but that shouldn't cause any problems).
I recommend using open_wmemstream instead. Something like that:
#include <stdio.h>
#include <string>
#include <stdarg.h>
using namespace std;
wstring wstring_format(const wchar_t* format, ...)
{
wchar_t* buf;
size_t size;
FILE* stream = open_wmemstream(&buf, &size);
va_list args;
va_start(args, format);
vfwprintf(stream, format, args);
va_end(args);
fclose(stream);
wstring result(buf, buf + size);
free(buf);
return result;
}
if I have this function:
printAll(const char *message, ...)
{
va_list argptr = NULL;
va_start(argptr, message);
// todo: how to printf all the arguments in the message?
va_end(argptr);
}
Suppose I call the function like this:
printAll("My info: Value1 = %d, Value 2=%d", 1, 2);
In this line: // todo: how to printf all the arguments in the message?
How can I print them all in order to have:
My info: Value1 = 1, Value 2=2
You're looking for the vprintf() function which was designed to do exactly this:
vprintf(message, argptr);
The v*printf() family of functions work basically in the same way as their normal counterparts, except they take a va_list instead of varargs. They don't call va_end() for you, so the way you have it now is correct.
Here's a sketch of the general idea (though a finished version, has to deal with quite a few more things such as field width, precision, more conversions, etc.
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
int my_vfprintf(FILE *file, char const *fmt, va_list arg) {
int int_temp;
char char_temp;
char *string_temp;
char ch;
int length = 0;
char buffer[512];
while ( ch = *fmt++) {
if ( '%' == ch ) {
switch (ch = *fmt++) {
/* %% - print out a single % */
case '%':
fputc('%', file);
length++;
break;
/* %c: print out a character */
case 'c':
char_temp = va_arg(arg, int);
fputc(char_temp, file);
length++;
break;
/* %s: print out a string */
case 's':
string_temp = va_arg(arg, char *);
fputs(string_temp, file);
length += strlen(string_temp);
break;
/* %d: print out an int */
case 'd':
int_temp = va_arg(arg, int);
itoa(int_temp, buffer, 10);
fputs(buffer, file);
length += strlen(buffer);
break;
/* %x: print out an int in hex */
case 'x':
int_temp = va_arg(arg, int);
itoa(int_temp, buffer, 16);
fputs(buffer, file);
length += strlen(buffer);
break;
}
}
else {
putc(ch, file);
length++;
}
}
return length;
}
int my_printf(char const *fmt, ...) {
va_list arg;
int length;
va_start(arg, fmt);
length = my_vfprintf(stdout, fmt, arg);
va_end(arg);
return length;
}
int my_fprintf(FILE *file, char const *fmt, ...) {
va_list arg;
int length;
va_start(arg, fmt);
length = my_vfprintf(file, fmt, arg);
va_end(arg);
return length;
}
#ifdef TEST
int main() {
my_printf("%s", "Some string");
return 0;
}
#endif
Here is a printAll() that will do exactly as you would like...
#ifdef __cplusplus
#include <cstring>
#include <cstdio>
#include <cstdarg>
using namespace std;
#else
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#endif
void printAll(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
const size_t MAX_LEN = 1023;
#ifdef __cplusplus
char msg[MAX_LEN+1] = {0};
#else
char msg[MAX_LEN+1];
for (int i = 0; i < MAX_LEN+1; i++)
{
msg[i] = 0;
}
#endif
vsnprintf(msg, MAX_LEN, fmt, ap);
printf("%s\n", msg);
va_end(ap);
}
int main() {
printAll("My info: Value1 = %d, Value 2=%d", 1, 2);
return 0;
}
My info: Value1 = 1, Value 2=2