Global initialization / Template in Caffe(c++)
本文重点分析深度学习平台(Caffe)中注册(register layer)的相关代码layer_factory.hpp
1. LayerRegistry - 功能模板类
好处:
- 添加新类时,总得修改factory::Create()函数,对于大系统很繁杂
 - factory需要提前知道所有的类
 
template <typename Dtype>  // 模板类(caffe中,预知是float或double)
class LayerRegistry {
 public:
  // 声明类型:函数指针 Creator
  typedef shared_ptr<Layer<Dtype> > (*Creator)(const LayerParameter&);
  // 声明类型:map存放layer-name和layer-creator函数指针的对应关系
  typedef std::map<string, Creator> CreatorRegistry;
  // 静态函数,可以通过类直接调用
  static CreatorRegistry& Registry() {
    // 静态变量,保证上述map(g_registry_)只被new一次
    static CreatorRegistry* g_registry_ = new CreatorRegistry();
    return *g_registry_;  // 整个程序只有1个这样的map, 存放所有的layername->layer-creator函数指针
  }
  // Helper function: Adds a creator.
  static void AddCreator(const string& type, Creator creator) {
    CreatorRegistry& registry = Registry();  // 拿到这个map
    // 如果map.count(key)为0,则代表不存在key(multimap可以有0,1之外的其他值)
    CHECK_EQ(registry.count(type), 0)
        << "Layer type " << type << " already registered.";
    registry[type] = creator;  // 实际的add动作
  }
  // Get a layer using a LayerParameter.
  static shared_ptr<Layer<Dtype> > CreateLayer(const LayerParameter& param) {
    if (Caffe::root_solver()) {
      LOG(INFO) << "Creating layer " << param.name();
    }
    const string& type = param.type();
    CreatorRegistry& registry = Registry();
    CHECK_EQ(registry.count(type), 1) << "Unknown layer type: " << type
        << " (known types: " << LayerTypeListString() << ")";
    // 同样拿到registry, 通过这个map拿到layer creator函数指针,并传入param,实际new了一个layer出来
    return registry[type](param);
  }
  static vector<string> LayerTypeList() {
    CreatorRegistry& registry = Registry();
    vector<string> layer_types;
    // 遍历这个map
    // typename必须: type names nested into dependent types(depend on template type)
    // need to be prepended with the typename keyword.
    // 如果CreatorRegistry实例化为int,则不需要typename了
    for (typename CreatorRegistry::iterator iter = registry.begin();
         iter != registry.end(); ++iter) {
      layer_types.push_back(iter->first);
    }
    return layer_types;
  }
 private:
  // Layer registry should never be instantiated - everything is done with its
  // static variables.
  LayerRegistry() {}
};2. LayerRegisterer - 注册
实际的注册接口
template <typename Dtype>
class LayerRegisterer {
 public:
  // 构造函数,接受type + 函数指针
  LayerRegisterer(const string& type,
                  shared_ptr<Layer<Dtype> > (*creator)(const LayerParameter&)) {
    // LOG(INFO) << "Registering layer type: " << type;
    LayerRegistry<Dtype>::AddCreator(type, creator); // 实际添加
  }
};
#define REGISTER_LAYER_CREATOR(type, creator)                                  \
  static LayerRegisterer<float> g_creator_f_##type(#type, creator<float>);     \
  static LayerRegisterer<double> g_creator_d_##type(#type, creator<double>)    \
#define REGISTER_LAYER_CLASS(type)
// 首先定义这个函数                                                            \
  template <typename Dtype>                                                    \
  shared_ptr<Layer<Dtype> > Creator_##type##Layer(const LayerParameter& param) \
  {                                                                            \
    return shared_ptr<Layer<Dtype> >(new type##Layer<Dtype>(param));//!实际new \
  }
// 随后作为第二个参数,传入上边的define(##:嵌入string, #:转化为string类型)     \
  REGISTER_LAYER_CREATOR(type, Creator_##type##Layer)3. INSTANTIATE_CLASS - 模板类实例化
// template的另一个问题:通过实例化,解决实现模板在.cpp与.h的分离
// Instantiate a class with float and double specifications.
#define INSTANTIATE_CLASS(classname) \
  char gInstantiationGuard##classname; \
  template class classname<float>; \
  template class classname<double>经过实例化,编译器会生成相应type的代码, 否则cpp中并不知道实例化成float或什么
4. SetUP Layer - 使用
例如新添加DispatchLayer层,在dispatch_layer.cpp底部需添加如下宏
INSTANTIATE_CLASS(DispatchLayer);
REGISTER_LAYER_CLASS(Dispatch);其#define经过编译后变成如下代码:
template <typename Dtype>
shared_ptr<Layer<Dtype> > Creator_DispatchLayer(const LayerParameter& param) {
  return shared_ptr<Layer<Dtype> >(new DispatchLayer<Dtype>(param));
}
// 因为有全局静态变量的定义,在其初始化时自动完成了create layer的注册,Dispatch和Creator_DispatchLayer这个函数对应
static LayerRegisterer<float> g_creator_f_Dispatch("Dispatch", Creator_DispatchLayer<float>);
// AddCreator完成了注册Register:将上述的pointer存入map: registry[type] = creator在net.cpp中通过以下代码,将上述类实例化为对象后,就添加到layers_中进行管理
// 调用registry[type](param),取出函数pointer,传入param,也就是实际调用了Creator_DispatchLayer函数,返回了shared_ptr<Layer<Dtype> >
layers_.push_back(LayerRegistry<Dtype>::CreateLayer(layer_param));If the layer is going to be created by another creator function, 可以直接调用REGISTER_LAYER_CREATOR
// Get convolution layer according to engine.
// 自定义creator
template <typename Dtype>
shared_ptr<Layer<Dtype> > GetConvolutionLayer(
    const LayerParameter& param) {
  ...
  if (engine == ConvolutionParameter_Engine_CAFFE) {
    return shared_ptr<Layer<Dtype> >(new ConvolutionLayer<Dtype>(param));
#ifdef USE_CUDNN
  } else if (engine == ConvolutionParameter_Engine_CUDNN) {
    return shared_ptr<Layer<Dtype> >(new CuDNNConvolutionLayer<Dtype>(param));
#endif
  } else {
    LOG(FATAL) << "Layer " << param.name() << " has unknown engine.";
  }
}
REGISTER_LAYER_CREATOR(Convolution, GetConvolutionLayer);5. Targets.cmake - 编译
# Defines global Caffe_LINK flag, This flag is required to prevent linker from excluding
# some objects which are not addressed directly but are registered via static constructors
macro(caffe_set_caffe_link)
  if(BUILD_SHARED_LIBS)
    set(Caffe_LINK caffe)
  else()
    if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
      // -force_load,可以防止编译优化: 否则全局没有调用过的变量,如g_creator_f_Dispatch可能不会被引入
      // include all symbols from a static library
      set(Caffe_LINK -Wl,-force_load caffe)
    elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
      // linux下,whole-archive作用于其后所有的lib,所以得no-whole-archive
      set(Caffe_LINK -Wl,--whole-archive caffe -Wl,--no-whole-archive)
    endif()
  endif()
endmacro()