我想用一个更现代的C ++ 11风格的API包装一个可变的C ++函数。 该函数来自this one:
Pin instrumentation trameworkVOID LEVEL_PINCLIENT::INS_InsertCall(INS ins,
IPOINT action,
AFUNPTR funptr,
...)
AFUNPTR
声明为:
typedef VOID (*AFUNPTR)();
和...
是传递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框架设置描述符以了解在运行时传递给用户函数的内容。
另请注意,函数参数的类型不能从参数描述符中自动推导出来。在我的例子中,所有描述符都是枚举,但它们描述了一个long和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)
答案 0 :(得分:1)
我对这个API的评价并不是很多:http://coliru.stacked-crooked.com/view?id=045edb71ffca8062a9e016506e4b51f7-4f34a5fd633ef9f45cb08f8e23efae0a
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不够了解了解还有什么可以实现自动化。
答案 1 :(得分:0)
您可以使用模板方法
#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。如果函数名称不明确,则无法避免显式模板实例化。