0

我想用更现代的 C++11 风格的 API 包装一个可变参数 C++ 函数。该功能是来自Pin仪表tramework的功能:

VOID 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 框架设置描述符以了解在运行时传递给用户函数的内容。

另请注意,不能从参数描述符中自动推断出函数参数的类型。在我的示例中,所有描述符都是枚举,但它们描述了一个长的 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)
4

2 回答 2

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 的了解还不够,无法理解还有什么可以自动化的。

于 2013-07-28T01:33:00.797 回答
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...);
}

如您所见,这也适用于 lambda。如果函数名称不明确,则无法避免显式模板实例化。

于 2013-07-27T13:49:29.350 回答