Reputation: 7349
We have a heavily-templated header-only codebase that a client would like access to. For example, let's say it contains the Foo
class in the header foo.hpp
:
#ifndef FOO_HEADER
#define FOO_HEADER
#include <iostream>
template <typename T>
struct Foo {
Foo(){
// Do a bunch of expensive initialization
}
void bar(T t){
std::cout << t;
}
// Members to initialize go here...
};
#endif /* FOO_HEADER */
Now we want to let the client try a reduced set of the functionality without exposing the core code and without rewriting the whole codebase.
One idea would be to use the PIMPL idiom to wrap this core code. Specifically, we could create a FooWrapper
class with the header foo_wrapper.hpp
:
#ifndef FOO_WRAPPER_HEADER
#define FOO_WRAPPER_HEADER
#include <memory>
struct FooWrapper {
FooWrapper();
~FooWrapper();
void bar(double t);
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
#endif /* FOO_WRAPPER_HEADER */
and implementation foo_wrapper.cpp
:
#include "foo.hpp"
#include "foo_wrapper.hpp"
struct FooWrapper::Impl {
Foo<double> genie;
};
void FooWrapper::bar(double t){
impl->genie.bar(t);
}
FooWrapper::FooWrapper() : impl(new Impl){
}
FooWrapper::~FooWrapper() = default;
This code works as I expect it: https://wandbox.org/permlink/gso7mbe0UEOOPG7j
However, there is one little nagging thing that is bothering me. Specifically, the implementation requires what feels like an additional level of indirection... We have to define the Impl
class to hold a member of the Foo
class. Because of this, all of the operations have this indirection of the form impl->genie.bar(t);
.
It would be better if we could somehow tell the compiler, "Actually Impl
IS the class Foo<double>
", in which case, we could instead say impl->bar(t);
.
Specifically, I am thinking something along the lines of typedef
or using
to get this to work. Something like
using FooWrapper::Impl = Foo<double>;
But this does not compile. So on to the questions:
I am targeting a C++11 solution, but C++14 may work as well. The important thing to remember is that the solution can't use the header foo.hpp
in foo_wrapper.hpp
. Somehow we have to compile that code into a library and distribute just the compiled library and the foo_wrapper
header.
Upvotes: 3
Views: 343
Reputation: 75698
Just use Foo<double>
:
// forward declaration so that you don't need to include "Foo.hpp"
template class Foo<double>;
struct FooWrapper {
//...
std::unique_ptr<Foo<double>> impl;
};
// explicit template instantiation so that Foo<double> exists without distributing "Foo.hpp"
template class Foo<double>;
void FooWrapper::bar(double t){
impl->bar(t);
}
Upvotes: 1
Reputation: 2868
You can just forward-declare Foo
in FooWrapper.h
. This will allow you to declare a std::unique_ptr
for it:
#ifndef FOO_WRAPPER_HEADER
#define FOO_WRAPPER_HEADER
#include <memory>
// Forward declaration
template <typename T>
class Foo;
struct FooWrapper {
FooWrapper();
~FooWrapper();
void bar(double t);
private:
std::unique_ptr<Foo<double>> impl;
};
#endif /* FOO_WRAPPER_HEADER */
foo_wrapper.cc
:
#include "foo_wrapper.h"
#include "foo.h"
void FooWrapper::bar(double t) {
impl->bar(t);
}
FooWrapper::FooWrapper() : impl(std::make_unique<Foo<double>>()) {}
FooWrapper::~FooWrapper() = default;
Upvotes: 4