是否可以避免样板文件?
没有。
C++ 的代码生成能力非常有限,自动注入代码不是其中的一部分。
免责声明:以下是对代理的深入探讨,其调用是为了防止用户在不绕过代理的情况下不应该调用的函数获得他们肮脏的爪子。
是否有可能更难忘记调用前/后函数?
通过代理强制执行委派……很烦人。具体来说,函数不能可能是public 或protected,否则调用者可能会得到它们的脏手,你可能会宣布放弃。
因此,一种可能的解决方案是将所有函数声明为私有,并提供强制执行日志记录的代理。抽象这一点,以使这种规模跨多个类,是可怕的样板,尽管它是一次性成本:
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};
这是相当的样板,但至少模式是明确的,任何违规都会像拇指酸痛一样突出。以这种方式应用后期函数也更容易,而不是跟踪每个return。
调用的语法也不是很好:
MyClass c;
MyClass::a(c);
std::cout << MyClass::b(c, 2) << "\n";
应该可以做得更好……
请注意,理想情况下您希望:
- 使用数据成员
- 其类型将偏移量编码到类(安全)
- 其类型编码要调用的方法
半途而废的解决方案是(半途而废,因为不安全......):
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;
};
但至少用法是“自然的”:
int main() {
MyClass c;
c.a();
std::cout << c.b(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 中的逻辑难以实现,这通常足以确保维护者遵循该模式。