Home > Software engineering >  Template wrangling
Template wrangling

Time:09-27

I'm having an issue where I've created a somewhat tangled hierarchy involving templates. The result is I'm having to put some code in the wrong header files just to get it to compile, and compilation is now fragile (I don't know if I can keep this project compiling if just the right function needs to be added.)

So I'm looking for a way to resolve this so that the code is nicely divided into proper files.

So without further ado, here is the code:

TemplatedBase.h

template <typename T> struct TemplatedBase
{
  T value;
  
  void go();
};

Derived.h

struct Derived : public TemplatedBase<int>
{
  void hello()
  {
    printf("HI %d\n", value);
  }
};

template <typename T> void TemplatedBase<T>::go()
{
  // TemplatedBase<T> NEEDS USE OF Derived!!
  // So TemplatedBase<T>::go() is appearing here in Derived.h,
  // that's the only way I could get it to compile and it seems really
  // out of place here.
  Derived der;
  der.hello();
}

main.cpp

#include <stdio.h>
#include "Derived.h"

int main(int argc, const char * argv[])
{
  Derived d;
  d.go();
  return 0;
}

Isn't there a way I can put TemplatedBase<T>::go() into a file like TemplatedBase.cpp? Alas, it doesn't seem to work (you will see Undefined symbol: TemplatedBase<int>::go() in XCode at least).

CodePudding user response:

You could do it by explicitly instantiating the template with a particular type in the cpp file:

// TemplatedBase.cpp

#include "TemplatedBase.h"
#include "Derived.h"

template <typename T>
void TemplatedBase<T>::go()
{
    // TemplatedBase<T> NEEDS USE OF Derived!!
    // So TemplatedBase<T>::go() is appearing here in Derived.h,
    // that's the only way I could get it to compile and it seems really
    // out of place here.
    Derived der;
    der.hello();
}

template struct TemplatedBase<int>; // This will make it work but now you can only use `TemplatedBase<int>`
// More instantiations go here...

But I wouldn't recommend doing this as this restricts what types you are able to use in TemplatedBase<T> (You'd have to manually add every single type yourself). So instead, use a templated type inside the go() member function (The trick here is that template parameters are not evaluated immediately):

// TemplatedBase.h

struct Derived; // Forward declaration

template <typename T>
struct TemplatedBase
{
    T value;

    void go()
    {
        go_impl();
    }
private:
    template <typename X = Derived>
    void go_impl()
    {
        X der;
        der.hello();
    }
};
// Derived.h

#include "TemplatedBase.h"

struct Derived : public TemplatedBase<int>
{
    void hello()
    {
        printf("HI %d\n", value);
    }
};

Note: BTW, since C 20, one can just do:

// TemplatedBase.h

struct Derived; // Forward declaration

template <typename T>
struct TemplatedBase
{
    T value;

    void go()
    {
        [] <typename X = Derived>() {
            X d;
            d.hello();
        }();
    }
};

CodePudding user response:

TemplatedBase and Derived are really coupled, so sharing the same header might be a viable option.

Else, you can create a file for your template definition (Header guards omitted):

// TemplatedBase.h
// Public header

#include "TemplatedBaseDecl.h"
#include "TemplatedBaseImpl.h"
// TemplatedBaseDecl.h

template <typename T>
struct TemplatedBase
{
  T value;
  
  void go();
};
// TemplatedBaseImpl.h
#include "TemplatedBaseDecl.h"
#include "Derived.h"

template <typename T> void TemplatedBase<T>::go()
{
  Derived der;
  der.hello();
}
// Derived.h
// Public header

#include "TemplatedBaseDecl.h" // Cannot use "TemplatedBase.h"

struct Derived : public TemplatedBase<int>
{
  void hello()
  {
    printf("HI %d\n", value);
  }
};
  • Related