Hiding implementation details, reducing the number of filled headers

I am developing a library. I have an interface class that will be called externally.

I also have an internal engine that cannot be called from outside.

As I read here and there, I have to hide the inner class of the engine and not even fill in its title. Since I have the following structure:

interface.hpp:

#include "engine.hpp" class interface{ private: enigne eng; }; 

interface.cpp:

 #include "engine.hpp" //code that uses member variables and functions from eninge 

engine.hpp:

 class engine{}; 

To solve the problem of filling "engine.hpp", I have to change the code to:

interface.hpp:

 class engine; class interface{ private: some_smart_pointer<enigne> eng_ptr; }; 

interface.cpp:

 #include "engine.hpp" //code that uses member variables and functions from eninge 

enigne.hpp:

 class engine{}; 

This solved the problem. However, the engine is now dynamically allocated. All its member variables are in free storage.

I can’t understand that I need to change my design and allocate a mechanism in free storage to solve the problem of hiding implementation details. Is there a better solution?

PS I do not ask why this solution works. I know that knowing the size class of an engine is mandatory if I leave it on the stack. My question is to ask for a different design that can solve the problem.

EDIT:

Both interface and engine have member variables.

+7
c ++ c ++ 11
source share
4 answers

You are using the PIMPL idiom. Another way to hide the implementation is to use interfaces, i.e. Abstract base class and factory function:

interface.hpp:

 class interface{ public: virtual ~interface(){} virtual void some_method() = 0; }; // the factory function static some_smart_pointer<interface> create(); 

interface.cpp:

 #include "interface.hpp" #include "engine.hpp" class concrete : public interface{ public: virtual void some_method() override { /* do something with engine */ } private: engine eng; }; some_smart_pointer<interface> create(){ return new concrete; } 

main.cpp

 #include "interface.hpp" int main() { auto interface = create(); interface->some_method(); return 0; } 

The downside here is that you have to dynamically allocate interface instead of engine .

Read more about PIMPL and interfaces here and here.

EDIT:

Based on the STL containers and Howard Hinnant stack allocator , a third way to avoid variables in free storage could be:

interface.hpp:

 class interface{ public: interface(); ~interface(); // I disable copy here, but you can implement them interface(const interface&) = delete; interface& operator=(interface&) = delete; private: engine* eng; }; 

interface.cpp:

 #include "interface.hpp" #include "engine.hpp" #include "short_alloc.h" #include <map> namespace { std::map< interface*, engine, std::default_order<interface*>, // use a stack allocator of 200 bytes short_alloc<std::pair<interface*, engine>, 200> > engines; } interface::interface(): eng(&engines[this]) // operator[] implicitly creates an instance and returns a reference to it // the pointer gets the address { } interface::~interface() { // destroy the instance engines.erase(this); } 
+4
source share

I can’t understand that I need to change my design and allocate a mechanism in free storage to solve the problem of hiding implementation details.

You seem to have already explained this yourself:

I know this about knowing the size class of an engine

Really. If the interface had a member of type engine , then it must have known the size of this member, otherwise the size of the interface could not be known. The size of an incomplete type cannot be known. Determining the type of an element would solve this, but it conflicts with your desire to hide the implementation.

Is there a better solution?

There is no better solution. PIMPL with free storage, which is the template you are currently using, is just as good as it is.

Technically, PIMPL does not require you to use a free store. You can store the object anywhere. You can use static storage. But this will limit the number of instances you can have. You can even allocate a memory buffer as a member of the interface , but you need to hardcode the size of such a buffer, and it must match the size (and alignment) of the engine .

I would classify both of these theoretical sentences as kludges - the latter in particular. Static storage might be worth it if your profiling assumes that the overhead of this extra allocation is significant and abandoning PIMPL is not an option.

+3
source share

This is the standard solution for this problem: it is called XIMI PIMPL (implementation pointer). And, as the name implies, it always includes a pointer (or smart pointer) to the implementation class, because C ++ simply does not allow this:

 class engine; class interface{ private: enigne eng; }; 
+2
source share

A typical solution for hiding implementations rather than forced objects that must be allocated on the heap is to put them in the detail namespace and place the headers for these types in a detailed subdirectory.

Please note that some aspect of your implementation will be revealed, as this will affect the ABI of your library. There is no solution that can give you ABI isolation and avoid allocating objects on the heap.

Most likely you are better off using pimpl as you described.

+1
source share

All Articles