Эволюция базового интерфейса

На самом нижнем уровне IBProvider и всех моих остальных, более менее крупных, проектов на С++ лежит единый базовый класс с парой методов — add_ref и release, управляющих счетчиком ссылок на объект.

Судя по истории репозитария (CVS), который был запущен 21 декабря 2000 года, этот класс появился раньше. Наверное, где-то в районе конца 99-го или начала 2000-го кода.

Забавно посмотреть на этапы эволюции этой конструкции, лежащей в основе всей архитектуры.

2000-12-21. Первоначальный вариант.

////////////////////////////////////////////////////////////////////////////////
// облегченные варианты COM объекта

class t_smart_object
{
  long m_cntRef;

 public:
  t_smart_object():m_cntRef(0){;}

 protected:
  virtual ~t_smart_object(){assert(m_cntRef==0);}

 public:
  virtual long add_ref(){return ::InterlockedIncrement(&m_cntRef);}
  virtual long release(){return ::InterlockedDecrement(&m_cntRef);}
  long get_cntRef(){return m_cntRef;}
};

///////////////////////////////////////////////////////////////////////////////
// упрошенный вариант COM-объекта

class t_smart_memory_object:public t_smart_object
{
 public:

  long release()
  {
   long cntRef=t_smart_object::release();

   if(cntRef==0)
    delete this;

   return cntRef;
  }
};

2001-06-24. Осознание, что нужно определять и прятать конструктор и оператор копирования.

////////////////////////////////////////////////////////////////////////////////
// облегченные варианты COM объекта

class t_smart_object
{
 private:
  t_smart_object(const t_smart_object&);
  t_smart_object& operator = (const t_smart_object&);
  
 private:
  long m_cntRef;

 protected: //только для наследования
  t_smart_object():m_cntRef(0){;}

  virtual ~t_smart_object(){assert(m_cntRef==0);}

 public:
  virtual long add_ref(){return ::InterlockedIncrement(&m_cntRef);}
  virtual long release(){return ::InterlockedDecrement(&m_cntRef);}
  long get_cntRef() const {return m_cntRef;}
};

///////////////////////////////////////////////////////////////////////////////
// упрошенный вариант COM-объекта

class t_smart_memory_object:public t_smart_object
{
 private:
  t_smart_memory_object(const t_smart_memory_object&);
  t_smart_memory_object& operator = (const t_smart_memory_object&);

 protected: //только для наследования
  t_smart_memory_object(){;}
   
 public:
  long release()
  {
   long cntRef=t_smart_object::release();

   if(cntRef==0)
    delete this;

   return cntRef;
  }
};

2002-05-08. Добавили отладочный контроль времени жизни. Наверное, после очередного удара граблями по лбу.

///////////////////////////////////////////////////////////////////////////////
// упрошенный вариант COM-объекта

class t_smart_memory_object:public t_smart_object
{
 private:
  t_smart_memory_object(const t_smart_memory_object&);
  t_smart_memory_object& operator = (const t_smart_memory_object&);

  DEBUG_CODE(bool m_is_destroyed;)

 protected: //только для наследования
  t_smart_memory_object()
  {
   DEBUG_CODE(m_is_destroyed=false);
  }
   
 public:
  long release()
  {
   assert(!m_is_destroyed);

   long cntRef=t_smart_object::release();

   if(cntRef==0)
   {
    DEBUG_CODE(m_is_destroyed=true;)
    delete this;
   }
   
   return cntRef;
  }
};//class t_smart_memory_object

2002-09-19. Нормализация определения класса. По моему, это через три дня после запуска проекта для «недвижимости» для которого IBProvider изначально и создавался.

///////////////////////////////////////////////////////////////////////////////
// упрошенный вариант COM-объекта

class t_smart_memory_object:public t_smart_object
{
 private:
  t_smart_memory_object(const t_smart_memory_object&);
  t_smart_memory_object& operator = (const t_smart_memory_object&);

  DEBUG_CODE(bool m_is_destroyed;)

 protected:
  t_smart_memory_object()
  {
   DEBUG_CODE(m_is_destroyed=false);
  }

 public:
  long release();

};//class t_smart_memory_object

2004-04-22. Переделываем на шаблоны (очередная стадия развития программиста на плюсах) и начинаем юзать внешний сервис для инкремента/декремента счетчика ссылок (thread_traits). Кстати, явно указываем virtual у t_basic_smem_obj::release — это борьба с багами компилятора BCB.

////////////////////////////////////////////////////////////////////////////////
//class t_basic_sobj

template<class thread_traits>
class t_basic_sobj
{
 private:
  typedef t_basic_sobj<thread_traits>                 self_type;

  t_smart_object(const self_type&);
  self_type& operator = (const self_type&);

 public: //typedefs ------------------------------------------------------
  typedef thread_traits                               thread_traits_type;
  typedef typename thread_traits_type::int_type       cnt_ref_type;

 protected: //inherited only ---------------------------------------------
  t_basic_sobj()
   :m_cntRef(0)
  {;}

  virtual ~t_basic_sobj();

 public:
  virtual cnt_ref_type add_ref();
  virtual cnt_ref_type release();

 public:
  cnt_ref_type get_cntRef() const
   {return m_cntRef;}

 private:
  cnt_ref_type m_cntRef;
};//class t_basic_sobj

//backward compatibility
typedef t_basic_sobj<t_def_thread_traits>             t_smart_object;

///////////////////////////////////////////////////////////////////////////////
//class t_basic_smem_obj

template<class thread_traits,class Allocator>
class t_basic_smem_obj:public t_basic_sobj<thread_traits>
{
 private:
  typedef t_basic_smem_obj<thread_traits,Allocator>   self_type;
  typedef t_basic_sobj<thread_traits>                 inherited;

  t_smart_memory_object(const self_type&);
  self_type& operator = (const self_type&);

 public: //typedefs -------------------------------------------------------
  typedef typename inherited::cnt_ref_type            cnt_ref_type;

 protected:
  t_basic_smem_obj()
   {DEBUG_CODE(m_is_destroyed=false);}

  virtual ~t_basic_smem_obj();

 public: //t_basic_sobj interface -----------------------------------------
  virtual cnt_ref_type release();

 public:
  static void* operator new (size_t sz)
   {return Allocator().allocate(sz);}

  static void operator delete (void* pv)
   {Allocator().deallocate(pv,0);}

 private:
  DEBUG_CODE(bool m_is_destroyed;)
};//class t_basic_smem_obj

//backward compatibility
typedef t_basic_smem_obj<t_def_thread_traits>  t_smart_memory_object;

2007-01-17. Нормализация.

////////////////////////////////////////////////////////////////////////////////
//class t_basic_sobj

template<class thread_traits>
class t_basic_sobj
{
 private:
  typedef t_basic_sobj<thread_traits>                 self_type;

  t_basic_sobj(const self_type&);
  self_type& operator = (const self_type&);

 public: //typedefs ------------------------------------------------------
  typedef thread_traits                               thread_traits_type;
  typedef typename thread_traits_type::int_type       cnt_ref_type;

 protected: //inherited only ---------------------------------------------
  t_basic_sobj();

  virtual ~t_basic_sobj();

 public:
  virtual cnt_ref_type add_ref();
  virtual cnt_ref_type release();

 public:
  cnt_ref_type get_cntRef()const;

 private:
  cnt_ref_type m_cntRef;

#ifndef NDEBUG
 protected:
  bool sobj_is_destroyed()const {return m_sobj_is_destroyed;}

 protected:
  bool m_sobj_is_destroyed;
#endif
};//class t_basic_sobj

//backward compatibility
typedef t_basic_sobj<t_def_thread_traits>             t_smart_object;

///////////////////////////////////////////////////////////////////////////////
//class t_basic_smem_obj

template<class thread_traits,class Allocator>
class t_basic_smem_obj:public t_basic_sobj<thread_traits>
{
 private:
  typedef t_basic_smem_obj<thread_traits,Allocator>   self_type;
  typedef t_basic_sobj<thread_traits>                 inherited;

  t_basic_smem_obj(const self_type&);
  self_type& operator = (const self_type&);

 public: //typedefs -------------------------------------------------------
  typedef typename inherited::cnt_ref_type            cnt_ref_type;

 protected:
  t_basic_smem_obj();

  virtual ~t_basic_smem_obj();

 public: //t_basic_sobj interface -----------------------------------------
  virtual cnt_ref_type release();

 public:
  static void* operator new (size_t sz);
  static void  operator delete (void* pv);
};//class t_basic_smem_obj

//backward compatibility
typedef t_basic_smem_obj<t_def_thread_traits>  t_smart_memory_object;

2007-08-07. Появился final_release. Вызывается при обнулении счетчика ссылок.

////////////////////////////////////////////////////////////////////////////////
//class t_basic_sobj

template<class thread_traits>
class t_basic_sobj
{
 private:
  typedef t_basic_sobj<thread_traits>                 self_type;

  t_basic_sobj(const self_type&);
  self_type& operator = (const self_type&);

 public: //typedefs ------------------------------------------------------
  typedef thread_traits                               thread_traits_type;
  typedef typename thread_traits_type::int_type       cnt_ref_type;

 protected: //inherited only ---------------------------------------------
  t_basic_sobj();

  virtual ~t_basic_sobj();

 public:
  virtual cnt_ref_type add_ref();
  virtual cnt_ref_type release();

 protected:
  virtual void final_release();
  
 public:
  cnt_ref_type get_cntRef()const;

 private:
  cnt_ref_type m_cntRef;

#ifndef NDEBUG
 protected:
  bool sobj_is_destroyed()const {return m_sobj_is_destroyed;}

 protected:
  bool m_sobj_is_destroyed;
#endif
};//class t_basic_sobj

//backward compatibility
typedef t_basic_sobj<t_def_thread_traits>             t_smart_object;

2009-04-22. Ключевой момент. Появляется абстрактный интерфейс — t_basic_smart_interface. До релиза третьей версии провайдера еще 16 месяцев. В дальнейшем у t_smart_interface начали появляться собственные производные абстрактные интерфейсы. А t_basic_sobj и t_basic_smem_obj постепенно уходят на второй план.

////////////////////////////////////////////////////////////////////////////////
//class t_basic_smart_interface

template<class tag_thread_traits>
class t_basic_smart_interface
{
 public: //typedefs ------------------------------------------------------
  typedef tag_thread_traits                           thread_traits;
  typedef typename thread_traits::int_type            cnt_ref_type;

 public:
  virtual cnt_ref_type add_ref()=0;
  virtual cnt_ref_type release()=0;
};//class t_basic_smart_interface

typedef t_basic_smart_interface<t_def_thread_traits>  t_smart_interface;

////////////////////////////////////////////////////////////////////////////////
//class t_basic_sobj

template<class tag_thread_traits>
class t_basic_sobj:public t_basic_smart_interface<tag_thread_traits>
{
 private:
  typedef t_basic_sobj<tag_thread_traits>             self_type;
  typedef t_basic_smart_interface<tag_thread_traits>  inherited;
  
  t_basic_sobj(const self_type&);
  self_type& operator = (const self_type&);

 public: //typedefs ------------------------------------------------------
  typedef typename inherited::cnt_ref_type            cnt_ref_type;

 protected: //inherited only ---------------------------------------------
  t_basic_sobj();

  virtual ~t_basic_sobj();

 public:
  virtual cnt_ref_type add_ref();
  virtual cnt_ref_type release();

 protected:
  virtual void final_release();
  
 public:
  cnt_ref_type get_cntRef()const;

 private:
  cnt_ref_type m_cntRef;

#ifndef NDEBUG
 protected:
  bool sobj_is_destroyed()const {return m_sobj_is_destroyed;}

 protected:
  bool m_sobj_is_destroyed;
#endif
};//class t_basic_sobj

//backward compatibility
typedef t_basic_sobj<t_def_thread_traits>             t_smart_object;

///////////////////////////////////////////////////////////////////////////////
//class t_basic_smem_obj

template<class tag_thread_traits,class Allocator>
class t_basic_smem_obj:public t_basic_sobj<tag_thread_traits>
{
 private:
  typedef t_basic_smem_obj<tag_thread_traits,Allocator>   self_type;
  typedef t_basic_sobj<tag_thread_traits>                 inherited;

  t_basic_smem_obj(const self_type&);
  self_type& operator = (const self_type&);

 public: //typedefs -------------------------------------------------------
  typedef typename inherited::cnt_ref_type                cnt_ref_type;

 protected:
  t_basic_smem_obj();

  virtual ~t_basic_smem_obj();

 public: //t_basic_sobj interface -----------------------------------------
  virtual cnt_ref_type release();

 public:
  static void* operator new (size_t sz);
  static void  operator delete (void* pv);
};//class t_basic_smem_obj

//backward compatibility
typedef t_basic_smem_obj<t_def_thread_traits>  t_smart_memory_object;

2009-05-11. Нормализация — указываем __declspec(novtable).

////////////////////////////////////////////////////////////////////////////////
//class t_basic_smart_interface

/// <summary>
///  Base class of smart interface.
/// </summary>
template<class tag_thread_traits>
class COMP_CONF_DECLSPEC_NOVTABLE t_basic_smart_interface
{
 public: //typedefs ------------------------------------------------------
  typedef tag_thread_traits                           thread_traits;
  typedef typename thread_traits::int_type            cnt_ref_type;

 public:
  virtual cnt_ref_type add_ref()=0;
  virtual cnt_ref_type release()=0;
};//class t_basic_smart_interface

typedef t_basic_smart_interface<t_def_thread_traits>  t_smart_interface;

Наши дни (2015-10-19). Похоже достигнуто идеальное описание этого интерфейса.

////////////////////////////////////////////////////////////////////////////////
//class t_smart_interface

/// <summary>
///  Base class of smart interface.
/// </summary>
class COMP_CONF_DECLSPEC_NOVTABLE t_smart_interface
{
 public:
  virtual void add_ref()=0;
  virtual void release()=0;
};//class t_basic_smart_interface

В процессе перекомпиляции провайдера нашел одно место, где проверялся результат вызова release:

 /// Останов свободных потоков
 while(!m_free_threads.empty())
 {
  task_thread_type* const thread(m_free_threads.head());

  //поток не должен быть привязан к контроллеру
  // - все свободные потоки проходят процедуру отключения
  //   см. task_thread_processor и add_free_task_thread
  assert(thread->m_task_controller==NULL);

  m_free_threads.remove(tag_thread_list_traits(),thread);

  thread->stop_thread();

  //контролируем, что на поток больше никто не ссылается
  _VERIFY_EQ(thread->release(),0);
 }//while

Но это мелочь, которая была исправлена.

Следующим этапом, думаю, будет определение производного интерфейса с методом query_interface. Я уже несколько раз натыкался на то, что этого метода очень не хватает.

И в итоге приду к тому, что умные люди придумали еще в прошлом тысячелетии: IUnknown.