Sunday 2 November 2014

Templated C++ classes with friend functions and implementation-header separation

In the last two posts we saw how to call a constructor from another constructor using friend functions and how to separate the implementation from the header when C++ templates are used. In this post, I'm explaining how to do both, i.e., to use friend functions and templates with the implementation in a separate file from the header. One would expect that this is a pretty straightforward extensions, but, unfortunately, there are a few details that need to be carefully considered.
The concept is thoroughly explained here, but I wanted to exemplify it a bit more to obfuscate any misunderstanding. As it will become clear, one needs to create a "preamble" with class and function definitions before specifying the class prototype and define the friend functions in a special way. Revisiting the example of StreamArrayReader, let's see how the the header file looks like:

/*
 * StreamArrayReader.h
 *
 *  Created on: Nov 1, 2014
 *      Author: Pantelis Sopasakis
 */

#ifndef STREAMARRAYREADER_H_
#define STREAMARRAYREADER_H_

#include <iostream>

using namespace std;


template <typename T>
  class StreamArrayReader;

template <typename T>
  void init_stream_array_reader (
    StreamArrayReader<T> *o, istream * is);

template <typename T>
class StreamArrayReader
{

private:
 istream * in;
 unsigned int data_count;
 T *data;
 StreamArrayReader();
 friend void init_stream_array_reader <> (
           StreamArrayReader<T> *o, istream * is);


public:
 
 StreamArrayReader(istream * in_stream); 
 StreamArrayReader(char * filepath);
 virtual ~StreamArrayReader();
 T* getData() const { return data; }
 unsigned int getDataCount() const { return data_count; }

};

#include "StreamArrayReader.cpp"

#endif /* STREAMARRAYREADER_H_ */

Notice that the function definition

template <typename T>
  void init_stream_array_reader (StreamArrayReader<T> *o, istream * is);

is given before the definition of class StreamArrayReader, so we need to precede its definition:

template <typename T>
  class StreamArrayReader;


Notice how the friend is declared inside the class definition:

friend void init_stream_array_reader <> (
   StreamArrayReader<T> *o, istream * is);

Finally, the file StreamArrayReader.cpp with the function implementations is appended inside the header file. This looks like this:

/*
 * StreamArrayReader.cpp
 *
 *  Created on: Nov 1, 2014
 *      Author: Pantelis Sopasakis
 */

#ifndef STREAMARRAYREADER_IMPL_
#define STREAMARRAYREADER_IMPL_

#include "StreamArrayReader.h"
// Other includes go here

template<typename T>
void init_stream_array_reader(
   StreamArrayReader<T> *o, istream * is) 
{ /* implementation goes here*/ }

template<typename T>
StreamArrayReader<T>::StreamArrayReader() 
{ /* implementation goes here*/ }

template<typename T>
StreamArrayReader<T>::StreamArrayReader(istream * is) 
{ /* implementation goes here*/ }

template<typename T>
StreamArrayReader<T>::StreamArrayReader(char * filepath) 
{ /* implementation goes here*/ }

template<typename T>
StreamArrayReader<T>::~StreamArrayReader() 
{ /* implementation goes here*/ }

#endif





1 comment: