2013-07-27 3 views
0

Я хотел бы обернуть переменную функцию C++ с помощью более современного API стиля C++ 11. Функция this one от Pin instrumentation tramework:Проектирование лучшего API для вариационной функции

VOID LEVEL_PINCLIENT::INS_InsertCall(INS ins, 
            IPOINT action, 
            AFUNPTR funptr, 
            ...) 

Где AFUNPTR объявлен как:

typedef VOID (*AFUNPTR)(); 

и ... список аргументов для передачи funptr. Список состоит из дескрипторов аргументов (IARG_TYPE enum), необязательных значений аргументов и терминатора 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 аргумент.

Я хотел бы создать этот API-интерфейс обложки со всем, что предлагает C++ 11, возможно, чтобы передать лямбда вместо указателя функции, добавив некоторую безопасность типов в список аргументов, используя вариативные шаблоны и т. Д. .

возможно использование может выглядеть следующим образом (но я открыт для предложений):

INS_InsertCall(ins, IPOINT_BEFORE, 
       [](long rax, THREADID tid) { cout << rax << endl << tid << endl; }, 
       IARG_REG_VALUE, REG_RAX, 
       IARG_THREAD_ID) 
+0

«Вставка» функционального элемента, такого как лямбда, потребует сохранения состояния где-то. Если 'INS_InsertCall' не будет сохранять состояние, API должен будет потребовать, чтобы вызывающий абонент сохранил состояние. –

+2

Это выглядит увлекательно интересным вопросом, мне нужно будет вернуться и ответить на него, когда я вернусь к компьютеру через несколько часов. –

+0

Если вы говорите о захваченном состоянии, я в порядке, когда лямбда не имеет гражданства. Вместо этого состояние можно обрабатывать как параметры для лямбда. Сегодня это также имеет место, поскольку AFUNPTR является указателем статической функции. –

ответ

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...); 
} 

Как вы видите, это работает с лямбды тоже. Если имя функции неоднозначно, невозможно избежать явного создания экземпляра шаблона.

+1

Спасибо за подробный ответ, однако я, вероятно, не очень четко о требованиях. API должен не просто принимать функцию и соответствующие ей аргументы. Он принимает функцию и метаданные, которые описывают, что мы должны передать этим функциям. Иногда одно значение будет описывать аргумент, а иногда два дескриптора будут выполнять задание для одного аргумента, как в моем примере. Здесь IARG_REG_VALUE и REG_RAX - 2 перечисления, которые описывают один аргумент. Первый говорит, что мы принимаем значение регистра, а второе сообщает, какой он регистр. Я улучшу свой вопрос. –

+0

@OmerMor Почему вы хотите, чтобы значения описывали один? Почему бы не «REG_RAX» быть достаточно? – stefan

+0

Я только что описал существующий API. Я бы не хотел иметь один дескриптор в конечном API. Взгляните в документацию enum для IARG_TYPE для объяснения каждого дескриптора, и это необязательный дополнительный аргумент: http://software.intel.com/sites/landingpage/pintool/docs/58423/Pin/html/group__INST__ARGS.html#g7e2c955c99fa84246bb2bce1525b5681 , Помните, что тип дескриптора! = Тип аргумента. Тип, отображаемый дескриптором, документируется в той же ссылке. –

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, чтобы понять, что еще можно было бы автоматизировать.

+0

Спасибо за усилие Mooing Duck. Я надеялся, что смогу использовать лямбды, но пока это не представляется возможным. –

+0

@OmerMor: В соответствии с [этим ответом] (http://stackoverflow.com/a/15657297/845092) только апатриды без атак являются конвертируемыми в указатели функций вообще, поэтому способы сделать работу без апатридов ямбда будут очень сложными. –

Смежные вопросы