C++ Pimpl Idiom using a pre-existing class - c++

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:
Is there a nice way to get rid of this indirection?
Is there a better idiom I should be using?
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.

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;

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);
}

Related

How to use a dllexport-ed class which is derived from an explicitly instantiated template in a dll without warnings?

So I have a dll which exports class which is derived from an explicitly instantiated (also exported) template.
parent.hpp
#pragma once
template <typename T>
struct parent {
parent(T t) m_t(t) {};
void print();
T m_t;
};
parent.cpp
template<typename T>
void parent<T>::print() {
cout << m_t << endl;
}
template class LIB_API parent<int>;
child.hpp
#include "parent.hpp"
extern template class parent<int>;
struct LIB_API child : public parent<int> {
using parent<int>::parent;
void some_method();
}
child.cpp defines some_method
So far everything is great and works. I can safely use the child class from targets which link with the dll. The problem comes when I use the child class in the dll itself in another compilation unit:
some_other_dll_file.cpp:
void func()
{
child c(53);
c.print();
c.some_method();
}
In this case I get a warning: warning C4661: 'void parent<int>::print(void)': no suitable definition provided for explicit template instantiation request
(or in my particular case a ton of warnings for each and every method which is not visible in the template header in each and every file in the dll which uses the child class)
Note that it's a warning only. Eventually everything compiles and links, and works fine.
Is there a way to change the code so I don't get this warning?
The code contains lots of errors, probably some are typos, some others are missing parts of code, and so on.
The warning you get is because the explicitly instantiated template (parent<int>) doesn't have the print method defined (only declared) in the some_other_dll_file translation unit. Check [SO]: warning C4661:no suitable definition provided for explicit template instantiation request (#SergeBallesta's answer). You'll get rid of the warning by moving print's body in parent.hpp.
Below it's a working example.
Dll project
dll00.h:
#pragma once
#if defined (DLL00_INTERNAL) || defined(DLL00_STATIC)
# define DLL00_API
#else
# if defined(DLL00_EXPORTS)
# define DLL00_API __declspec(dllexport)
# else
# define DLL00_API __declspec(dllimport)
# endif
#endif
parent.hpp:
#pragma once
#include <dll00.h>
#include <iostream>
template <typename T>
class parent {
public:
parent(T t): m_t(t) {};
void print();
private:
T m_t;
};
template <typename T>
void parent<T>::print() {
std::cout << m_t << std::endl;
}
parent.cpp:
#define DLL00_EXPORTS
#include <parent.hpp>
template class DLL00_API parent<int>;
child.hpp:
#pragma once
#include <dll00.h>
#include <parent.hpp>
extern template class parent<int>;
class DLL00_API child : public parent<int> {
public:
using parent<int>::parent;
void some_method();
};
child.cpp:
#define DLL00_EXPORTS
#include <child.hpp>
#include <iostream>
void child::some_method() {
std::cout << "child::some_method" << std::endl;
}
other.cpp:
#define DLL00_INTERNAL
#include <child.hpp>
void func() {
//*
child c(53);
c.print();
c.some_method();
//*/
}
App project
main.cpp:
#include <child.hpp>
int main() {
child c(12);
c.print();
c.some_method();
return 0;
}
If for some reason you want to have the function body in parent.cpp, then you'll have to simply ignore the warning. If you don't want to see it, add #pragma warning(disable: 4661) at parent.hpp's beginning. But bear in mind that ignoring warnings might get you in trouble in some cases.

Incomplete type used in nested name specifier for Pimpl Idiom

I have this error for the following code
incomplete type ‘Foo::Pimpl’ used in nested name specifier
AnotherFoo.hpp
struct AnotherFoo {
void methodAnotherFoo(Foo &);
};
AnotherFoo.cpp
#include "Foo.hpp"
#include "AnotherFoo.hpp"
void AnotherFoo::methodAnotherFoo(Foo &foo) {
// here i want to save the function pointer of methodPimpl(), std::function for ex:
std::function<void(void)> fn = std::bind(&Foo::Pimpl::methodPimpl, foo._pimpl); // <-- Here i am getting the error
}
Foo.hpp
struct Foo {
Foo();
class Pimpl;
std::shared_ptr<Pimpl> _pimpl;
};
Foo.cpp
#include "Foo.hpp"
struct Foo::Pimpl {
void methodPimpl(void) {}
};
Foo::Foo() : _pimpl(new Pimpl) {}
main.cpp
#include "Foo.hpp"
#include "AnotherFoo.hpp"
int main() {
Foo foo;
AnotherFoo anotherFoo;
anotherFoo.methodAnotherFoo(foo);
}
Does anyone have a good solution for how to fix this?
The main goal that I am trying to achieve is to keep the signature of the methodAnotherFoo method hidden from the header files.
The only file in which you may access details of Foo::Pimpl is Foo.cpp, the file in which it is defined.
You may not access it in AnotherFoo.cpp.
Your choices are:
Change the implementation of AnotherFoo::methodAnotherFoo to use only the public interface of Foo.
Move the implementation of AnotherFoo::methodAnotherFoo to Foo.cpp.
If AnotherFoo.cpp needs direct access to the implementation object it is going to have to see the definition of that type, there is no way around that. Perhaps add a "detail/foo.h" header that is meant for internal use like this.
Your Pimpl implementation is not correct. It should hide details while you are trying to access them directly from methodAnotherFoo. So you should make implementation details private and provide a public proxy methods to manipulate stored implementation:
class Foo
{
public: Foo();
public: void method(void);
private: class Pimpl;
private: std::shared_ptr<Pimpl> _pimpl;
};
// Foo.cpp
struct Foo::Pimpl
{
void methodPimpl(void) {}
};
Foo::Foo() : _pimpl(new Pimpl) {}
void Foo::method(void) {_pimpl->method();}
And change the rest of the code to utilize those proxy methods instead of digging to implementation details:
void AnotherFoo::methodAnotherFoo(Foo &foo)
{
std::function<void(void)> fn = std::bind(&Foo::method, foo);
}
One solution that i found is to move the Pimpl implementation to AnotherFoo.cpp

Implementation switching with fully-specialized aliases

I'm writing for a project that uses makefile rules to switch between GPU and CPU implementations. I'd like to present an interface to user code that automatically switches between implementations without having to write #ifdef GPU_MODE all over the place.
My solution had been to use a templated interface class followed by alias templates (just typedefs, really, but I like the left-to-right style) like so:
namespace INTERFACE{
template <class VERSION>
class MyClass{
public:
MyClass():theClass(){};
//more methods...
private:
VERSION theClass;
};
}
using CpuMyClass = INTERFACE::MyClass<CpuMode>; //CpuMode defined elsewhere
#ifdef GPU_MODE
using MyClass = INTERFACE::MyClass<GpuMode>;
#else
using MyClass = INTERFACE::MyClass<CpuMode>;
#endif
Thus allowing outside code to use the symbol MyClass freely, trusting it to switch between modes automatically. Unfortunately using this is proving confusing. For example, I have a second class for which I'd like to write a ctor from MyClass:
#include "MyClass.hpp"
class OutsideClass{
public:
OutsideClass(const MyClass& A);
};
This ctor triggers an "identifier 'MyClass' is undefined" error. Does anyone know what's going on here?
Did you mean something like:
struct GpuMode { };
struct CpuMode { };
namespace INTERFACE {
// ~~~~~~~~^^^^^^
template <class VERSION>
class MyClass{
public:
MyClass() : theClass(){};
//more methods..
private:
VERSION theClass;
};
}
using CpuMyClass = INTERFACE::MyClass<CpuMode>; //CpuMode defined elsewhere
#ifdef GPU_MODE
using MyClass = INTERFACE::MyClass<GpuMode>;
#else
using MyClass = INTERFACE::MyClass<CpuMode>;
#endif
class OutsideClass{
public:
OutsideClass(const MyClass& A);
};
Demo

Templated classes with pimpl idiom incorrect

As described in the MSDN library here I wanted to experiment a bit with the pimpl idiom. Right now I have a Foo.hpp with
template<typename T>
class Foo {
public:
typedef std::shared_ptr<Foo<T>> Ptr;
Foo();
private:
class Impl;
std::unique_ptr<Impl> pImpl;
};
where the T parameter isn't used yet. The implementation is stored in Foo.cpp
template<typename T>
class Foo<T>::Impl {
public:
int m_TestVar;
};
template<typename T>
Foo<T>::Foo() : pImpl(new Impl) {
this->pImpl->m_TestVar = 0x3713;
}
Currently the compiler has two errors and one warning:
use of undefined type 'Foo<T>::Impl'; ... vc\include\memory in line 1150
can't delete an incomplete type; ... vc\include\memory in line 1151
deletion of pointer to incomplete type 'Foo<T>::Impl'; no destructor called; ... vc\include\memory in line 1152
What is the concflict here and how could I resolve it?
Edit. Removed the call to std::make_shared - copy&paste fail based on one old version.
I have had a similar issue - we've a base class in our system called NamedComponent and I wanted to create a template which takes an existing named component and converts it into a pimpl facade.
What I did was separate the template into a header and an inline file, and create a function to cause the template to be instantiated. This allows the implementation to be in a library, with the template instantiations of the facade with that implementation, and for the client to be able to use the facade based on the template and a forward declaration of the implementation.
header 'Foo.h':
template<class T> class Foo
{
public:
Foo ();
virtual ~Foo();
private:
T *impl_;
public:
// forwarding functions
void DoIt();
};
inline functions 'Foo.inl':
#include "Foo.h"
template<class T> Foo<T>::Foo() :
impl_ ( new T )
{
}
template<class T> Foo<T>::~Foo()
{
delete impl_;
}
// forwarding functions
template<class T> void Foo<T>::DoIt()
{
impl_ -> DoIt();
}
// force instantiation
template<typename T>
void InstantiateFoo()
{
Foo<T> foo;
foo.DoIt();
}
implementation cpp file - include the template inline functions, define the implementation, reference the instantiation function:
#include "Foo.inl"
class ParticularImpl {
public:
void DoIt() {
std::cout << __FUNCTION__ << std::endl;
}
};
void InstantiateParticularFoo() {
InstantiateFoo<ParticularImpl>();
}
client cpp file - include the template header, forward declare the implementation and use the pimpl facade:
#include "Foo.h"
class ParticularImpl;
int main () {
Foo<ParticularImpl> bar;
bar.DoIt();
}
You may have to fiddle with the InstantiateFoo function's contents to force the compiler to instantiate all functions - in my case, the base called all the pimpl's functions in template methods so once one was referenced, they all were. You don't need to call the Instantiate functions, just link to them.
IMHO PIMPL doesn't make much sense with templates, unless you know all possible template parameters and that set is fairly small. The problem is, that you will either have the Impl implementation in the header file otherwise, as has been noted in the comments. If the number of possible T parameters is small, you still can go with the separation, but you'll need to declare the specialisations in the header and then explicitly instantiate them in the source file.
Now to the compiler error: unique_ptr<Impl> requires the definition of Impl to be available. You'll need to directly use new and delete in the ctor Foo::Foo and dtor Foo::~Foo, respectively instead and drop the convenience/safety of smart pointers.

Compile error when trying to register observer to template subject class

I am trying to implement an observer pattern with a template subject class. The observers don't (need to) know the subjects type, so I made an interface for the attach method without this type. This is my implementation:
SubjectInterface.h
#ifndef SUBJECTINTERFACE_H_
#define SUBJECTINTERFACE_H_
#include <list>
#include "Observer.h"
// Template-independant interface for registering observers
class SubjectInterface
{
public:
virtual void Attach(Observer*) = 0;
}; // class SubjectInterface
#endif // SUBJECTINTERFACE_H_
Subject.h
#ifndef SUBJECT_H_
#define SUBJECT_H_
#include <list>
#include "Observer.h"
#include "SubjectInterface.h"
template <class T>
class Subject : public SubjectInterface
{
public:
Subject();
~Subject();
void Attach(Observer*);
private:
T mValue;
std::list<Observer*> mObservers;
}; // class Subject
#include "Subject.cpp"
#endif // SUBJECT_H_
Subject.cpp
template <class T>
Subject<T>::Subject()
{
}
template <class T>
Subject<T>::~Subject()
{
}
template <class T>
void Subject<T>::Attach(Observer* test)
{
mObservers.push_back(test);
}
Observer.h
#ifndef OBSERVER_H_
#define OBSERVER_H_
#include "SubjectInterface.h"
#include <iostream>
class Observer
{
public:
Observer(SubjectInterface* Master);
virtual ~Observer();
private:
SubjectInterface* mMaster;
}; // class Observer
#endif // OBSERVER_H_
Observer.cpp
#include "Observer.h" // include header file
Observer::Observer(SubjectInterface* Master)
{
Master->Attach(this);
}
Observer::~Observer()
{
}
When I compile this using the gcc 4.3.4, I get the following error message:
SubjectInterface.h:10: error: ‘Observer’ has not been declared
I don't understand this, because the Observer is included just a few lines above. When I change the pointer type from Observer* to int*, it compiles OK. I assume that there is a problem with the template subject and the non-template interface to it, but that is not what gcc is telling me and that doesn't seem to be the problem when using int*.
I searched for template/observer, but what I found (e.g. Implementing a Subject/Observer pattern with templates) is not quite what I need.
Can anyone tell me, what I did wrong or how I can call the templated attach-method from a non-template observer?
You have a circular include chain, SubjectInterface.h includes Observer.h which in turns includes SubjectInterface.h.
This means that the include guards will prevent Observer from being visible. To fix it instead forward declare Observer.
// SubjectInterface.h
#ifndef SUBJECTINTERFACE_H_
#define SUBJECTINTERFACE_H_
#include <list>
class Observer; //Forward declaration
// Template-independant interface for registering observers
class SubjectInterface
{
public:
virtual void Attach(Observer*) = 0;
}; // class SubjectInterface
#endif // SUBJECTINTERFACE_H_
You have a circular dependency; Observer.h includes SubjectInterface.h, and vice versa. You will need to break this with a forward declaration.