I am trying to understand this use case.
I do understand that one of the objectives of pimpl is to hide the implementation from the users. This means that for the users, they only see the visible class definition in the header file, but not the implementation class.
For class templates, if you do not include the template code for member functions in the header file, you may run into various linkage errors.
If we have a combination of both, I am not sure how I can get this compiled and at the same time hiding the implementation from the users of the visible class.
Let's say, if I try to do a BigInt class,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
// bigint.h:
#ifndef BIGINT_H
#define BIGINT_H
#include <memory>
template<int ndigits>
class BigInt {
public:
BigInt();
BigInt(int x);
BigInt(const BigInt &);
BigInt(const BigInt &&); // default in bigint.cpp
BigInt &operator=(const BigInt &);
BigInt &operator=(const BigInt &&); // default in bigint cpp
BigInt &operator+=(const BigInt &x);
~BigInt(); // default in bigint.cpp
// ...
// void print(std::ostream &os) const;
private:
class BigIntImpl;
std::unique_ptr<BigIntImpl> pimpl_;
};
template<int ndigits>
extern BigInt<ndigits> operator+(const BigInt<ndigits> &x, const BigInt<ndigits> &y);
// *** later I added this line and some other stuff (labeled as ***) to get it compiled
// #include "bigint.cpp"
#endif
// bigint.cpp
#include "bigint.h" // *** I take out this line as well later
template<int ndigits>
class BigInt<ndigits>::BigIntImpl {
public:
BigIntImpl() {}
BigIntImpl(int x) {}
};
template<int ndigits>
BigInt<ndigits>::BigInt() : pimpl_(std::make_unique<BigInt<ndigits>::BigIntImpl>()) {}
template <int ndigits>
BigInt<ndigits>::BigInt(const BigInt<ndigits> &x) : pimpl_(std::make_unique<BigInt<ndigits>::BigIntImpl>(*x.pimpl_)) {}
template <int ndigits>
BigInt<ndigits>::BigInt(int x) : pimpl_(std::make_unique<BigInt<ndigits>::BigIntImpl>(x)) {}
template <int ndigits>
BigInt<ndigits>::~BigInt() = default;
// test.cpp
#include "bigint.h"
// *** I added in the following line to get it compile
// I do not understand why it is needed!!! without this, linkage error again
// #include "bigint.cpp"
int main(int argc, char *argv[]) {
BigInt<2> a{10};
BigInt<2> b{20};
}
// compile
joe@hehehe:~/Projects/cpp/lint/t$ g++ -std=c++14 test.cpp bigint.cpp
/tmp/cc0RM1wj.o: In function `main':
test.cpp:(.text+0x2c): undefined reference to `BigInt<2>::BigInt(int)'
test.cpp:(.text+0x3d): undefined reference to `BigInt<2>::BigInt(int)'
test.cpp:(.text+0x49): undefined reference to `BigInt<2>::~BigInt()'
test.cpp:(.text+0x55): undefined reference to `BigInt<2>::~BigInt()'
test.cpp:(.text+0x7a): undefined reference to `BigInt<2>::~BigInt()'
collect2: error: ld returned 1 exit status
|
I do know that as I am creating a BigInt<2> object in main(), and BigInt<N> template is defined, but not BigInt<2>, and hence the linkage error.
If I did some modifications to the code above (marked with ***), and I got it compiled. However, this is something I don't quite understand. I know, for templates to link properly, the member function definitions should be available
in the header file, which I did with #include "bigint.cpp" in bigint.h. But why is it I have to do this again in test.cpp (where my main() function is)?
Again, by doing an include of the cpp file, you cannot make this as a library where users only need to #include your header, and compiled and linked against the library.
If you have any insights into this, appreciate if you let me know. Thanks.
Daniel.