我有一个方法可以通过用宏替换函数来包装函数,这样我就可以记录调用和返回代码。这是一个有效的示例:
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)
它所替代的函数的宏会调用函数并记录函数名称、调用位置以及返回代码。包装任何函数使用相同的语法,只需要在宏中替换函数名称 3 次。我想做的是创建包装器宏,其中为 foo 重新定义的宏类似于:
#define foo(args, ...) WRAPPPER(foo)
我了解 stringify 和 double stringify 的基础知识,但我什至无法让 WRAPPER 宏执行真正的函数调用。理想情况下,我想将其简化为单个 WRAP(foo) 语句。原因是,我有大约 100 个或更多的函数我想包装,它想通过一个简单的强制包含文件来简单地完成它。我得出的结论是这是不可能的,但我想在放弃这个想法之前先在这里问一下。我正在使用 clang 和 vc++,如果这有什么不同的话,但在我调试很多不同的系统时,在任何编译器上使用它会很好。
因为我是新来的,所以我不确定这应该是一个单独的答案还是编辑更新。这基本上是 Jonathan Leffler 的回答。这是一个功能示例。虽然它调用的 2 个函数毫无意义,但目标是拥有一个可以用任何参数列表包装任何函数的宏。我的主要用途是在有问题的大型代码库中记录使用流程。此外,我的原始样本有一个明显的弱点。通过将返回代码存储在全局中,如果没有在 TLS 中进行繁琐的准备,它就不是线程安全的。现在删除了全局变量,宏不再使用序列运算符返回值,它由日志函数保留和返回。此外,正如 Augurar 在 Jonathan 的例子中指出和展示的那样。如果宏与函数声明在同一文件中声明,则需要括号。
#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;
}
发布构建输出:
1 + 2 = 3
3 doubled = 6
调试构建输出:
1 + 2 = 3
test.cpp.main.35: foo() ret:00000003
3 doubled = 6
test.cpp.main.36: bar() ret:00000006
主要好处是不必在代码中查找每次出现的 foo() 或 bar() 即可插入调试打印以记录调用和结果或您要插入的任何调试代码。
最佳答案
这段代码看起来好像符合您的要求:
#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;
}
示例输出:
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
预处理后的源代码(不包括 #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;
}
在 Mac OS X 10.9.2 上使用 GCC 4.8.2 测试。
关于C++ 用可变参数包装一个包装器宏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22395738/
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?