Pimpl my class: autogenerate PImpl boilerplate#
PImpl is a common C++ programming technique that removes implementation details of a class from its object representation by placing them in a separate class, accessed through an opaque pointer.
Its advantages are:
Real code encapsulation (as opposed hiding details in private members, which remain clearly visible in the header file)
ABI stability is ensured (since the exposed class ABI consists of only one pointer)
Improve compilation time (since only the implemenation .cpp file will include the required dependencies)
Its drawbacks are:
An additional indirection for method calls
Writing a PImpl lead to a lot of boilerplate code, which is often a tedious to write
srcmlcpp_tools.pimpl_my_class
enables you to automate the generation of the boiler code: given the source code of a PImpl class implementation it will generate two outputs:
a header file with the API that is exposed
some glue code that should be added to the C++ file
A simple example#
Let’s take an example.
Suppose we have this PImpl code somewhere in a C++ file of a class MyClassPImpl
, and that we want to:
publish
MyClassPImpl
asMyClass
in the header file,publish all public methods,
publish all the documentation present in the PImpl class
First, let’s show the original code, that defines MyClassPImpl
:
from srcmlcpp_tools import pimpl_my_class
from litgen.demo import litgen_demo
cpp_code = pimpl_my_class.demo_cpp_no_marker()
litgen_demo.show_cpp_code(cpp_code, "MyClassPImpl cpp file")
#include "my_class.h"
#include <string>
#include <future>
// Some doc about the class, that you want to see in the header file
class MyClassPImpl
{
public:
//
// Some doc you also want to see in the header file, since it is in a public block
//
// Construct an Instance
MyClassPImpl(const std::string& someVar)
{
// Some code you provide in the C++ file, but do not want to see in the header file
}
// Some method
bool SomeMethod() { /* ... */ }
// Some public static method
static bool SomeStaticMethod() { /* ... */ }
// The destructor should not be published as is, but should use the unique_ptr default destructor
~MyClassPImpl() { /* ... */ }
private:
//
// Some private doc, which should not be published
//
bool SomePrivateMethod() { /* ... */ }
std::string mSomePrivateMember;
std::future<void> mAnoterPrivateMember;
};
We can generate the PImpl boilerplate code by calling pimpl_my_code
:
pimpl_result = pimpl_my_class.pimpl_my_code(cpp_code)
if pimpl_result is not None:
litgen_demo.show_cpp_code(pimpl_result.header_code, "Header Code")
litgen_demo.show_cpp_code(pimpl_result.glue_code, "Glue Code (add this at the bottom of the C++ file)")
class MyClassPImpl;
// Some doc about the class, that you want to see in the header file
class MyClass
{
public:
//
// Some doc you also want to see in the header file, since it is in a public block
//
// Construct an Instance
MyClass(const std::string & someVar);
// Some method
bool SomeMethod();
// Some public static method
static bool SomeStaticMethod();
~MyClass();
private:
std::unique_ptr<MyClassPImpl> mPImpl;
};
MyClass::MyClass(const std::string & someVar)
: mPImpl(std::make_unique<MyClassPImpl>(someVar)) { }
bool MyClass::SomeMethod() {
return mPImpl->SomeMethod(); }
bool MyClass::SomeStaticMethod() {
return MyClassPImpl::SomeStaticMethod(); }
MyClass::~MyClass() = default;
Running PImplMyCode on a whole codebase#
By calling pimpl_my_class.pimpl_my_file
it is possible to automatically add the generated code to a C++ and a header file.
These file should include markers (<pimpl_glue_code>
, </pimpl_glue_code>
, <pimpl_header_code>
, and </pimpl_header_code>
), to mark where the code should be added.
In order to keep this documentation short, we will automatically generate two example files (a C++ and a header file). Let’s show their content:
from srcmlcpp_tools import pimpl_my_class
cpp_file, header_file = pimpl_my_class.create_pimpl_example_files()
litgen_demo.show_cpp_file(cpp_file)
litgen_demo.show_cpp_file(header_file)
#include "my_class.h"
struct MyStructPImpl
{
MyStructPImpl() { /* ...*/ }
bool SomeMethod() { /* ... */ }
private:
bool SomePrivateMethod() { /* ... */ }
int mSomePrivateMember;
};
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AUTOGENERATED CODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// <pimpl_glue_code> // Autogenerated code below! Do not edit!
// </pimpl_glue_code> // Autogenerated code end
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AUTOGENERATED CODE END !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#pragma once
#include <memory>
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AUTOGENERATED CODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// <pimpl_header_code> // Autogenerated code below! Do not edit!
// </pimpl_header_code> // Autogenerated code end
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AUTOGENERATED CODE END !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
By running pimpl_my_file
, these files will be modified, and the generated code will be inserted between the markers.
Below, we run pimpl_my_file
and show the content of the files after it was run.
from srcmlcpp_tools.pimpl_my_class import pimpl_my_file, PimplOptions
pimpl_options = PimplOptions()
pimpl_options.pimpl_suffixes = ["PImpl"]
pimpl_my_file(cpp_file, header_file)
litgen_demo.show_cpp_file(cpp_file)
litgen_demo.show_cpp_file(header_file)
#include "my_class.h"
struct MyStructPImpl
{
MyStructPImpl() { /* ...*/ }
bool SomeMethod() { /* ... */ }
private:
bool SomePrivateMethod() { /* ... */ }
int mSomePrivateMember;
};
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AUTOGENERATED CODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// <pimpl_glue_code> // Autogenerated code below! Do not edit!
MyStruct::MyStruct()
: mPImpl(std::make_unique<MyStructPImpl>()) { }
bool MyStruct::SomeMethod() {
return mPImpl->SomeMethod(); }
MyStruct::~MyStruct() = default;
// </pimpl_glue_code> // Autogenerated code end
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AUTOGENERATED CODE END !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#pragma once
#include <memory>
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AUTOGENERATED CODE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// <pimpl_header_code> // Autogenerated code below! Do not edit!
struct MyStructPImpl;
struct MyStruct
{
MyStruct();
bool SomeMethod();
~MyStruct();
private:
std::unique_ptr<MyStructPImpl> mPImpl;
};
// </pimpl_header_code> // Autogenerated code end
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! AUTOGENERATED CODE END !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!