For instance, here is what I mean. Let's say you have a single header file with a single pimpl class. Can you define the functions of this class across two cpp files without redefining the variables of the class?
I've tried this before using a static variable for the pointer and a redefinition in both files. I keep running into issues regarding class variables being erased when moving across files, however.
//Header
class PRIVATE {
struct Test2;
public:
struct Test;
std::shared_ptr<Test> Client_ptr;
PRIVATE();
}; //PRIVATE
static std::shared_ptr<PRIVATE> PB = std::shared_ptr<PRIVATE>();
//Cpp1
//Implementation for Private
//Implementation for Test1
//Function not inside either class, references PB, defined in Cpp2 -> READ ACCESS VIOLATION
//Cpp2
//Definition Goes Here
//Implementation for Test2
//Function not inside either class, references PB, defined in Cpp1 -> READ ACCESS VIOLATION
CodePudding user response:
Usually with this kind of thing, you'd have a public header, e.g.
#pragma once
class Foo {
Foo();
~Foo();
private:
struct FooPimpl;
FooPimpl* pimpl;
};
Then you'd have a second private header file (typically in your source directory, rather than include dir). The private header would define your Pimpl struct type.
#pragma once
struct Foo::FooPimpl {
/*stuff*/
};
You'd need to declare your ctors / dtors somewhere, e.g.
Foo.cpp
#include "public/Foo.h"
#include "./Foo.h"
Foo::Foo() {
pimpl = new FooPimpl;
}
Foo::~Foo() {
delete pimpl;
}
And you can use that same pattern (e.g. include public header, then private header) for all your other source files.
CodePudding user response:
One issue with splitting pimpl internals across separate compilation units is that you need at least one compilation unit that knows how to destroy all members.
For example:
//main.h
class Main {
struct Test;
struct Test2;
std::unique_ptr<Test> pimpl1;
std::unique_ptr<Test2> pimpl2;
public:
Main();
~Main();
};
//test1.cpp
struct Main::Test {
};
// we don't know what Test2 is, so we cannot define Main::~Main().
//test2.cpp
struct Main::Test2 {
};
// we don't know what Test is, so we cannot define Main::~Main().
One solution is this:
class Main {
struct Test;
struct Test2;
std::unique_ptr<Test> pimpl1;
struct Defer {
std::unique_ptr<Test2> pimpl2;
Defer();
~Defer();
};
Defer defer;
public:
Main();
~Main();
};
Now, Main::Defer::~Defer()
can live in test2.cpp where it knows how to destroy its pimpl2 instance but doesn't need to know how to destroy pimpl. Similarly, since Main::Defer
is defined (not just declared) in main.h, Main::~Main()
can properly destroy its defer
member:
//test1.cpp
/* Test definition */
Main::Main() : pimpl(std::make_unique<Test>()), defer() {}
Main::~Main() {}
//test2.cpp
/* Test2 definition */
Main::Defer::Defer() : pimpl2(std::make_unique<Test2>()) {}
Main::Defer::~Defer() {}
It's still difficult to have Test
and Test2
talk between each other but that is kind of the point of pimpl. It can be facilitated by Main offering some interfaces or by some intermediate header that declares some interfaces.