C++ class declarations and namespaces - c++

I'm having an issue with a C++ library I'm trying to write. It's the usual setup, one cpp file, one header file. I want the header file to only expose the parts that are meant to be used (I have an abstract base class for instance, I don't want in the header file). So far, I'm just working with a single file (I assume this should make no difference, as includes are done by the preprocessor, which doesn't care about anything).
You'll note that the "header file" is spread over two spots, before and after the header implementation file.
#include <stdio.h>
// lib.h
namespace foo {
template <class T> class A;
}
// lib.cpp
namespace foo {
template <class T> class A {
private:
T i;
public:
A(T i) {
this->i = i;
}
T returnT() {
return i;
}
};
};
// lib.h
namespace foo {
template <class T> T A<T>::returnT();
}
// foo.cpp
void main() {
foo::A<int> a = foo::A<int>(42);
printf("a = %d",a.returnT());
}
So, naturally, I'd like my header file to contain just
namespace foo {
template <class T> class A;
template <class T> T A<T>::returnT();
}
But my compiler does not like this (it complains that returnT is not a member of foo::A<T>. The reason I don't want to put the class declaration itself in the header is that then it would (as I understand it), contain all the private and similar stuff, which I'd like to hide.
Maybe it's just me, but the following header file seems "bad", at least as an "interface specification." It exposes some of the internals of A, which a user of the lib would not need to know about.
// lib.h
namespace foo {
template <class T> class A {
private:
int i;
public:
A(T);
T returnT();
};
}
// lib.cpp
namespace foo {
template <class T> A<T>::A(T i) {
this->i = i;
}
template <class T> T A<T>::returnT() {
return i;
}
};
Is this the accepted way of doing it? I'd like a more abstract header file, if at all possible.

You cannot separate the definition of a template from its declaration. They both have to go into the header file together.
For "why?" I recommend reading "Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file?".
I may have misread your question. To address what may also be your question, this is not valid:
namespace foo {
template <class T> class A;
template <class T> T A<T>::returnT();
}
It is not valid for the same reason that this is not valid:
namespace foo {
class A;
int A::returnT();
}
Member functions must be declared inside the definition of the class.

There are two problems with .cpp files you are dealing here:
I.
If you want to put an instance of that class on the stack (like you do in your main()) the compiler needs to know the size of the class (to allocate enough memory). For that it needs to know the members and by that the complete declaration.
The only way to hide the class' layout away is to built up an interface and a factory method/function and put the instance on the heap in the factory.
As an example (without the template; see below to know why):
namespace foo {
class IA {
public:
virtual ~IA();
virtual int returnT() = 0;
static IA *Create();
};
}
In your .cpp you then do:
namespace foo {
class A : public IA {
private:
int i;
public:
A() :
i(0) {
}
virtual ~A() {
}
virtual int returnT() {
return i;
}
};
IA::~IA() {
}
IA *IA::Create() {
return new A();
}
}
BTW: Using smart pointers would be suggested...
II.
Since you are using a template the method definitions must be either visible via header file or explicitly instantiated for a specific set of types.
So you can split up your code into a lib.h and a lib_impl.h:
lib.h:
namespace foo {
template <typename T> class IA {
public:
virtual ~IA() {
}
virtual T returnT() = 0;
static IA *Create();
};
}
lib_impl.h:
namespace foo {
template <typename T> class A : public IA<T> {
private:
T i;
public:
A() :
i(T()) {
}
virtual ~A() {
}
virtual T returnT() {
return i;
}
};
template <typename T> IA<T> *IA<T>::Create() {
return new A<T>();
}
}
so you include the lib_impl.h where ever you need the impleemntations.
To use the explicit instantiations add a lib.cpp and just let that file allow to include lib_impl.h:
lib.cpp:
#include <lib_impl.h>
namespace foo {
template class IA<int>;
template class A<int>;
template class IA<float>;
template class A<float>;
template class IA<char>;
template class A<char>;
// ...
}

Related

How can I offload dependency injected template class providing templated functions to pimpl class

I have an application class that can take in a dependent class as a template argument to the constructor. This dependent class is required to provide certain templated functions that the application class can call. I would like to offload this dependent class object to a pimpl class so the application class is not a template class and thus header-only.
Here is a rough idea of what I mean.
///////////
// impl.h
///////////
template<typename Helper>
struct Impl
{
public:
Impl(Helper& helper) : helper_(helper)
{
}
template <typename T>
void someHelperFn1(T t)
{
helper_->fn1(t);
}
template <typename U>
SomeOtherClass<U> someHelperFn2()
{
return helper_->fn2();
}
private:
Helper& helper_;
};
///////////
// app.h
///////////
#include "impl.h"
class App
{
public:
template<typename Helper>
App(Helper &h) :impl_(new Impl) {}
template <typename T>
void someHelperFn1(T t)
{
impl_->someHelperFn1(t);
}
template <typename U>
SomeOtherClass<U> someHelperFn2()
{
return impl_->someHelperFn2();
}
void someAppFn();
private;
std::unique_ptr<Impl> impl_;
};
///////////
// app.cpp
///////////
void App::someAppFn()
{
// some useful code
}
I realize the above code doesn't compile since Impl is really a template class and so App would also be a template class too. That is what I would like to avoid so that App is not a header-only class. I found something similar except the functions that I want to call from the helper dependency are template functions and they are not in this case. It seemed pretty close otherwise to what I wanted to do.
Any ideas on how I can avoid making App a template class?
I tried making the helper class use a common base class but that is not really possible with the template functions.
Also, note that I am limited to C++ 17 for the compiler.
You will need to make sure the public header file (the one with the class that has the pimpl pointer) doesn't expose the header file only class template of the implementation. Use an interface for that like this.
I did not dependency inject the implementation because that should not be needed.
#include <memory>
#include <iostream>
// public header file
// for pimpl pattern I often use an interface
// (also useful for unit testing later)
class PublicItf
{
public:
virtual void do_something() = 0;
virtual ~PublicItf() = default;
protected:
PublicItf() = default;
};
// the public class implements this interface
// and the pimpl pointer points to the same interface
// added advantage you will have compile time checking that
// the impl class will all the methods too.
class PublicClass final :
public PublicItf
{
public:
PublicClass();
virtual ~PublicClass() = default;
void do_something() override;
private:
std::unique_ptr<PublicItf> m_pimpl; // the interface decouples from the template implementation (header file only)
};
// private header file
// this can now be a template
template<typename type_t>
class ImplClass final :
public PublicItf
{
public:
void do_something() override
{
m_value++;
std::cout << m_value << "\n";
}
private:
type_t m_value{};
};
// C++ file for Public class
// inlcude public header and impl header (template)
PublicClass::PublicClass() :
m_pimpl{ std::make_unique<ImplClass<int>>() }
{
};
void PublicClass::do_something()
{
m_pimpl->do_something();
}
// main C++ file
int main()
{
PublicClass obj;
obj.do_something();
return 0;
}

Specializing a class method based on overall class template

How do I specialize initialize() (see below) where the type isn't based on the method argument, just the overall class template parameter?
template<class STREAM_TYPE>
class MyClass
{
struct MyStruct
{
STREAM_TYPE* _ifs{nullptr};
}
public:
// Use this when STREAM_TYPE = std::ifstream
void initialize()
{
for(MyStruct& ms : _myStructs)
{
ms._ifs = new std::ifstream("");
}
}
// Use this when STREAM_TYPE = std::stringstream
void initialize()
{
}
private:
std::array<MyStruct, 10> _myStructs;
};
Non-template members of class template are themselves independent templates. You can specialize them independently. In your case - by using explicit specialization
// Main template
template<class STREAM_TYPE>
class MyClass
{
void initialize()
{
}
};
// Specialization, declared outside the main template definition
template<>
void MyClass<std::ifstream>::initialize()
{
for(MyStruct& ms : _myStructs)
{
ms._ifs = new std::ifstream("");
}
}
It is up to you to decide which version of the method is the "default" version and which is the "specialized" version. Or maybe you want to declare both versions as specializations.
For example, you might decide to consider both versions as specializations, while defining the main version as deleted
// Main template
template<class STREAM_TYPE>
class MyClass
{
void initialize() = delete;
};
// Specialization, declared outside the main template definition
template<>
void MyClass<std::ifstream>::initialize()
{
for(MyStruct& ms : _myStructs)
{
ms._ifs = new std::ifstream("");
}
}
template<>
void MyClass<std::stringstream>::initialize()
{
}
Just keep in mind that an explicit specialization is no longer a template. It obeys ODR as an ordinary function. Even if your template class is defined in a header file (as template classes usually are), the definition of the above specialization(s) have to go to a .cpp file. The header file should contain mere declarations for your specializations
// header file declarations
template<> void MyClass<std::ifstream>::initialize();
template<> void MyClass<std::stringstream>::initialize();
while the definitions should go to a .cpp file.
What about using SFINAE to enable only the correct version?
template <typename ST = STREAM_TYPE>
std::enable_if_t<std::is_same<ST, std::ifstream>::value> initialize ()
{
std::cout << "ifstream case" << std::endl;
for (MyStruct & ms : _myStructs)
ms._ifs = new std::ifstream("");
}
template <typename ST = STREAM_TYPE>
std::enable_if_t<std::is_same<ST, std::stringstream>::value> initialize ()
{
std::cout << "stringstream case" << std::endl;
}

Class typedef defined outside of class

I am writing a class C which has an inner class T, and I'd like the details of T hidden as an internal implementation of C. Methods in C are all using pointers to T. This is of course possible as:
// In header
class C {
public:
class T;
T* f();
void g(T*);
};
// In .cpp
class C::T { /* details here */ };
Now my question is, how can I define C::T as a type alias of another one, in .cpp file. The following doesn't compile at all, but it illustrates what I want to do:
// Outside the class C
using C::T = std::string;
Is there any workaround to this, while maintaining the goal, i.e. hide the detail of C::T?
As others pointed out, it cannot be done. This is my suggestion:
// .h
class C {
public:
struct T;
T* f();
void g(T*);
};
// .cpp
struct C::T
{
IMPL_TYPE data;
//If one is carefull with lifetimes this can almost in any context act as IMPL_TYPE.
//And if necessary, the data member can be accessed.
operator IMPL_TYPE&(){return data};
}
You cannot, because the forward declaration class T; within class C declares a class type whose True Name is C::T, and is therefore not identical to the type whose True Name is std::basic_string<...>.
You might consider the following:
// C.h
#include "internal/T.h"
namespace foo {
class C {
public:
using T = internal::T;
// ...
};
}
// internal/T.h
namespace foo { namespace internal {
using T = std::string;
}}
The closest you can come to this would be to have your t derive from string:
class C::T : public std::string { ... };
T can never be truly hidden or redefined to be a type alias in a different .cpp file.
The following round about method should work for your needs.
class C
{
public:
// Use a base class for just the pointers.
struct TBase
{
virtual ~TBase() {}
};
TBase* f();
void g(TBase*);
// Allow client code to define a concrete type using a template parameter.
template <typename Data> struct T : TBase
{
Data data;
};
};
Then, in a .cpp file, use:
using Type = C::T<std::string>;
Type* obj = new Type;
obj->data = "Some string";
C c;
c.g(obj);
TBase* ptr = c.f();

Calling a template method from another template method in C++?

I am currently having an issue with templated methods. I have this public class implementing a template method:
namespace Private { class InternalClass; }
namespace Public
{
class PublicClass
{
public:
PublicClass();
virtual ~PublicClass();
template<class T>
bool Add(bool primary);
private:
Private::InternalClass* _pInternal;
};
template<class T>
bool PublicClass::Add(bool primary) { return _pInternal->Add<T>(primary); }
}
The internal class is implemented that way:
namespace Private
{
class InternalClass
{
public:
InternalClass();
virtual ~InternalClass();
template <class T>
bool Add(bool primary);
};
template<class T>
bool InternalClass::Add(bool primary) { return false; }
}
As this internal class header won't be available with the provided sources, I must class forward it within the PublicClass header and I add the include to PrivateClass.h inside the PublicClass.cpp file.
1) Any idea why I would be getting the following error:
error : member access into incomplete type 'Private::InternalClass' / note: forward >declaration of 'Private::InternalClass'
2) What would be the best way of hiding my PublicClass::Add() implementation?
UPDATED
Reason for error at 1) is because of this as stated by Cornstalks.
For 2), how can I hide my implementation without including PrivateClass.h within the PublicClass header file?
You have encountered a very interesting problem - you want to implement the PImpl idiom where the privately implemented class has a template method. Well, this can be solved, meaning you CAN hide the template's implementation, but only when you know which types will be used to instantiate your Add<T> method in your program.
Say, your template will work only with types AClass and BClass. Then you can split your files as follows (comments are inlined):
File public.h:
#ifndef PUBLIC_H
#define PUBLIC_H
// Forward declaration ! It's sufficient in this case !
namespace Private { class InternalClass; }
// Declare all classes your Add<T> method should work with
struct AClass {};
struct BClass {};
namespace Public
{
class PublicClass
{
public:
PublicClass() {}
virtual ~PublicClass() {}
template <typename T>
bool Add(bool primary); // DO NOT implement this method, just declare
private:
Private::InternalClass* _pInternal;
};
// "Explicit instantiation declarations", for each type the method will work with:
extern template bool PublicClass::Add<AClass>(bool primary);
extern template bool PublicClass::Add<BClass>(bool primary);
}
#endif
File public.cpp:
#include "public.h"
// NOTE: this is hidden in CPP file, noone will see your implementation
namespace Private
{
class InternalClass
{
public:
InternalClass() {}
virtual ~InternalClass() {}
template <typename T>
bool Add(bool primary);
};
// Magic! Here is the actual implementation of your private method
template <typename T>
bool InternalClass::Add(bool primary)
{
return false;
}
}
namespace Public
{
// Original definition moved to CPP file !
template <typename T>
bool PublicClass::Add(bool primary)
{
return _pInternal->Add<T>(primary);
}
// And again list the allowed types, this time using "explicit instantiation definitions"
template bool PublicClass::Add<AClass>(bool primary);
template bool PublicClass::Add<BClass>(bool primary);
}
File main.cpp:
#include "public.h"
int main()
{
Public::PublicClass pc;
pc.Add<AClass>(true); // works !
pc.Add<BClass>(false); // works !
// pc.Add<int>(true); linker error as expected,
// becuase there is no explicit instantiation for Add<int>
return 0;
}

Using pimpl with Templated Class and explicitly instantiated templates

How do I use pimpl for a templated class, when I explicitly instantiate the templates?
All I need is an example code.
What I have tried is:
// MyTemplatedClass.h
template< class T >
class MyTemplatedClass
{
private:
class Impl;
Impl* _pimpl;
public:
void PublicMethod();
}
Here my implementation goes:
// MyTemplatedClass.cpp
template< class T >
class MyTemplatedClass<T>::Impl
{
public:
void PublicMethod();
}
template <class T>
void MyTemplatedClass<T>::Impl::PublicMethod()
{
...
}
Forwarding method call to implementation class:
template< class T >
void MyTemplatedClass<T>::PublicMethod()
{
_pimpl->PublicMethod();
}
Explicit instantiation:
Example with int and double:
template class MyTemplatedClass< int >;
template class MyTemplatedClass< double >;
But it doesn't seem to work.
This would answer your question, but I doubt it does what you hoped to achieve. I suspect you would want to declare the template implementation outside the scope of MyTemplatedClass. It might be a better design to inherit from the template implementation instead of having it as a member variable.
If you compiler does not support extern template declarations I cannot see that having a template pointer to implementation adds any value. You would after all have to have the implementation details you wanted to hide away in the header file anyway.
#include <iostream>
template < class T > class MyTemplatedClass {
private:
template < class U> class Impl {
public:
void ImplPublicMethod() {
std::cout << "Standard implementation" << std::endl;
}
};
Impl<T> * _pimpl;
public:
MyTemplatedClass() : _pimpl(new Impl<T>) { }
~MyTemplatedClass() { delete _pimpl; }
void publicMethod() {
_pimpl->ImplPublicMethod();
}
};
template<> class MyTemplatedClass<int> {
private:
class Impl {
public:
void ImplPublicMethod() {
std::cout << "Integer specialisation" << std::endl;
};
};
Impl * _pimpl;
public:
MyTemplatedClass() : _pimpl(new Impl) { }
~MyTemplatedClass() { delete _pimpl; }
void publicMethod() {
_pimpl->ImplPublicMethod();
}
};
int main(int argc, char ** argv) {
MyTemplatedClass<char> charVersion;
charVersion.publicMethod();
MyTemplatedClass<int> intVersion;
intVersion.publicMethod();
return 0;
}
Methods of a template class always have to be defined in the header. You cannot have a MyTemplatedClass.cpp as compilation unit on its own. What you can do is to #include the file containing the definitions of the methods at the end of MyTemplatedClass.h so that declaration and definition are at least separated at file level. So your problem may be fixed by adding
#include "MyTemplatedClass.cpp"
at the end of MyTemplatedClass.h.
I use pimpls with template classes in my own code, it works for me that way. Your code looks about right - I'd use a std::unique_ptr for pimpl, but I don't see any problems with how you're doing it.