如何确保一个类的每个方法先调用其他方法?
我有 :
class Foo { public: void log() { } void a() { log(); } void b() { log(); } };
有没有办法,我可以有每个Foo
方法,调用log()
,但没有我必须显式inputlog()作为每个函数的第一行? 我想这样做,以便我可以添加行为,而不必每个函数,并确保调用,并添加新的function,代码会自动添加…
这甚至有可能吗? 我无法想象如何做到这一点与macros的,所以不知道从哪里开始…到目前为止唯一的方法是想添加一个“预构build步骤”,以便在编译前我扫描文件并编辑源代码,但这似乎并不聪明….
编辑:只是为了澄清 – 我不希望日志()明确调用自己。 它不需要成为class级的一部分。
编辑:我宁愿使用跨平台的工作方法,只使用stl。
由于operator ->
的不同特性,我们可以在任何成员访问之前注入代码,代价是稍微弯曲的语法:
// Nothing special in Foo struct Foo { void a() { } void b() { } void c() { } }; struct LoggingFoo : private Foo { void log() const { } // Here comes the trick Foo const *operator -> () const { log(); return this; } Foo *operator -> () { log(); return this; } };
用法如下所示:
LoggingFoo f; f->a();
在Coliru上看到它
这是一个最小(但相当普遍)的包装问题的解决scheme:
#include <iostream> #include <memory> template<typename T, typename C> class CallProxy { T* p; C c{}; public: CallProxy(T* p) : p{p} {} T* operator->() { return p; } }; template<typename T, typename C> class Wrapper { std::unique_ptr<T> p; public: template<typename... Args> Wrapper(Args&&... args) : p{std::make_unique<T>(std::forward<Args>(args)...)} {} CallProxy<T, C> operator->() { return CallProxy<T, C>{p.get()}; } }; struct PrefixSuffix { PrefixSuffix() { std::cout << "prefix\n"; } ~PrefixSuffix() { std::cout << "suffix\n"; } }; struct MyClass { void foo() { std::cout << "foo\n"; } }; int main() { Wrapper<MyClass, PrefixSuffix> w; w->foo(); }
定义一个PrefixSuffix
类,其前缀代码在其构造函数中,而后缀代码在析构函数中是一种方法。 然后,你可以使用Wrapper
类(使用->
来访问你的原始类的成员函数),并且每个调用都会执行前缀和后缀代码。
现场看到它。
感谢这篇文章 ,我find了解决办法。
作为一个侧面提示:如果要包装的class
没有virtual
函数,可以声明Wrapper::p
成员variables不是指针,而是作为一个普通对象 ,然后对Wrapper
的语义进行一些攻击箭头运算符 ; 结果是你将不会有更多的dynamic内存分配的开销。
你可以做一个包装,就像
class Foo { public: void a() { /*...*/ } void b() { /*...*/ } }; class LogFoo { public: template <typename ... Ts> LogFoo(Ts&&... args) : foo(std::forward<Ts>(args)...) {} const Foo* operator ->() const { log(); return &foo;} Foo* operator ->() { log(); return &foo;} private: void log() const {/*...*/} private: Foo foo; };
然后用->
代替.
:
LogFoo foo{/* args...*/}; foo->a(); foo->b();
使用lambdaexpression式和高阶函数来避免重复,并尽可能减less忘记调用log
的机会:
class Foo { private: void log(const std::string&) { } template <typename TF, typename... TArgs> void log_and_do(TF&& f, TArgs&&... xs) { log(std::forward<TArgs>(xs)...); std::forward<TF>(f)(); } public: void a() { log_and_do([this] { // `a` implementation... }, "Foo::a"); } void b() { log_and_do([this] { // `b` implementation... }, "Foo::b"); } };
这种方法的好处是,如果您决定改变日志logging行为,则可以更改log_and_do
而不是更改每个函数调用log
。 您还可以传递任何数量的额外参数来log
。 最后,它应该被编译器优化 – 它的行为就好像你已经写了一个调用手动log
每个方法。
您可以使用macros(叹气)来避免一些样板:
#define LOG_METHOD(...) \ __VA_ARGS__ \ { \ log_and_do([&] #define LOG_METHOD_END(...) \ , __VA_ARGS__); \ }
用法:
class Foo { private: void log(const std::string&) { } template <typename TF, typename... TArgs> void log_and_do(TF&& f, TArgs&&... xs) { log(std::forward<TArgs>(xs)...); std::forward<TF>(f)(); } public: LOG_METHOD(void a()) { // `a` implementation... } LOG_METHOD_END("Foo::a"); LOG_METHOD(void b()) { // `b` implementation... } LOG_METHOD_END("Foo::b"); };
我同意在你的原帖的评论上写的是什么,但是如果你真的需要这样做,而不喜欢使用Cmacros,你可以添加一个方法来调用你的方法。
下面是使用C ++ 2011正确处理不同函数参数的完整示例。 经GCC和叮当testing
#include <iostream> class Foo { void log() {} public: template <typename R, typename... TArgs> R call(R (Foo::*f)(TArgs...), const TArgs... args) { this->log(); return (this->*f)(args...); } void a() { std::cerr << "A!\n"; } void b(int i) { std::cerr << "B:" << i << "\n"; } int c(const char *c, int i ) { std::cerr << "C:" << c << '/' << i << "\n"; return 0; } }; int main() { Foo c; c.call(&Foo::a); c.call(&Foo::b, 1); return c.call(&Foo::c, "Hello", 2); }
是否有可能避免样板?
没有。
C ++具有非常有限的代码生成能力,自动注入代码不是其中的一部分。
免责声明:以下是代理中的深入探讨,呼吁用户不要绕过代理就不应该让他们的肮脏的爪子function,他们不应该调用。
是否可以忘记调用前/后function更难?
通过委托代理来强制委派是令人讨厌的。 具体来说,这些function不可能是public
, 也不可能是被protected
,否则主叫方可能会弄脏它们,你可能会被宣告没收。
因此,一个可能的解决scheme就是将所有function私有化,并提供执行日志logging的代理。 为了使这个规模跨越多个阶层而被抽象出来,这个规模是可怕的,但却是一次性成本:
template <typename O, typename R, typename... Args> class Applier { public: using Method = R (O::*)(Args...); constexpr explicit Applier(Method m): mMethod(m) {} R operator()(O& o, Args... args) const { o.pre_call(); R result = (o.*mMethod)(std::forward<Args>(args)...); o.post_call(); return result; } private: Method mMethod; }; template <typename O, typename... Args> class Applier<O, void, Args...> { public: using Method = void (O::*)(Args...); constexpr explicit Applier(Method m): mMethod(m) {} void operator()(O& o, Args... args) const { o.pre_call(); (o.*mMethod)(std::forward<Args>(args)...); o.post_call(); } private: Method mMethod; }; template <typename O, typename R, typename... Args> class ConstApplier { public: using Method = R (O::*)(Args...) const; constexpr explicit ConstApplier(Method m): mMethod(m) {} R operator()(O const& o, Args... args) const { o.pre_call(); R result = (o.*mMethod)(std::forward<Args>(args)...); o.post_call(); return result; } private: Method mMethod; }; template <typename O, typename... Args> class ConstApplier<O, void, Args...> { public: using Method = void (O::*)(Args...) const; constexpr explicit ConstApplier(Method m): mMethod(m) {} void operator()(O const& o, Args... args) const { o.pre_call(); (o.*mMethod)(std::forward<Args>(args)...); o.post_call(); } private: Method mMethod; };
注意:我不期望增加对volatile
支持,但没有人使用它,对吗?
一旦这个第一个障碍通过,你可以使用:
class MyClass { public: static const Applier<MyClass, void> a; static const ConstApplier<MyClass, int, int> b; void pre_call() const { std::cout << "before\n"; } void post_call() const { std::cout << "after\n"; } private: void a_impl() { std::cout << "a_impl\n"; } int b_impl(int x) const { return mMember * x; } int mMember = 42; }; const Applier<MyClass, void> MyClass::a{&MyClass::a_impl}; const ConstApplier<MyClass, int, int> MyClass::b{&MyClass::b_impl};
这是相当的样板,但至less模式是清楚的,任何违规行为都会像拇指一样突然出现。 以这种方式应用后期function也比较容易,而不是跟踪每一个return
。
要调用的语法并不是那么好:
MyClass c; MyClass::a(c); std::cout << MyClass::b(c, 2) << "\n";
应该可以做得更好…
请注意,理想情况下,您会希望:
- 使用数据成员
- 其types编码类的偏移量(安全)
- 其types编码调用的方法
一半的解决scheme是(中途因为不安全…):
template <typename O, size_t N, typename M, M Method> class Applier; template <typename O, size_t N, typename R, typename... Args, R (O::*Method)(Args...)> class Applier<O, N, R (O::*)(Args...), Method> { public: R operator()(Args... args) { O& o = *reinterpret_cast<O*>(reinterpret_cast<char*>(this) - N); o.pre_call(); R result = (o.*Method)(std::forward<Args>(args)...); o.post_call(); return result; } }; template <typename O, size_t N, typename... Args, void (O::*Method)(Args...)> class Applier<O, N, void (O::*)(Args...), Method> { public: void operator()(Args... args) { O& o = *reinterpret_cast<O*>(reinterpret_cast<char*>(this) - N); o.pre_call(); (o.*Method)(std::forward<Args>(args)...); o.post_call(); } }; template <typename O, size_t N, typename R, typename... Args, R (O::*Method)(Args...) const> class Applier<O, N, R (O::*)(Args...) const, Method> { public: R operator()(Args... args) const { O const& o = *reinterpret_cast<O const*>(reinterpret_cast<char const*>(this) - N); o.pre_call(); R result = (o.*Method)(std::forward<Args>(args)...); o.post_call(); return result; } }; template <typename O, size_t N, typename... Args, void (O::*Method)(Args...) const> class Applier<O, N, void (O::*)(Args...) const, Method> { public: void operator()(Args... args) const { O const& o = *reinterpret_cast<O const*>(reinterpret_cast<char const*>(this) - N); o.pre_call(); (o.*Method)(std::forward<Args>(args)...); o.post_call(); } };
它为每个“方法”添加一个字节(因为C ++很奇怪),并且需要一些相当复杂的定义:
class MyClassImpl { friend class MyClass; public: void pre_call() const { std::cout << "before\n"; } void post_call() const { std::cout << "after\n"; } private: void a_impl() { std::cout << "a_impl\n"; } int b_impl(int x) const { return mMember * x; } int mMember = 42; }; class MyClass: MyClassImpl { public: Applier<MyClassImpl, sizeof(MyClassImpl), void (MyClassImpl::*)(), &MyClassImpl::a_impl> a; Applier<MyClassImpl, sizeof(MyClassImpl) + sizeof(a), int (MyClassImpl::*)(int) const, &MyClassImpl::b_impl> b; };
但至less用法是“自然的”:
int main() { MyClass c; ca(); std::cout << cb(2) << "\n"; return 0; }
就个人而言,为了执行这个我只是简单地使用:
class MyClass { public: void a() { log(); mImpl.a(); } int b(int i) const { log(); return mImpl.b(i); } private: struct Impl { public: void a_impl() { std::cout << "a_impl\n"; } int b_impl(int x) const { return mMember * x; } private: int mMember = 42; } mImpl; };
不是非同寻常的,但是简单地隔离MyClass::Impl
的状态很难在MyClass
实现逻辑,这通常足以确保维护者遵循该模式。