0

I have the following problem:

Suppose there is a base class A.

This class has a large part which is always the same. A small part should be generic for example with a template parameter T.

For now I don't want to make a template class of A because I don't want to have the large non-generic part implementation in the header file. What can I do with it? Is there any possibility with which I have the implementation of the large non-generic part seperated from the class header but also have the small generic part as some kind of template?

As an example: Suppose you have a generic Tree, which has a few template functions (like add(T o)), but many functions not depending on the template (like size(), but more complex).

As an addition: A has multiple sub classes and those should decide what the type of T should finally be...

I know that the compiler can't handle the implementation of templates within cpp files. But ist there a conceptual way to solve the seperation? For example something different than templates?

Claudio
  • 10,614
  • 4
  • 31
  • 71
kke
  • 353
  • 2
  • 8
  • 2
    Have you tried something? Could you post some code? – jabujavi Apr 12 '16 at 15:41
  • 1
    How does your `add(T o)` actually add the `T`, assuming `template < typename T > add(T o) { list.push_back(o); }` .. wouldn't the underlying sequence container need to know about the type of `T`? Additionally, how do your sub-classes of `A` handle the `T` type for generics? Do you have templated inner classes that handle this? .. And as for your question, what about `class A { #include "a.impl" }` where `a.impl` contains the implementation of the `A` class? .. there's also inheritance of templated classes ... each has a pro/con you need to weigh in on though, so it is ultimately up to you. – txtechhelp Apr 12 '16 at 15:45
  • The usual solution is to separate the implementation out into a `.icc` or `.tcc` file that is included from the header. See also [Why can templates only be implemented in the header file?](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) (I'm tempted to close your question as a dupe of that one). – πάντα ῥεῖ Apr 12 '16 at 15:52

2 Answers2

0

A way to handle the separation directly is to split the template into two header files (which closely resemble the way you separate non-template stuff into .h declarations and a .cpp implementation).

Then, you can use explicit template instantiation to on the template.

  // tree.h
  // Included by anything that needs to use the tree template.
  template <typename T> class tree {
     void inline_func() { }
     void noninline_func();
  };

  // tree_impl.h
  // This header is only included by a single .cpp file: the
  // template repository module that contains instantiations.
  template <typename T> tree<T>::noninline_func()
  {
  }

  // template-repo.cpp
  #include "tree.h"
  #include "tree_impl.h" // only template-repo.cpp includes this!
  // same for all other templates
  #include "mytype.h"
  #include "othertype.h"

  template class tree<mytype>; // instantiate template here over mytype
  template class tree<othertype>; // ditto over othertype
  // ... other instantiations

You can roll the two header files together using the preprocessor:

  // tree.h
  template <typename T> class tree {
     void inline_func() { }
     void noninline_func();
  };

  #ifdef INSTANTIATING_TEMPLATES
  template <typename T> tree<T>::noninline_func()
  {
  }
  #endif

  // template-repo-cpp
  #define INSTANTIATING_TEMPLATES
  #include "tree.h"
  #include "mytype.h"

  template class tree<mytype>;

However this slight convenience means that when you #include "tree.h" everywhere, you're passing a lot of extra cruft through the preprocessor.

There is a downside to all this, like all things C++. Not only is it manual maintenance, but every time you touch either a template header or one of the types used as template arguments, the entire template-repo.cpp has to be recompiled. (Of course, things can be split into multiple mini-repos: counter-workaround for issues with the workaround).

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • That won't work. The `noninline_func()` must still appear in the header if the whole class is declared as template class. As OP states correctly: _"For now I don't want to make a template class of A because I don't want to have the large non-generic part implementation in the header file."_ – πάντα ῥεῖ Apr 12 '16 at 15:46
  • @πάνταῥεῖ It is not in **the** header file; it is in a different header file: one which is not included by any source file other than `template-repo.cpp`. That **must** be the case, otherwise it would violate the one-definition rule. – Kaz Apr 12 '16 at 15:50
  • But specializing for any possible type is, uhm clumsy, not to say unmaintainable. – πάντα ῥεῖ Apr 12 '16 at 15:53
  • @πάνταῥεῖ This is not a discussion forum. If you have a better way, you can post it as your answer, or an edit over an existing one. C++ **is** clumsy and unmaintainable. When templates were shiny and new, there was a lot of talk about how future C++ compilers would seamlessly implement template repositories, integrated with the build system. – Kaz Apr 12 '16 at 15:56
  • _"This is not a discussion forum."_ I know that pretty well Why do you think I want to discuss with you? There's already an answer to that question (I mentioned in my comment at the OP), so why should I write another one. What you propose is simply not useful IMHO. – πάντα ῥεῖ Apr 12 '16 at 16:00
  • @πάνταῥεῖ What I "propose" is designed into ISO C++ for this purpose. I used this in actual projects when I developed professionally in C++. It is usable and useful. – Kaz Apr 12 '16 at 16:09
0

But isn't there a conceptual way to solve the seperation? For example something different than templates?

If you are looking for a portable c++ alternative to templates, their is simply no such thing that provides the sheer power that templates do. That aside, let's try to solve your problem.

You can simply package functions into a separate class which you can implement in any other header. We have two choices here, adding that proxy class as a member to the base class or using inheritance.

Choice A (directly including as a member)

Using this method you come across a problem, their is no way to directly access the functions. You can't overload the . operator! One way to deal with this is a proxy function that simply returns a reference to the member variable. Here's a minimal working example:

#include <iostream>
using namespace std;

// forward declaration 
template <typename T> 
class B;

template <typename T>
class A {
public:

    // allows friendship regardless of the type instantiated with class A
    template <typename U>
    friend class B;

    A(int a) : a_(a), funcs_(this) {}

    // this could be named anything
    B<T>& utils() { return funcs_; }

private:
    int a_;
    // proxy class instance, should probably be static 
    B<T> funcs_;
};

// this class could be modifed to accept any type but i did this for simplicity 
template <typename T> 
class B {
public:

    explicit B(A<T>* base) : base_(base) {}

    // function implementations go here...
    void print() { std::cout << base_->a_; }

private:
    A<T>* base_;
};


int main() {

    A<int> testA(23);

    testA.utils().print();

    return 0;
}

Choice B (inheritance)

Using this method you will be able to directly access the functions, but it comes at the cost of using inheritance, which i know some people would preer not to use over choice A. Here is a working example:

#include <iostream>
using namespace std;

// forward declaration 
template <typename T> 
class B;

template <typename T>
class A : public B<T> {
public:

    // allows friendship regardless of the type instantiated with class A
    template <typename U>
    friend class B;

    A(int a) : a_(a), B(this) {}

private:
    int a_;

};

// this class could be modifed to accept any type but i did this for simplicity 
template <typename T> 
class B {
public:

    explicit B(A<T>* base) : base_(base) {}

    // function implementations go here...
    void print() { std::cout << base_->a_; }

private:
    // pointer to base class 
    A<T>* base_; 
};


int main() {

    A<int> testA(23);

    testA.print();

    return 0;
}

If it were my choice i would most likely use Choice B, I wouldn't want to give the client the need to explicitly call a function just too call another function. You could obviously use some tricks involving typedef and bind but I think that adds unnecessary overhead.

Nowhere Man
  • 475
  • 3
  • 9