AOP(Aspect-Oriented Programming) 是一种编程范式,将程序的非核心逻辑都“横切”处理,实现非核心逻辑与核心逻辑的分离【1】
在日常工作中,会遇到一类需求:统计业务处理的耗时或者加锁,业务函数可以动态替换而非侵入式修改业务函数;
简单粗暴的方法是:
Ret Process(...) // 业务函数
{
return {};
}
{
timeBegin = ...;
Process(...);
elapse = timeEnd = timeBegin;
}
显然这是临时性的处理,重复代码与扩展等都会存在问题。
易扩展代码的关键在于分析业务逻辑的变化点,抽离变化而稳定公共的部分,这里的变化点包括:
- Process函数的变化,后续需要考虑替换其他Process函数
- elapse非核心业务的变化,后续可能是其他的统计处理,例如trace等
在【1】中介绍了应用代理模式进行设计,文中使用了继承方式,当然也可以使用组合方式,例如:
int Process(int)
{
usleep(10000);
return 0;
}
template<typename F>
struct TimeProxy {
TimeProxy(F f) : m_f(f) {} // 隐式推导指南自动推导F类型
template<typename ...Args>
typename FunctionTraits<F>::RetType Process(Args&&...args)
{
std::chrono::time_point<std::chrono::high_resolution_clock> begin = std::chrono::high_resolution_clock::now();
decltype(auto) ret = m_f(std::forward<Args>(args)...);
int64_t elapseMs = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - begin).count();
DBG_LOG("elapse time: %lld", elapseMs);
return ret;
}
private:
F m_f;
};
TimeProxy tp(Process);
tp.Process(0);
这里分离出了第一个变化点,将Process函数作为泛型传入,后续替换为其他类型函数时则不用修改TimeProxy的框架。
对于第二个变化点,同理进行抽离:
struct AspectTimer {
AspectTimer() : m_begin(std::chrono::high_resolution_clock::now())
{
DBG_LOG("begin");
}
~AspectTimer()
{
DBG_LOG("end elapse time: %lld",
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_begin).count());
}
private:
std::chrono::time_point<std::chrono::high_resolution_clock> m_begin;
};
template<typename Aspect>
struct AspectProxy {
template<typename F, typename ...Args>
static decltype(auto) Process(F f, Args&&...args)
{
Aspect aop {};
return f(std::forward<Args>(args)...);
}
};
AspectProxy<AspectTimer>::Process(Process, 0);
AspectTimer为一个RAII对象,由调用者动态织入,实现了两个变化点的扩展。
此时函数是全局或者静态函数,进一步支持类成员函数,例如在DFX维测时对某类或其基类函数的切面织入,修改为:
template<typename Aspect>
struct AspectProxy {
template<typename F, typename ...Args>
static decltype(auto) Process(F f, Args&&...args)
{
Aspect aop {};
return f(std::forward<Args>(args)...);
}
};
#define INVOKE(ASPECT, CALLER, FUNC, ...) \
AspectProxy<ASPECT>::Process([&CALLER](auto&& ...args) \
{ \
return CALLER.FUNC(std::forward<decltype(args)>(args)...); \
}, __VA_ARGS__) \
Test t{};
INVOKE(AspectTimer, t, UsrProcess, 0); // 统计派生类调用
INVOKE(AspectTimer, t, Base::UsrProcess, 0); // 统计基类调用
这时需要多个切面的组合,扩展不同的切面函数:
完整代码如下:
class Base {
public:
virtual int UsrProcess(int)
{
usleep(10000);
DBG_LOG();
return 0;
}
};
class Test : public Base {
public:
int UsrProcess(int) override
{
usleep(10000);
DBG_LOG();
return 0;
}
};
struct AspectTimer {
AspectTimer() : m_begin(std::chrono::high_resolution_clock::now())
{
DBG_LOG("begin");
}
~AspectTimer()
{
DBG_LOG("end elapse time: %lld",
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_begin).count());
}
private:
std::chrono::time_point<std::chrono::high_resolution_clock> m_begin;
};
struct Aspect1 {
Aspect1()
{
DBG_LOG("begin");
}
~Aspect1()
{
DBG_LOG("end");
}
};
struct Aspect2 {
Aspect2()
{
DBG_LOG("begin");
}
~Aspect2()
{
DBG_LOG("end");
}
};
template<typename ...Types>
class AspectTypes {
public:
template<std::size_t I>
struct GetType {
using Type = typename std::tuple_element<I, std::tuple<Types...>>::type;
};
};
template<typename Type>
struct AspectProxy;
template<typename ...Aspects>
struct AspectProxy<AspectTypes<Aspects...>> {
template<typename F, typename ...Args>
static decltype(auto) Process(F&& f, Args&&...args)
{
return Invoke(std::make_index_sequence<sizeof...(Aspects)> {}, std::forward<F>(f), std::forward<Args>(args)...);
}
template<std::size_t I, typename F, typename ...Args>
static decltype(auto) Invoke(std::index_sequence<I>, F&& f, Args&&... args)
{
using AspectFuncType = typename AspectTypes<Aspects...>::template GetType<I>::Type;
AspectFuncType asp {};
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template<std::size_t I, std::size_t... R, typename F, typename ...Args>
static decltype(auto) Invoke(std::index_sequence<I, R...>, F&& f, Args&&... args)
{
using AspectFuncType = typename AspectTypes<Aspects...>::template GetType<I>::Type;
AspectFuncType asp {};
return Invoke(std::index_sequence<R...> {}, std::forward<F>(f), std::forward<Args>(args)...);
}
};
#define AOP(...) AspectTypes<__VA_ARGS__>
#define INVOKE(ASPECTS, CALLER, FUNC, ...) \
AspectProxy<ASPECTS>::Process([&CALLER](auto&& ...args) \
{ \
return CALLER.FUNC(std::forward<decltype(args)>(args)...); \
}, __VA_ARGS__) \
Test t{};
INVOKE(AOP(AspectTimer, Aspect2, Aspect1), t, Base::UsrProcess, 0);
INVOKE(AOP(AspectTimer, Aspect1, Aspect2), t, UsrProcess, 0);
但是要注意:随着切面的增多,调用栈增加性能也会有所损失,需要结合实际业务场景进行应用。
参考资料
【1】深入应用C++11代码优化与工程级应用
【2】http://vitiy.info/c11-functional-decomposition-easy-way-to-do-aop/
标签:std,...,args,return,Process,编程,C++,template,AOP From: https://blog.51cto.com/u_13137973/8854076