How to hide dependencies of implementation of template class? - c++

In regular C++ classes, we can have a class A as follows:
in A.h:
class A {
public:
void method();
// rest of class declaration
};
in A.cpp:
#include "A.h"
#include "implementation_dependencies.h"
void A::method(){
// use dependencies to implement method
}
But if A is a template class, this isn't possible. How can we still achieve information hiding with C++ template classes?
The use of extern template in C++11 (or global function prior to C++11 with the same effect) proves useful for this, but what to do when the template class needs to be available for all types?
Like a smart pointer for example: I can't put the definition of the class inside A.h since that would expose "implementation_dependencies.h" to anyone including "A.h".

From c++20, you can do this with modules:
// A.cpp
export module A;
export
template <typename>
class A {
// ...
};
#include "implementation_dependencies.h"
export template <typename T>
void A<T>::method() {
// use dependencies, but don't export them from this module
}

Related

C++ Pimpl Idiom using a pre-existing class

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

forward-declaration, embedded classes and two .hpp including each other: how to use? [duplicate]

This question already has answers here:
Forward declaration of nested types/classes in C++
(7 answers)
Closed 2 years ago.
I have a class like so...
class Container {
public:
class Iterator {
...
};
...
};
Elsewhere, I want to pass a Container::Iterator by reference, but I don't want to include the header file. If I try to forward declare the class, I get compile errors.
class Container::Iterator;
class Foo {
void Read(Container::Iterator& it);
};
Compiling the above code gives...
test.h:3: error: ‘Iterator’ in class ‘Container’ does not name a type
test.h:5: error: variable or field ‘Foo’ declared void
test.h:5: error: incomplete type ‘Container’ used in nested name specifier
test.h:5: error: ‘it’ was not declared in this scope
How can I forward declare this class so I don't have to include the header file that declares the Iterator class?
This is simply not possible. You cannot forward declare a nested structure outside the container. You can only forward declare it within the container.
You'll need to do one of the following
Make the class non-nested
Change your declaration order so that the nested class is fully defined first
Create a common base class that can be both used in the function and implemented by the nested class.
I don't believe forward declaring inner class of on an incomplete class works (because without the class definition, there is no way of knowing if there actually is an inner class). So you'll have to include the definition of Container, with a forward declared inner class:
class Container {
public:
class Iterator;
};
Then in a separate header, implement Container::Iterator:
class Container::Iterator {
};
Then #include only the container header (or not worry about forward declaring and just include both)
I know of no way to do exactly what you want, but here is a workaround, if you are willing to use templates:
// Foo.h
struct Foo
{
export template<class T> void Read(T it);
};
// Foo.cpp
#include "Foo.h"
#include "Container.h"
/*
struct Container
{
struct Inner { };
};
*/
export template<>
void Foo::Read<Container::Inner>(Container::Inner& it)
{
}
#include "Foo.h"
int main()
{
Foo f;
Container::Inner i;
f.Read(i); // ok
f.Read(3); // error
}
Hopefully, this idiom might be of some use to you (and hopefully your compiler is EDG-based and implements export ;) ).

CRTP, forward declarations and templates in cpp files

I'm using the CRTP pattern to create an interface, which other classes will derive from.
In the interface I forward declare a structure (important because I don't want to drag other stuff in the interface), but I include its definition in the cpp file which defines the interface.
Interface.h
#ifndef INTERFACE_H_INCLUDED
#define INTERFACE_H_INCLUDED
// forward declaration
class ForwardDecl;
template <class Derived>
class Interface
{
public:
ForwardDecl interfaceMethod();
};
#endif // INTERFACE_H_INCLUDED
ForwardDecl.h
#ifndef FORWARDDECL_H_INCLUDED
#define FORWARDDECL_H_INCLUDED
struct ForwardDecl
{
ForwardDecl(int i):internal(i)
{}
int internal;
};
#endif // FORWARDDECL_H_INCLUDED
Interface.cpp
#include "Interface.h"
#include "ForwardDecl.h"
template<class Derived>
ForwardDecl Interface<Derived>::interfaceMethod()
{
return static_cast<Derived *>(this)->implementation_func();
}
And this is the implementation which implements the interface
Implementation.h
#ifndef IMPLEMENTATION_H_INCLUDED
#define IMPLEMENTATION_H_INCLUDED
#include "Interface.h"
class ForwardDecl;
class Implementation: public Interface<Implementation>
{
friend class Interface<Implementation>;
private:
ForwardDecl implementation_func();
};
#endif // IMPLEMENTATION_H_INCLUDED
Implementation.cpp
#include "Implementation.h"
#include "ForwardDecl.h"
#include <iostream>
struct ForwardDecl Implementation::implementation_func()
{
ForwardDecl fd(42);
std::cout << fd.internal << std::endl;
return fd;
}
And the main file
#include <iostream>
#include "Implementation.h"
#include "ForwardDecl.h"
using namespace std;
int main()
{
Implementation impl;
ForwardDecl fd = impl.interfaceMethod();
cout << fd.internal << endl;
return 0;
}
I get linking errors on both VS and GCC.
Any workaround? Thank you.
There is a flaw in your very approach: You have a public function returning a ForwardDecl instance, so every client wanting to use this function also must include the according definition of that type, which implies you can make that type public from the beginning. This includes making the function definition inline, which will fix your linker problems.
However, if you really want to hide the content of that structure and you are sure clients don't need it directly, you can declare it and then pass around references to such a structure (or pointers, but raw pointers are evil albeit not in the same league of evil as #macros). In that case, I would still make the function definition inline.
If you really, really want to not make the function inline, you can also explicitly instantiate the function template for the types that you need. You would add at the end of the template's .cpp file something like template class Interface<int>; (I don't remember the exact syntax so take that with a few flakes of fleur de sel, check out the C++ FAQ at parashift.com for more info). This makes the template a little less universal though, as it requires adjustments for any type that you want to use it with, but it can be an approach in some corner cases.
The definitions of function templates and member functions of class templates need to be visible in all translation units that instantiate those templates. That is, you shouldn't put template definitions in a .cpp file, which means you need to move the contents of Interface.cpp up into Interface.h.

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.

Setting DSO interface for specialized template method

I have a class that has a template method:
class DoStuffWithAnything {
public:
template <typename T>
void doStuff(const T&);
};
Note that the method is not defined and will never be defined generically. Compilation units will define the implementation for these methods.
On gcc, I actually have no issue, because if a symbol is not defined it will be searched on the DSO.
On msvc I have a problem, how can I tell MSVC that it should import the definition (and when compiling the DSO, that it should export the definition)?
Clarifications
Suppose I have a lib called XX. and the libXX has a class XX. Also, the libXX defines <> doStuff(const XX&).
So, XX.hpp
class XX {
//...
};
And, XX.cpp:
#include <XX.hpp>
#include <do_stuff_with_anything.hpp>
//...
template <>
void DoStuffWithAnything::doStuff(const XX&) {
//...
}
So, my app.exe, would have a code like that:
#include <XX.hpp>
#include <do_stuff_with_anything.hpp>
int main() {
XX a;
DoStuffWithAnything stuffer;
stuffer.doStuff(a);
}
app.exe must know that doStuff<XX> is imported. How can I tell that?