Search code examples
c++templatesinterfacevirtualmember-functions

Interface for template member functions


Is there some common way to define a interface for template member functions? I would like to create some pure abstract base classes with declarations of template member functions that should be overridden in the derived classes. Than I want to be able to call the functions of the derived classes through the interface. I know that virtual template member functions are not allowed. So till now I came up with the following solution (see below). What I would like to know is if my approach is bad design or if there are some better ways to achieve my goal:

Edit: What I want to implement is an serialization system (similar to boost::serialization). Therefor I would like to have a common base-interface function serialize which can be used to save and load objects. The correct function calls for save and load will be determined by the provided derived archive classes when calling the *do_serialize* function.

Because I'm not sure about my provided solution I would like to ask if there are maybe better ways (or even simpler ways) to achieve my goal?

The interfaces:

template<typename Derived>
class Archive{
public:
  virtual ~Archive() = 0 {}

  template<typename T>
  Archive& serialize(T t) {
    Derived* derived = static_cast<Derived*>(this);
    return derived->serialize_derived(t);
  }
};

template<typename Derived>
class Writer : public Archive<Derived> {
public:
  virtual ~Writer() = 0 {}

  template<typename T> 
  void write(const T& t) {
    Derived* derived = static_cast<Derived*>(this);
    derived->write_derived(t);
  }
};

template<typename Derived>
class Reader : public Archive<Derived> {
public:
  virtual ~Reader() = 0 {}

  template<typename T> 
  void read(const T& t) {
    Derived* derived = static_cast<Derived*>(this);
    derived->read_derived(t);
  }
};

The derived classes:

class BinaryWriter : public Writer<BinaryWriter> {
public:
  template<typename T>
  BinaryWriter& serialize_derived(T t) {
    save(t);
    return *this;
  }

  template<typename T>
  void save(T t) {
    std::cout << "DerivedWriter::save: " << t << std::endl;
  }

  template<typename T> 
  void write_derived(const T& t) {
    std::cout << "DerivedWriter::write_derived: " << t << std::endl;
  }
};

class BinaryReader : public Reader<BinaryReader> {
public:
  template<typename T>
  BinaryReader& serialize_derived(T t) {
    load(t);
    return *this;
  }

  template<typename T>
  void load(T t) {
    std::cout << "DerivedWriter::load: " << t << std::endl;
  }

  template<typename T> 
  void read_derived(const T& t) {
    std::cout << "DerivedReader::read_derived: " << t << std::endl;
  }
};

The example test function and calls (Note: calls to the Writer/Reader-interface are required in similarly (other derived Writer/Reader-classes are here omitted (i.e. TextReader, XMLReader, etc.))):

template<typename Derived, typename T>
void do_serialize(Archive<Derived>& obj, T t) {
  obj.serialize(t);
}

DerivedWriter dw;
DerivedReader dr;

do_serialize(dw, 1);
do_serialize(dr, 2);

The output:

DerivedWriter::save: 1
DerivedReader::read: 2

Solution

  • After knowing this technique is called CRTP (thanks to pmr) I found this post CRTP to avoid virtual member function overhead which I think answers my question. As stated in the answer of Kerrek SB in that post this technique can be used to provide an interface. In my case this interface can be defined even for template member functions.

    It seems this pattern is quite common (which I was not sure about) so I will accept it as the answer to my question.

    Thanks for all your replies!