1

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
F35H
  • 13
  • 6
  • Can you show a [mcve] so we can understand what you are trying to do? – Retired Ninja Dec 27 '22 at 22:42
  • 2
    ***Can you use the pimpl idiom for use across multiple cpp files with one class?*** Yes. You can define your private implementation in multiple cpp files. I find it a little odd however to ever want to split a class into multiple source files. – drescherjm Dec 27 '22 at 22:42
  • 1
    ***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.*** You are going to have to show an example. – drescherjm Dec 27 '22 at 22:43
  • Yes, simply but the defition inside another header that all cpp files import. – Yeladia Dec 27 '22 at 22:43
  • Yes, include the header in each `cpp` file. – Galik Dec 27 '22 at 22:50
  • If you have any static members of your pimpl class implement them in only 1 of the source files that you have split the implementation of the pimpl class across – drescherjm Dec 27 '22 at 22:58
  • 1
    "_without redefining the variables of the class_" What do you mean by "variables of the class"? – starball Dec 27 '22 at 22:59
  • 1
    Get `static std::shared_ptr PB = std::shared_ptr();` out of the header. Why do you have this `PB` variable at all? This is not really what PIMPL is about. – drescherjm Dec 27 '22 at 23:28

2 Answers2

2

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.

robthebloke
  • 9,331
  • 9
  • 12
  • I think the OP for some reason wants to use multiple cpp files to implement the functionality of the Foo class perhaps Foo1.cpp and Foo2.cpp where 1/2 of the functions of Foo are implemented in each. This sounds like an unusual way to implement a class but it can be done across multiple source files. – drescherjm Dec 27 '22 at 22:52
  • @drescherjm This is exactly what I want. Although, it's more of I want certain classes within each defined that reference each other through PB in each cpp file. – F35H Dec 27 '22 at 23:09
1

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.

bitmask
  • 32,434
  • 14
  • 99
  • 159
  • Although this is not the exact answer I needed, this told me what the problem actually was. – F35H Jan 08 '23 at 01:33