2015-06-06 7 views
12

Ci sono modi per decorare funzioni o metodi in C++ come in stile Python?Decoratori C++ simili a Python

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

Utilizzo di macro per esempio:

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

o

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

è possibile?

risposta

14

std::function fornisce la maggior parte degli elementi costitutivi per la mia soluzione proposta.

Ecco la mia soluzione proposta.

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

uscita:

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

PS

Decorator fornisce un luogo dove è possibile aggiungere funzionalità di là di fare la chiamata di funzione. Se si desidera un passaggio semplice attraverso std::function, è possibile utilizzare:

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

Codice carino, ma troppo lungo. Capisco, questo non è Py, e C++ non ha fornito un forte zucchero sintassi come Py. :) Ma c'è modo di dichiarare alcuni macro e usare il tuo codice come mostro nei miei esempi? Questo metodo decorato è definito in runtime, ma voglio usarlo in tutte le altre funzioni e classi. Devo ripetere "makeDecorator" ovunque? Grazie. :) – Broly

+0

@Broly, sì, dovrai ripetere le chiamate a 'makeDecorator()' almeno una volta per ogni funzione che desideri decorare. Per rendere la qualità di produzione del codice suggerita, avrai bisogno di un po 'di lavoro. –

2

È possibile ottenere alcune funzionalità limitate di questo tipo utilizzando l'operatore di ## di elaborazione dei token-incolla. Vedi https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html. La difficoltà è che in C ogni nome di funzione deve essere definito al momento del collegamento, quindi le funzioni non sono oggetti che possono essere trasformati come fa Python. Quindi in Python i decoratori sono utili e di buon stile, ma in C questi trucchi dovrebbero essere usati con parsimonia se non del tutto.

4

Ecco il mio tentativo. Funziona sotto C++ 14 (generico lambda e deduzione tipo di reso).

#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

Neat, ma non funzionerà su funzioni non statiche, c'è un modo per risolverlo? – pholat

+0

@pholat Puoi avvolgere la tua funzione in questo modo: 'MyClass non; auto cmp_wrapper = [&] (auto ... args) {return non.cmp (args ...);}; ' – thorhunter

-1

Tutte le risposte sopra sono complicate e utilizzano le librerie. La mia risposta qui è di gran lunga la più semplice e non ha bisogno di alcuna intestazione di libreria.

// "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 

Ora nel file di implementazione effettuare le seguenti operazioni

// "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; 
    } 

Fine della storia. Ora questo è come usarlo nel tuo codice.

// "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; 
    } 

Questo è gente!

Pro:

  • alla classe "deco" ha un solo piede di stampa => piccola memoria un membro di dati

  • l'operatore() prende un numero qualsiasi di argomenti, in modo da poter decorare qualsiasi funzione indipendentemente dal numero di argomenti.

  • Implementazione semplice => debug e test semplici.

Contro:

  • nessuno conosciuto!