2015-06-06 9 views
12

Czy istnieją sposoby dekorowania funkcji lub metod w C++, jak w stylu Pythona?Pythonowe konstruktory C++

@decorator 
def decorated(self, *args, **kwargs): 
    pass 

Korzystanie makra na przykład:

DECORATE(decorator_method) 
int decorated(int a, float b = 0) 
{ 
    return 0; 
} 

lub

DECORATOR_MACRO 
void decorated(mytype& a, mytype2* b) 
{ 
} 

jest to możliwe?

Odpowiedz

14

dostarcza większość elementów do mojego proponowanego rozwiązania.

Oto moje proponowane rozwiązanie.

#include <iostream> 
#include <functional> 

//------------------------------- 
// BEGIN decorator implementation 
//------------------------------- 

template <class> struct Decorator; 

template <class R, class... Args> 
struct Decorator<R(Args ...)> 
{ 
    Decorator(std::function<R(Args ...)> f) : f_(f) {} 

    R operator()(Args ... args) 
    { 
     std::cout << "Calling the decorated function.\n"; 
     return f_(args...); 
    } 
    std::function<R(Args ...)> f_; 
}; 

template<class R, class... Args> 
Decorator<R(Args...)> makeDecorator(R (*f)(Args ...)) 
{ 
    return Decorator<R(Args...)>(std::function<R(Args...)>(f)); 
} 

//------------------------------- 
// END decorator implementation 
//------------------------------- 

//------------------------------- 
// Sample functions to decorate. 
//------------------------------- 

// Proposed solution doesn't work with default values. 
// int decorated1(int a, float b = 0) 
int decorated1(int a, float b) 
{ 
    std::cout << "a = " << a << ", b = " << b << std::endl; 
    return 0; 
} 

void decorated2(int a) 
{ 
    std::cout << "a = " << a << std::endl; 
} 

int main() 
{ 
    auto method1 = makeDecorator(decorated1); 
    method1(10, 30.3); 
    auto method2 = makeDecorator(decorated2); 
    method2(10); 
} 

wyjściowa:

Calling the decorated function. 
a = 10, b = 30.3 
Calling the decorated function. 
a = 10 

PS

Decorator stanowi miejsce, w którym można dodać funkcjonalność poza podejmowania wywołanie funkcji. Jeśli chcesz prosty przechodzi do std::function, można użyć:

template<class R, class... Args > 
std::function<R(Args...)> makeDecorator(R (*f)(Args ...)) 
{ 
    return std::function<R(Args...)>(f); 
} 
+0

Ładny kod, ale za długi. Rozumiem, że to nie jest Py, a C++ nie zapewnia silnego cukru składniowego, takiego jak Py. :) Ale czy można zadeklarować niektóre makra i użyć kodu, jak pokazuję w moich przykładach? Ta dekorowana metoda jest zdefiniowana w środowisku wykonawczym, ale chcę ją wykorzystać we wszystkich innych funkcjach i klasach. Muszę powtórzyć "makeDecorator" wszędzie? Dzięki. :) – Broly

+0

@Broly, tak, będziesz musiał powtarzać wywołania 'makeDecorator()' przynajmniej raz dla każdej funkcji, którą chcesz udekorować. Aby uzyskać sugerowaną jakość produkcji kodu, potrzebujesz trochę pracy. –

2

Można uzyskać ograniczoną funkcjonalność tego typu za pomocą operatora przetwarzania wstępnego token-pasting ##. Zobacz https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html. Trudność polega na tym, że w C każda nazwa funkcji musi być zdefiniowana w czasie połączenia, więc funkcje nie są obiektami, które można przekształcić tak, jak robi to Python. Więc w Pythonie dekoratory są użyteczne i mają dobry styl, ale w C takie sztuczki powinny być używane oszczędnie, jeśli w ogóle.

4

Oto moja próba. Działa w C++ 14 (ogólne lambdy i dedukcja typu powrotu).

#include <iostream> 
#include <functional> 

/* Decorator function example, 
    returns negative (! operator) of given function 
*/ 
template <typename T> 
auto reverse_func(T func) 
{ 
    auto r_func = 
    [=](auto ...args) 
    { 
     return !func(args...); 
    }; 

    return r_func; 
} 

/* Decorator function example, 
    prints result of given function before it's returned 
*/ 
template <typename T> 
auto print_result_func(T func) 
{ 
    auto r_func = 
    [=](auto ...args) 
    { 
     auto result = func(args...); 
     std::cout << "Result: " << result << std::endl; 
     return result; 
    }; 

    return r_func; 
} 

/* Function to be decorated example, 
    checks whether two given arguments are equal 
*/ 
bool cmp(int x, int y) 
{ 
    return x == y; 
} 

/* Decorator macro */ 
#define DECORATE(function, decorator) \ 
    decorator<decltype(function)>(function) 

int main() 
{ 
    auto reversed = DECORATE(cmp, reverse_func); 
    auto print_normal = DECORATE(cmp, print_result_func); 
    auto print_reversed = DECORATE(reversed, print_result_func); 
    auto print_double_normal = DECORATE(print_normal, print_result_func); 
    auto print_double_reversed = DECORATE(print_reversed, print_result_func); 

    std::cout << cmp(1,2) << reversed(1,2) << std::endl; 
    print_double_normal(1,2); 
    print_reversed(1,2); 
    print_double_reversed(1,2); 
} 
+0

Zgrabne, ale nie będą działać na funkcjach nie statycznych, czy istnieje sposób na rozwiązanie tego problemu? – pholat

+0

@pholat Możesz zawinąć swoją funkcję w następujący sposób: 'MyClass non; auto cmp_wrapper = [&] (auto ... args) {return non.cmp (args ...);}; ' – thorhunter

-1

Wszystkie powyższe odpowiedzi są skomplikowane i korzystają z bibliotek. Moja odpowiedź tutaj jest zdecydowanie najprostsza i nie wymaga nagłówka biblioteki.

// "DECORATOR.h" 
    #pragma once 
    #ifndef DECORATOR_H 
    #define DECORATOR_H 

    template<typename T> 
    class deco 
    { 
     T* m_func; 
    public: 
     explicit deco(T func); 

     template<typename ...args> 
     auto operator()(args... Args); 
    } 
    #endif // DECORATOR_H 

Teraz w pliku wdrażania wykonaj następujące czynności

// "DECORATOR.cpp" 
    template<typename T> 
    inline deco<T>::deco(T func) 
    :m_func(func) 
    { 
    }; 

    // implementing the function call operator 
    template <typename T> 
    template <typename ...args> 
    auto deco<T>::operator()(args ...Args) 
    { 
     //Do some stuff defore the decorated function call 
     // .... 
     // Call the decorated function. 
     auto rv = m_func(Args...); 

     //Do some stuff after the function call 
     // .... 
     return rv; 
    } 

koniec historii. Teraz to jest sposób użycia go w kodzie.

// "main.cpp" 
    #include "DECORATOR.h" 
    #include <stdio.h> // just for printf() 

    // functions to decorate 
    int add(int a, int b) 
    { 
     return a+b; 
    }; 

    int sub(int a, int b) 
    { 
     return a-b; 
    }; 

    // Main function 
    int main() 
    { 
     // decorate the functions "add", "sub" 
     deco<decltype(add)> add_Deco(add); 
     deco<decltype(sub)> sub_Deco(sub); 

     // call your decorated functions 
     printf("result of decorated Add =%d\n", add_Deco(5,2)); 
     printf("result of decorated Sub =%d\n", sub_Deco(4,3)); 
     return 0; 
    } 

To jest to ludzie!

Plusy:

  • klasy "deco" ma tylko jeden członek dane => mały pamięci foot print

  • operatora() wykonuje dowolną liczbę argumentów, więc można ozdobić dowolną funkcję niezależnie od liczby argumentów.

  • Prosta implementacja => proste debugowanie i testowanie.

Wady:

  • Nieznane!
Powiązane problemy