Decoradores de C++ similares a Python
function macros (4)
¿Hay formas de decorar funciones o métodos en C ++ como en el estilo python?
@decorator
def decorated(self, *args, **kwargs):
pass
Usando macros por ejemplo:
DECORATE(decorator_method)
int decorated(int a, float b = 0)
{
return 0;
}
o
DECORATOR_MACRO
void decorated(mytype& a, mytype2* b)
{
}
¿Es posible?
Puede obtener alguna funcionalidad limitada de este tipo utilizando el operador de preprocesamiento de token-pasting ##. Ver https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html . La dificultad es que en C, cada nombre de función debe definirse en el tiempo del enlace, por lo que las funciones no son objetos que se puedan transformar como lo hace Python. Así que en Python los decoradores son útiles y tienen un buen estilo, pero en C esos trucos deben usarse con moderación o nada.
std::function
proporciona la mayoría de los bloques de construcción para mi solución propuesta.
Aquí está mi solución propuesta.
#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);
}
Salida:
Calling the decorated function.
a = 10, b = 30.3
Calling the decorated function.
a = 10
PD
Decorator
proporciona un lugar donde puede agregar funcionalidad más allá de realizar la llamada de función. Si desea una función de paso simple a std::function
, puede usar:
template<class R, class... Args >
std::function<R(Args...)> makeDecorator(R (*f)(Args ...))
{
return std::function<R(Args...)>(f);
}
Aquí está mi intento. Funciona bajo C ++ 14 (lambdas genérico y deducción del tipo de devolución).
#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);
}
Todas las respuestas anteriores son complicadas y usa bibliotecas. Mi respuesta aquí es de lejos la más simple y no necesita ningún encabezado de biblioteca.
// "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
Ahora en el archivo de Implementación haz lo siguiente
// "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;
}
El final de la historia. Ahora esta es la forma de usarlo en tu código.
// "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;
}
¡Esto es gente!
Pros:
La CLASS "deco" tiene solo un miembro de datos => pequeña huella de memoria
el operador () toma cualquier cantidad de argumentos, por lo que puede decorar cualquier función independientemente de su número de argumentos.
Implementación simple => depuración y prueba simples.
Contras:
- ¡ninguno conocido!