2016-11-03 2 views
4

Я портирую библиотеку метрик Python на C++. Один API, предоставляемый библиотекой Python, представляет собой декоратор функции, который позволяет легко записывать данные синхронизации по функциям. Изменяя определение функции будетЧто такое эквивалент декораторов функций Python в C++?

@timed('timing.foo') 
def foo(): 
    ... 

foo_result = foo() по существу превратился в

start = time.time() 
foo_result = foo() 
post_metric_to_endpoint('timing.foo', time.time() - start) 

В Function hooking in C++ мы находим this answer, что оборачивает экземпляры и помещает бремя вызова функции синхронизации на вызывающем , что означает, что мы не будем получать данные о производительности через кодовую базу (в первую очередь это раздражает обновление и легко забывается позже). Аналогично, this answer - Timing in an elegant way in c++ требует изменения сайтов вызова. This other answer в этом вопросе предоставляет способ обертывания произвольных блоков кода, что теоретически означает, что мы могли бы отпечатать весь элемент функции, который мы хотим, и вложить его в область видимости. Это самое близкое, что я нашел к тому, что я хочу, но довольно уродливый, довольно навязчивый, и я не уверен в влиянии производительности.

Поскольку это предназначено для использования в качестве библиотеки, мы можем изменить источник функций, которые мы хотим использовать во времени; на самом деле, это предпочтительнее, так как я хотел бы каждый вызов этой функции для синхронизации. В большинстве обсуждений основное внимание уделяется специальному профилированию в разработке, в то время как я пытаюсь создать систему, которая всегда будет работать в производственной среде.

+1

C++ не имеют аналогов этой функцию питона. –

ответ

8

Хотя C++ не поддерживает явный язык для декораторов, то получается, что вы можете «эмулировать» их довольно хорошо, используя C++ 14 родовой лямбды. Вот мой взгляд на него:

#include <iostream> 

template<class T> 
auto decorator(T&& func) 
{ 
    // we create a closure below 
    auto new_function = [func = std::forward<T>(func)](auto&&... args) 
    { 
     std::cout << "BEGIN decorating...\n"; 
     auto result = func(std::forward<decltype(args)>(args)...); 
     std::cout << "END decorating\n"; 
     return result; 
    }; 
    return new_function; 
} 

int f(int x) 
{ 
    std::cout << x * x << '\n'; 
    return x * x; 
} 

auto decorated = decorator(f); 
auto double_decorated = decorator(decorator(f)); 

int main() 
{ 
    decorated(5); 
    double_decorated(10); 
} 

Live on Coliru

Конечно внутри декоратора вы можете добавить все, что вы хотите (включая сроки и т.д.), выше, является лишь минимальный пример. Если это выглядит слишком сложным вы можете игнорировать фетиш std::forward и C++ 14 обобщенной лямбда захватывает и просто

#include <iostream> 

template<class T> 
auto decorator(T func) 
{ 
    // we create a closure below 
    auto new_function = [func](auto... args) 
    { 
     std::cout << "BEGIN decorating...\n"; 
     auto result = func(args...); 
     std::cout << "END decorating\n"; 
     return result; 
    }; 
    return new_function; 
} 

int f(int x) 
{ 
    std::cout << x * x << '\n'; 
    return x * x; 
} 

auto decorated = decorator(f); 
auto double_decorated = decorator(decorator(f)); 

int main() 
{ 
    decorated(5); 
    double_decorated(10); 
} 
2

C++ не имеет декораторов Pythonic, но имеет детерминированное разрушение. Чтобы создать функцию, создайте класс, который фиксирует временную метку во время построения и сообщает об уничтожении. Затем объявить экземпляр в качестве первой строки в функции:

class Timed 
{ 
//... 
} 

void foo() 
{ 
    Timed t_; 

    //Do the rest of work 
} 
+0

Это приведет к небольшому разному времени - время, необходимое для копирования параметров, и возвращаемое значение НЕ будет включено (хотя в большинстве случаев это будет незначительно по сравнению с накладными расходами, которые сам вводит код синхронизации). – Leon