为可变函数设计一个更好的API
Designing a better API for a variadic function
我想用更现代的C++11风格的API包装一个可变的C++函数。该功能来自Pin仪器Trameworks:
VOID LEVEL_PINCLIENT::INS_InsertCall(INS ins,
IPOINT action,
AFUNPTR funptr,
...)
其中AFUNPTR
声明为:
typedef VOID (*AFUNPTR)();
并且CCD_ 2是要传递funptr的参数的列表。该列表由参数描述符(IARG_TYPE
枚举)、可选参数值和表示列表末尾的终止符IARG_END
组成。
以下是在给定指令(ins
)之前检测函数的用法示例,该指令将打印rAX寄存器的内容:
void print_rax_and_tid(long rax, THREADID tid) {
cout << rax << endl << tid << endl;
}
...
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)print_rax_and_tid,
IARG_REG_VALUE, REG_RAX, // the value of rAX register
IARG_THREAD_ID, // the thread id
IARG_END)
在这里,我们声明我们的函数将接受一个包含寄存器值的参数。我们还请求该工具将rAX寄存器的值传递给函数。
请注意,每个函数参数都由一个或两个描述符参数描述:
- (
IARG_REG_VALUE
、REG_RAX
)描述(long rax
) - (
IARG_THREAD_ID
)描述(THREADID tid
)
Pin框架将描述符设置为知道在运行时传递给用户函数的内容。
还要注意,函数参数的类型不能从参数描述符中自动推导出来。在我的示例中,所有描述符都是枚举,但它们描述了一个长的THREADID参数。
我想用C++11所提供的一切来设计这个包装器API,可能能够传递lambda而不是函数指针,向参数列表添加一些类型安全性,使用可变模板等
可能用法可能是这样的(但我愿意接受建议):
INS_InsertCall(ins, IPOINT_BEFORE,
[](long rax, THREADID tid) { cout << rax << endl << tid << endl; },
IARG_REG_VALUE, REG_RAX,
IARG_THREAD_ID)
我真的没有太多能想到与API有关的事情:http://coliru.stacked-crooked.com/view?id=045edb71ffca8062a9e016506e4b51f7-4f3a5fd633ef9f45cb08e23efe0a
struct REG_VALUE {
IARG_TYPE arg = IARG_REG_VALUE;
REG_TYPE reg;
REG_VALUE(REG_TYPE r) :reg(r) {}
};
template<REG_TYPE reg_v>
struct REGISTER : REG_VALUE {
REGISTER() : REG_VALUE(reg_v) {}
};
template<class func_type, class ...param_types>
VOID InsertCall(INS ins, IPOINT action, func_type funptr,
param_types... param_values)
{ INS_InsertCall(ins, action, (AFUNPTR)funptr, param_values..., IARG_END); }
然后
InsertCall(ins, IPOINT_BEFORE, print_rax_and_tid,
REGISTER<REG_RAX>(), IARG_THREAD_ID);
我将寄存器设为模板类型,这样就不必有类型/值对,然后还将IARG_END
设为自动化,但除此之外,我对API的了解还不够,无法理解还有什么可以自动化。
您可以使用模板方法
#include <iostream>
template <typename Runnable, typename... Types>
auto execute(Runnable f, Types... ts) -> decltype(f(ts...))
{
return f(ts...);
}
// Some methods to test with:
void a() { std::cerr << __func__ << __LINE__ << "n"; }
void b(int) { std::cerr << __func__ << __LINE__ << "n"; }
void c(int, char) { std::cerr << __func__ << __LINE__ << "n"; }
int d() { std::cerr << __func__ << __LINE__ << "n"; return 0; }
int e(int) { std::cerr << __func__ << __LINE__ << "n"; return 0; }
int f(int, char) { std::cerr << __func__ << __LINE__ << "n"; return 0; }
int g() { std::cerr << __func__ << __LINE__ << "n"; return 0; }
void g(int) { std::cerr << __func__ << __LINE__ << "n"; }
int main()
{
int tmp = 1;
char tmp_2 = '0';
execute(a);
execute(b, tmp);
execute(c, tmp, tmp_2);
execute(d);
execute(e, tmp);
execute(f, tmp, tmp_2);
execute([](int){ std::cerr << __func__ << __LINE__ << "n"; }, 0);
execute(b); // This won't compile, as too few arguments provided.
execute<int()>(g); // Explicit template instantiation needed (typename Runnable only)
execute<void(int)>(g, 0); // Explicit template instantiation needed (typename Runnable only)
}
如果你想放弃函数的返回值,模板会变得更简单
template <typename Runnable, typename... Types>
void execute(Runnable f, Types... ts)
{
f(ts...);
}
正如你所看到的,这也适用于lambdas。如果函数名称不明确,则无法避免显式模板实例化。
- FFmpeg:制作一个应用程序比直接使用ffmepg更好吗
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 在类的第一个/最后一个实例存在之前/之后调用一对函数.有没有更好的方法?
- 做一个unordered_multimap键的平均值是更好的方法吗?
- 静态常量与常量局部变量,哪一个性能更好
- 试图为命名数组元素创建一个更好的循环
- 在 C 和 C++ 编程中使用哪一个更好?
- 哪一个更好?使用C 代码中的System()函数或使用源代码
- 如何将输入与文件中的内容相匹配?我需要一个更好的解释
- 一个更好的实数生成器
- 对于此语句,这是一个更好的表达
- 这是一个更好的Get成员方法
- 在 c++ 中选择有序映射的最后一个条目时,哪一个更好
- 为可变函数设计一个更好的API
- 重载一个方法只是为了在C++中报告一个更好的错误.有更好的方法吗
- typecasting与memcpy():哪一个更好
- c++的一个更好的低级别int交换
- 哪一个更好,自动&&,自动或自动常量
- 3D模型移动,找到一个更好的方式来移动3D模型
- 在QGraphicsView中设置QGraphicsScene的精确视图区域(rect) -一个更好的fitInView