使用 PIMPL 习语时,有什么方法可以限制重复的样板文件吗?
Is there any way to limit repetitive boilerplate when using the PIMPL idiom?
我有如下内容:
// foo.h:
class foo {
public:
foo();
~foo();
// note: the param type repetition here is only incidental, assume the
// functions can't easily be made to share type signatures
void bar(a b, c d);
void baz(a b, c d, e f, g h, i j);
void quux(a b, c d, e f, g h, i j);
private:
class impl;
impl *m_pimpl;
}
然后:
// foo.cpp:
class foo::impl {
public:
void bar(a b, c d);
void baz(a b, c d, e f, g h, i j);
void quux(a b, c d, e f, g h, i j);
private:
// lots of private state, helper functions, etc.
};
void foo::impl::bar(a b, c d) { ... }
void foo::impl::baz(a b, c d, e f, g h, i j) { ... }
void foo::impl::quux(a b, c d, e f, g h, i j) { ... }
foo::foo() : m_pimpl(new impl()) { }
foo::~foo() { delete m_pimpl; m_pimpl = NULL; }
void foo::bar(a b, c d) {
return m_pimpl->bar(b, d);
}
void foo::baz(a b, c d, e f, g h, i j) {
return m_pimpl->baz(b, d, f, h, j)
}
void foo::quux(a b, c d, e f, g h, i j) {
return m_pimpl->quux(b, d, f, h, j);
}
这里 bar
、baz
和 quux
有很多重复:
- 曾经在
foo
的声明中
- 曾经在
foo::impl
的声明中
- 曾经在
foo::impl
的定义中
- 两次在
foo
的定义中:一次用于foo
的函数参数列表,一次调用相应的foo::impl
函数。
对于除最后一个之外的每一个,我都必须写出整个参数列表,其类型可以涉及更多。
减少此处重复的最佳方法是什么(如果有的话)?一个简单的方法是内联 foo::impl
public 函数的定义,但是除了以不同方式设计 类 以减少 public 函数之外,还有什么吗?
如果您的 public class 只是代理对实现的调用,请考虑使用接口 -> 实现模型而不是 pimpl。
您只能通过用户代码中的接口引用您的实现,class foo
是接口
class foo {
public:
virtual void bar(a b, c d) = 0;
virtual void baz(a b, c d, e f, g h, i j) = 0;
virtual void quux(a b, c d, e f, g h, i j) = 0;
virtual ~foo(){}
}
pimpl 背后的想法是将 private 数据和函数存储在单独的对象指针中,这样您就不会破坏 public class 接口,以防万一数据存储和处理变化。但这并不意味着所有代码都移到了私有对象中。因此,您通常会就地实施 public 功能。当您的 public 函数 实现 更改时,您不会破坏用户代码,因为您的 public 函数实现将与私有 [=24= 一起对用户隐藏]定义。
使用签名,我们可以将其减少到 3 次提及,加上 1 组转发:
using signature_1 = int(int);
struct foo {
signature_1 x;
foo();
~foo();
private:
struct fooimpl;
std::unique_ptr<fooimpl> pimpl;
};
int main() {
foo f;
std::cout << f.x(1) << '\n';
}
struct foo::fooimpl {
signature_1 x;
int v = 3;
};
foo::~foo() = default;
foo::foo():pimpl(new foo::fooimpl()){}
int foo::x(int y){return pimpl->x(y);}
int foo::fooimpl::x(int y){return y+v;}
如果我们的 pimpl 是纯虚拟的,我们可以将它降低到 2+forward class。从 decltype(&foo::method)
->signature 写一个映射,并让纯虚拟接口使用它(和 decltype)来发明纯虚拟 class.
的签名
在foo
中写一次签名,在foo_impl_interface
中decltype-确定,然后在cpp文件中foo_impl
中内联实现。加上一组前锋。
template<class MethodPtrType>
struct method_sig;
template<class MethodPtrType>
using method_sig_t = typename method_sig<MethodPtrType>::type;
template<class T, class R, class...Args>
struct method_sig< R(T::*)(Args...) > {
using type=R(Args...);
};
加上另外11个奇怪的专业化(叹气)来获得所有案例。 (const&
const
const&&
const volatile
const volatile&
const volatile&&
&
&&
</code> <code>volatile
volatile&
volatile&&
据我所知,限定词都是必需的)。
现在method_sig_t<decltype(&foo::x)>
是int(int)
,我们可以使用:
struct foo_impl_interface {
virtual method_sig_t<decltype(&foo::x)> x = 0;
virtual ~foo_impl_interface() {}
};
假设我们使用 pimpl 来规范我们的类型而不是隐藏状态。
最后,不要在 pimpl 中实现大部分代码,而只是将 STATE 存储在 pimpl 中,将代码留在 class 本身。
这给了你 "I others do not depend on my size":谁在乎代码是处于 foo
还是 foo_impl
读取 foo_impl
的状态?所以,如果您不使用 foo_impl_interface
技术,为什么要转发?
实现此目的的一种方法是定义一个接口class,这样您就不需要重新声明所有内容并使用直通指针语义(重载运算符->)
这是一个例子:
Is it possible to write an agile Pimpl in c++?
结束这个问题:最终我认为 最能解决这个问题:"I tend to implement the impl class's methods in the class definition, which reduces some of the repetition."
我上面的代码会变成:
// foo.cpp:
class foo::impl {
public:
// lots of private state, helper functions, etc.
};
foo::foo() : m_pimpl(new impl()) { }
foo::~foo() { delete m_pimpl; m_pimpl = NULL; }
void foo::bar(a b, c d) {
... code using m_pimpl-> when necessary ...
}
void foo::baz(a b, c d, e f, g h, i j) {
... code using m_pimpl-> when necessary ...
}
void foo::quux(a b, c d, e f, g h, i j) {
... code using m_pimpl-> when necessary ...
}
现在更合理了——只有一个用于声明,一个用于定义。将 class 转换为使用添加 m_pimpl->
s 的 pimpl 时,开销很小,但在我看来,这比所有重复都没有那么烦人。
我有如下内容:
// foo.h:
class foo {
public:
foo();
~foo();
// note: the param type repetition here is only incidental, assume the
// functions can't easily be made to share type signatures
void bar(a b, c d);
void baz(a b, c d, e f, g h, i j);
void quux(a b, c d, e f, g h, i j);
private:
class impl;
impl *m_pimpl;
}
然后:
// foo.cpp:
class foo::impl {
public:
void bar(a b, c d);
void baz(a b, c d, e f, g h, i j);
void quux(a b, c d, e f, g h, i j);
private:
// lots of private state, helper functions, etc.
};
void foo::impl::bar(a b, c d) { ... }
void foo::impl::baz(a b, c d, e f, g h, i j) { ... }
void foo::impl::quux(a b, c d, e f, g h, i j) { ... }
foo::foo() : m_pimpl(new impl()) { }
foo::~foo() { delete m_pimpl; m_pimpl = NULL; }
void foo::bar(a b, c d) {
return m_pimpl->bar(b, d);
}
void foo::baz(a b, c d, e f, g h, i j) {
return m_pimpl->baz(b, d, f, h, j)
}
void foo::quux(a b, c d, e f, g h, i j) {
return m_pimpl->quux(b, d, f, h, j);
}
这里 bar
、baz
和 quux
有很多重复:
- 曾经在
foo
的声明中 - 曾经在
foo::impl
的声明中 - 曾经在
foo::impl
的定义中 - 两次在
foo
的定义中:一次用于foo
的函数参数列表,一次调用相应的foo::impl
函数。
对于除最后一个之外的每一个,我都必须写出整个参数列表,其类型可以涉及更多。
减少此处重复的最佳方法是什么(如果有的话)?一个简单的方法是内联 foo::impl
public 函数的定义,但是除了以不同方式设计 类 以减少 public 函数之外,还有什么吗?
如果您的 public class 只是代理对实现的调用,请考虑使用接口 -> 实现模型而不是 pimpl。
您只能通过用户代码中的接口引用您的实现,class foo
是接口
class foo {
public:
virtual void bar(a b, c d) = 0;
virtual void baz(a b, c d, e f, g h, i j) = 0;
virtual void quux(a b, c d, e f, g h, i j) = 0;
virtual ~foo(){}
}
pimpl 背后的想法是将 private 数据和函数存储在单独的对象指针中,这样您就不会破坏 public class 接口,以防万一数据存储和处理变化。但这并不意味着所有代码都移到了私有对象中。因此,您通常会就地实施 public 功能。当您的 public 函数 实现 更改时,您不会破坏用户代码,因为您的 public 函数实现将与私有 [=24= 一起对用户隐藏]定义。
使用签名,我们可以将其减少到 3 次提及,加上 1 组转发:
using signature_1 = int(int);
struct foo {
signature_1 x;
foo();
~foo();
private:
struct fooimpl;
std::unique_ptr<fooimpl> pimpl;
};
int main() {
foo f;
std::cout << f.x(1) << '\n';
}
struct foo::fooimpl {
signature_1 x;
int v = 3;
};
foo::~foo() = default;
foo::foo():pimpl(new foo::fooimpl()){}
int foo::x(int y){return pimpl->x(y);}
int foo::fooimpl::x(int y){return y+v;}
如果我们的 pimpl 是纯虚拟的,我们可以将它降低到 2+forward class。从 decltype(&foo::method)
->signature 写一个映射,并让纯虚拟接口使用它(和 decltype)来发明纯虚拟 class.
在foo
中写一次签名,在foo_impl_interface
中decltype-确定,然后在cpp文件中foo_impl
中内联实现。加上一组前锋。
template<class MethodPtrType>
struct method_sig;
template<class MethodPtrType>
using method_sig_t = typename method_sig<MethodPtrType>::type;
template<class T, class R, class...Args>
struct method_sig< R(T::*)(Args...) > {
using type=R(Args...);
};
加上另外11个奇怪的专业化(叹气)来获得所有案例。 (const&
const
const&&
const volatile
const volatile&
const volatile&&
&
&&
</code> <code>volatile
volatile&
volatile&&
据我所知,限定词都是必需的)。
现在method_sig_t<decltype(&foo::x)>
是int(int)
,我们可以使用:
struct foo_impl_interface {
virtual method_sig_t<decltype(&foo::x)> x = 0;
virtual ~foo_impl_interface() {}
};
假设我们使用 pimpl 来规范我们的类型而不是隐藏状态。
最后,不要在 pimpl 中实现大部分代码,而只是将 STATE 存储在 pimpl 中,将代码留在 class 本身。
这给了你 "I others do not depend on my size":谁在乎代码是处于 foo
还是 foo_impl
读取 foo_impl
的状态?所以,如果您不使用 foo_impl_interface
技术,为什么要转发?
实现此目的的一种方法是定义一个接口class,这样您就不需要重新声明所有内容并使用直通指针语义(重载运算符->)
这是一个例子: Is it possible to write an agile Pimpl in c++?
结束这个问题:最终我认为
我上面的代码会变成:
// foo.cpp:
class foo::impl {
public:
// lots of private state, helper functions, etc.
};
foo::foo() : m_pimpl(new impl()) { }
foo::~foo() { delete m_pimpl; m_pimpl = NULL; }
void foo::bar(a b, c d) {
... code using m_pimpl-> when necessary ...
}
void foo::baz(a b, c d, e f, g h, i j) {
... code using m_pimpl-> when necessary ...
}
void foo::quux(a b, c d, e f, g h, i j) {
... code using m_pimpl-> when necessary ...
}
现在更合理了——只有一个用于声明,一个用于定义。将 class 转换为使用添加 m_pimpl->
s 的 pimpl 时,开销很小,但在我看来,这比所有重复都没有那么烦人。