2015-06-06 5 views
12

Gibt es Möglichkeiten, Funktionen oder Methoden in C++ wie im Python-Stil zu dekorieren?Python-like C++ - Dekoratoren

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

Verwenden von Makros zum Beispiel:

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

oder

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

Ist es möglich?

Antwort

14

std::function bietet die meisten Bausteine ​​für meine vorgeschlagene Lösung.

Hier ist meine vorgeschlagene Lösung.

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

Ausgang:

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

PS

Decorator bietet einen Ort, an dem Sie die Funktionalität über Herstellung der Funktionsaufruf hinzufügen können. Wenn Sie einen einfachen Durchgang durch zu std::function möchten, können Sie verwenden:

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

Hübscher Code, aber zu lang. Ich verstehe, das ist nicht Py, und C++ nicht starke Syntax Zucker wie Py zur Verfügung gestellt. :) Aber gibt es Weg, einige Makros zu erklären und Ihren Code zu verwenden, wie ich in meinen Beispielen zeige? Diese dekorierte Methode wird in Runtime definiert, aber ich möchte sie in allen anderen Funktionen und Klassen verwenden. Ich muss "makeDecorator" überall wiederholen? Danke. :) – Broly

+0

@Broly, ja, du musst die Aufrufe von 'makeDecorator()' mindestens einmal für jede Funktion wiederholen, die du dekorieren willst. Um die vorgeschlagene Code-Produktionsqualität zu erreichen, benötigen Sie ein wenig Arbeit. –

2

Sie können eine eingeschränkte Funktionalität dieses Typs mit dem Token-Pasting-Vorverarbeitungsoperator ## erhalten. Siehe https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html. Die Schwierigkeit besteht darin, dass in C jeder Funktionsname zur Verknüpfungszeit definiert werden muss, so dass Funktionen keine Objekte sind, die wie Python transformiert werden können. In Python sind Dekorateure also nützlich und guter Stil, aber in C sollten solche Tricks sparsam verwendet werden, wenn überhaupt.

4

Hier mein Versuch ist. Funktioniert unter C++ 14 (generische Lambdas und Rückgabetyp Abzug).

#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

Ordentlich, aber nicht auf nicht statische Funktionen funktionieren, gibt es eine Möglichkeit, das zu lösen? – pholat

+0

@pholat Sie können Ihre Funktion folgendermaßen umschließen: 'MyClass nicht; auto cmp_wrapper = [&] (automatisch ... args) {return non.cmp (args ...);}; ' – thorhunter

-1

Alle oben genannten Antworten sind kompliziert und verwenden Bibliotheken. Meine Antwort hier ist bei weitem die einfachste und benötigt keine Bibliothek Header.

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

nun in der Implementierungsdatei gehen Sie wie folgt

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

Ende der Geschichte. Jetzt ist es, wie Sie es in Ihrem Code verwenden können.

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

Das ist es Leute!

Vorteile:

  • die Klasse „deco“ hat nur ein Datenelement => kleine Speicher Fußdruck

  • der Betreiber() nimmt eine beliebige Anzahl von Argumenten, also können Sie jede Funktion dekorieren unabhängig von der Anzahl der Argumente.

  • Einfache Implementierung => einfaches Debugging und Testen.

Nachteile:

  • keine bekannt!
Verwandte Themen