Skip to content

Subclassing HyPerLayer

Brian Broom-Peltz edited this page Jul 30, 2015 · 1 revision

When creating different kinds of layers, we often want to be able to override methods called in the base class's initialization method. There is a difficulty in the way that C++ handles virtual methods and inheritance.

When a derived class inherits from a base class, and an instance of the derived class is created, the derived class constructor calls a base class constructor. However, once inside the base class constructor, the class type is that of the base class, not the derived class. Hence, if the derived class overrides a virtual method and the base class constructor calls that method (or a non-virtual method that calls the virtual method), the function that gets executed is that of the base class, not the derived class. This behavior prevents overriding certain types of initializations.

To overcome this difficulty, we've implemented the paradigm below for subclassing HyPerLayer. Following the paradigm when designing your own layer classes will make it easier to subclass those classes in the future.

CONSTRUCTORS AND INITIALIZE METHODS

HyPerLayer has a default constructor with no arguments, that's protected. It calls HyPerLayer::initialize_base, a private method, which sets member variables to safe values and doesn't call any other methods or functions.

HyPerLayer::initialize, which is protected, reads the params file. It creates the clayer that holds the activity and V buffers among other things, allocates space for the GSyn buffers, and calls initializeV. Note that HyPerLayer::initialize is not called by the HyPerLayer constructor; instead it is called by subclasses' initialize methods.

Subclasses should have the following:

  • A protected default constructor with no arguments, that calls initialize_base and only initialize_base.
  • Public constructor(s) with arguments, that are called by buildandrun or a customgroups function. They call the class's initialize_base and initialize methods.
  • Private initialize_base() that doesn't call any virtual methods. Often initialize_base() isn't really necessary, but I included them in all the existing layer classes to establish the pattern.
  • Protected initialize() with arguments. It is called by the public constructors and subclasses' initialize methods. It should call the parent class's initialize method exactly once.

PSEUDOCODE FOR .HPP AND .CPP FILES

The .hpp file for a class named DerivedLayer that is derived from a class named BaseLayer should have:

namespace PV {
class DerivedLayer : public BaseLayer {
public:
  DerivedLayer(arguments);
  /* other methods */
protected:
  DerivedLayer();
  int initialize(arguments);
  /* other methods and member variables */
private:
  int initialize_base();
  /* other methods and member variables */
};
}

The .cpp file should have:

namespace PV {
DerivedLayer::DerivedLayer() {
  initialize_base();
}
DerivedLayer::DerivedLayer(arguments, generally includes the layer's name and the parent HyPerCol) {
  initialize_base();
  initialize(arguments);
}
DerivedLayer::initialize_base() {
  /*the most basic initializations.  Don't call any virtual methods,
     or methods that call virtual methods, etc. from initialize_base(); */
}
DerivedLayer::initialize(arguments) {
  /* DerivedLayer-specific initializations that need to precede BaseClass initialization, if any */
  BaseClass::initialize(BaseClass initialization arguments);
  /* DerivedLayer-specific initializations */
}
  /* other DerivedLayer methods */
}

ORDER OF EXECUTION OF INITIALIZE METHODS

If DerivedLayer is a subclass of BaseLayer, which is a subclass of HyPerLayer, then constructing a DerivedClass will call the initialize/initialize_base methods in the following order:

HyPerLayer::initialize_base
BaseLayer::initialize_base
DerivedLayer::initialize_base
DerivedLayer::initialize
the part of the DerivedLayer::initialize code preceding the call to BaseLayer::initialize
BaseLayer::initialize
the part of the BaseLayer::initialize code preceding the call to HyPerLayer::initialize
HyPerLayer::initialize
the part of the BaseLayer::initialize code following the call to HyPerLayer::initialize
the part of the DerivedLayer::initialize code following the call to BaseLayer::initialize

These changes should make it easier to add subclasses to the HyPerLayer hierarchy in the future.