Is there a way to limit the repeating pattern when using the PIMPL idiom?

I have something like the following:

// 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(ab, cd); void baz(ab, cd, ef, gh, ij); void quux(ab, cd, ef, gh, ij); private: class impl; impl *m_pimpl; } 

Then:

 // foo.cpp: class foo::impl { public: void bar(ab, cd); void baz(ab, cd, ef, gh, ij); void quux(ab, cd, ef, gh, ij); private: // lots of private state, helper functions, etc. }; void foo::impl::bar(ab, cd) { ... } void foo::impl::baz(ab, cd, ef, gh, ij) { ... } void foo::impl::quux(ab, cd, ef, gh, ij) { ... } foo::foo() : m_pimpl(new impl()) { } foo::~foo() { delete m_pimpl; m_pimpl = NULL; } void foo::bar(ab, cd) { return m_pimpl->bar(b, d); } void foo::baz(ab, cd, ef, gh, ij) { return m_pimpl->baz(b, d, f, h, j) } void foo::quux(ab, cd, ef, gh, ij) { return m_pimpl->quux(b, d, f, h, j); } 

There are many repetitions of bar , baz and quux here:

  • Once the ad foo
  • As soon as the declaration foo::impl
  • Once in foo::impl definitions
  • Twice in foo definitions: once for foo list of function parameters and again to call the corresponding functions foo::impl .

For each of them, except the last, I have to write down the entire list of parameters, the types of which may be more involved.

What is the best way, if any, to reduce repetition here? One simple way is to embed the definition of public functions foo::impl , but is there anything beyond the fact that apart from designing classes, it is different to have fewer public functions?

+7
c ++ pimpl-idiom
source share
4 answers

To close this question: ultimately, I think Adrian comment is best suited to the problem: "I tend to implement impl class methods in the class definition, which reduces some of the repetitions."

My code is above:

 // 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(ab, cd) { ... code using m_pimpl-> when necessary ... } void foo::baz(ab, cd, ef, gh, ij) { ... code using m_pimpl-> when necessary ... } void foo::quux(ab, cd, ef, gh, ij) { ... code using m_pimpl-> when necessary ... } 

Now this is much more reasonable - only one for the announcement and one for the definition. There's a little overhead when converting a class to using pimpl to add m_pimpl-> s, but IMO is less annoying than all repetitions.

0
source share

Using signatures, we can reduce it to 3 mentions plus 1 set of forwards:

 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 << fx(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;} 

We can get it up to 2 + forward if our pimpl is a pure virtual class. Write a map from decltype(&foo::method) signatures and use a clean virtual interface (and decltype) to create a signature of a pure virtual class.

Write the signature once in foo , decltype - define it in foo_impl_interface , and then insert it inline inside foo_impl into the cpp file. Plus one set forward.

  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...); }; 

plus another 11 odd specializations (sigh) to get all the cases. ( const& const const&& const volatile const volatile& const volatile&& & && volatile volatile& volatile&& classifiers - all, as far as I can tell, is required).

Now method_sig_t<decltype(&foo::x)> is an int(int) that we can use:

 struct foo_impl_interface { virtual method_sig_t<decltype(&foo::x)> x = 0; virtual ~foo_impl_interface() {} }; 

Assuming we use pimpl to regularize our type, and not to hide the state.


Finally, instead of implementing most of your code in pimpl, instead just store STATE in pimpl, leave the code in the class itself.

This gives you "I, others, does not depend on my size": who cares if the code is in foo or foo_impl that reads the state of foo_impl ? So, if you are not using the foo_impl_interface method, why forward?

+4
source share

If your public class is simply a proxy accessing an implementation, consider using an interface β†’ an implementation model instead of pimpl.

You only reference your implementation using an interface in user code with class foo being an interface

 class foo { public: virtual void bar(ab, cd) = 0; virtual void baz(ab, cd, ef, gh, ij) = 0; virtual void quux(ab, cd, ef, gh, ij) = 0; virtual ~foo(){} } 

The idea behind pimpl is to store personal data and functions in a separate object pointer so that you don’t break your open class interface in case of data storage and change processing. But this does not mean that all code is moved to a private object. Therefore, you usually perform your public functions locally. You do not violate the user code when changing the implementation of public functions, since the implementation of your public functions will be hidden from the user along with the definition of a private class.

+2
source share

One way to do this is to define an interface class, so you don't need to update everything and use the through pointer semantics (overloaded operator β†’)

Here is an example: Is it possible to write flexible Pimpl in C ++?

0
source share

All Articles