代码之家  ›  专栏  ›  技术社区  ›  Praetorian

C++单模板模板类

  •  3
  • Praetorian  · 技术社区  · 14 年前

    /**
     * @brief 
     *    Singleton design pattern implementation using a dynamically allocated singleton instance.
     *
     * The SingletonDynamic class is intended for use as a base for classes implementing the Singleton
     * design pattern and require lazy initialization of the singleton object. The default 
     * implementation is not thread-safe, however, the derived classes can make it so by reinitializing
     * the function pointers SingletonDynamic<T>::pfnLockMutex, SingletonDynamic<T>::pfnUnlockMutex
     * and SingletonDynamic<T>::pfnMemoryBarrier. The member function pointers are initialized by 
     * default to point to placeholder functions that do not perform any function. The derived class
     * must provide alternate implementations for SingletonDynamic<T>::lock_mutex(),
     * SingletonDynamic<T>::unlock_mutex() and SingletonDynamic<T>::memory_barrier() respectively
     * and reinitialize the respective function pointer members to these alternate implementations.
     *
     * @tparam T
     *    The type name of the derived (singleton) class
     *
     * @note The derived class must have a no-throw default constructor and a no-throw destructor.
     * @note The derived class must list this class as a friend, since, by necessity, the derived class'
     *       constructors must be protected / private.
     */
    template< typename T >
    class SingletonDynamic
    {
    public:
      /**
       * Factory function for vending mutable references to the sole instance of the singleton object.
       *
       * @return A mutable reference to the one and only instance of the singleton object.
       */
      static T &instance()
      {
        return *SingletonDynamic< T >::get_instance();
      }
    
    
      /**
       * Factory function for vending constant references to the sole instance of the singleton object.
       *
       * @return A constant reference to the one and only instance of the singleton object.
       */
      static const T &const_instance()
      {
        return *SingletonDynamic< T >::get_instance();
      }
    
    protected:
      /** Default constructor */
      SingletonDynamic() {}
    
      /** Destructor */
      virtual ~SingletonDynamic() 
      {
        delete SingletonDynamic< T >::pInstance_;
      }
    
      /** Defines an alias for a function pointer type for executing functions related to thread-safety */
      typedef void(*coherence_callback_type)();
    
      /** 
       * Pointer to a function that will lock a mutex denying access to threads other that the current 
       * 
       * @note The function must have the signature void foo()
       * @note The derived class must never set this variable to NULL, doing so will cause a crash. The 
       *       default value must be left unchanged if this functionality is not desired.
       */
      static coherence_callback_type  pfnLockMutex;
    
      /** 
       * Pointer to a function that will unlock a mutex allowing access to other threads 
       * 
       * @note The function must have the signature void foo()
       * @note The derived class must never set this variable to NULL, doing so will cause a crash. The 
       *       default value must be left unchanged if this functionality is not desired.
       */
      static coherence_callback_type  pfnUnlockMutex;
    
      /** 
       * Pointer to a function that executes a memory barrier instruction that prevents the compiler
       * from reordering reads and writes across this boundary.
       * 
       * @note The function must have the signature void foo()
       * @note The derived class must never set this variable to NULL, doing so will cause a crash. The 
       *       default value must be left unchanged if this functionality is not desired.
       */
      static coherence_callback_type  pfnMemoryBarrier;
    
    private:
      /** The sole instance of the singleton object */
      static T *pInstance_;
    
      /** Flag indicating whether the singleton object has been created */
      static volatile bool flag_;
    
      /** Private copy constructor to prevent copy construction */
      SingletonDynamic( SingletonDynamic const & );
    
      /** Private operator to prevent assignment */
      SingletonDynamic &operator=( SingletonDynamic const & );
    
    
      /** 
       * Fetches a pointer to the singleton object, after creating it if necessary
       *
       * @return A pointer to the one and only instance of the singleton object.
       */
      static T *get_instance()
      {
        if( SingletonDynamic< T >::flag_ == false ) {
          /* acquire lock */
          (*SingletonDynamic< T >::pfnLockMutex)();
    
          if( SingletonDynamic< T >::pInstance_ == NULL ) {
            pInstance_ = new T();
          }
    
          /* release lock */
          (*SingletonDynamic< T >::pfnUnlockMutex)();
    
          /* enforce all prior I/O to be completed */
          (*SingletonDynamic< T >::pfnMemoryBarrier)();
    
          SingletonDynamic< T >::flag_ = true;
    
          return SingletonDynamic< T >::pInstance_;
        } else {
          /* enforce all prior I/O to be completed */
          (*SingletonDynamic< T >::pfnMemoryBarrier)();
    
          return SingletonDynamic< T >::pInstance_;
        }
      }
    
    
      /**
       * Placeholder function for locking a mutex, thereby preventing access to other threads. This 
       * default implementation does not perform any function, the derived class must provide an 
       * implementation if this functionality is desired.
       */
      inline static void lock_mutex()
      {
        /* default implementation does nothing */
        return;
      }
    
    
      /**
       * Placeholder function for unlocking a mutex, thereby allowing access to other threads. This 
       * default implementation does not perform any function, the derived class must provide an 
       * implementation if this functionality is desired.
       */
      inline static void unlock_mutex()
      {
        /* default implementation does nothing */
        return;
      }
    
    
      /**
       * Placeholder function for executing a memory barrier instruction, thereby preventing the 
       * compiler from reordering read and writes across this boundary. This default implementation does 
       * not perform any function, the derived class must provide an implementation if this 
       * functionality is desired.
       */
      inline static void memory_barrier()
      {
        /* default implementation does nothing */
        return;
      }
    };
    
    /* Initialize the singleton instance pointer */
    template< typename T >
    T *SingletonDynamic<T>::pInstance_        = NULL;
    
    /* Initialize the singleton flag */
    template< typename T >
    volatile bool SingletonDynamic<T>::flag_  = false;
    
    /* Initialize the function pointer that locks the mutex */
    template< typename T >
    typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnLockMutex  
                                                                  = &SingletonDynamic<T>::lock_mutex;
    
    /* Initialize the function pointer that unlocks the mutex */
    template< typename T >
    typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnUnlockMutex  
                                                                  = &SingletonDynamic<T>::unlock_mutex;
    
    /* Initialize the function pointer that executes the memory barrier instruction */
    template< typename T >
    typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnMemoryBarrier
                                                                  = &SingletonDynamic<T>::memory_barrier;
    

    我特别担心头文件中的静态成员初始化,以及当从SingleDynamic派生的类的头文件包含在多个文件中时,这是否会导致多个定义错误。我已经试过了,它似乎工作,但我不明白为什么它工作:)。

    提前谢谢,

    编辑:

    /**
     * This is the default ConcurrencyPolicy implementation for the SingletonDynamic class. This 
     * implementation does not provide thread-safety and is merely a placeholder. Classes deriving from
     * SingletonDynamic must provide alternate ConcurrencyPolicy implementations if thread-safety is
     * desired.
     */
    struct DefaultSingletonConcurrencyPolicy
    {
      /**
       * Placeholder function for locking a mutex, thereby preventing access to other threads. This 
       * default implementation does not perform any function, the derived class must provide an 
       * alternate implementation if this functionality is desired.
       */
      static void lock_mutex() 
      { 
        /* default implementation does nothing */
        return;
      }
    
      /**
       * Placeholder function for unlocking a mutex, thereby allowing access to other threads. This 
       * default implementation does not perform any function, the derived class must provide an 
       * alternate implementation if this functionality is desired.
       */
      static void unlock_mutex()
      {
        /* default implementation does nothing */
        return;
      }
    
      /**
       * Placeholder function for executing a memory barrier instruction, thereby preventing the 
       * compiler from reordering read and writes across this boundary. This default implementation does 
       * not perform any function, the derived class must provide an alternate implementation if this 
       * functionality is desired.
       */
      static void memory_barrier()
      {
        /* default implementation does nothing */
        return;
      }
    };
    
    
    /**
     * @brief 
     *    Singleton design pattern implementation using a dynamically allocated singleton instance.
     *
     * The SingletonDynamic class is intended for use as a base for classes implementing the Singleton
     * design pattern and that dynamic allocation of the singleton object. The default implementation 
     * is not thread-safe; however, the class uses a policy-based design pattern that allows the derived 
     * classes to achieve threaad-safety by providing an alternate implementation of the 
     * ConcurrencyPolicy.
     *
     * @tparam T
     *    The type name of the derived (singleton) class
     * @tparam ConcurrencyPolicy
     *    The policy implementation for providing thread-safety
     *
     * @note The derived class must have a no-throw default constructor and a no-throw destructor.
     * @note The derived class must list this class as a friend, since, by necessity, the derived class'
     *       constructors must be protected / private.
     */
    template< typename T, typename ConcurrencyPolicy = DefaultSingletonConcurrencyPolicy >
    class SingletonDynamic : public ConcurrencyPolicy
    {
    public:
      /**
       * Factory function for vending mutable references to the sole instance of the singleton object.
       *
       * @return A mutable reference to the one and only instance of the singleton object.
       */
      static T &instance()
      {
        return *SingletonDynamic< T, ConcurrencyPolicy >::get_instance();
      }
    
    
      /**
       * Factory function for vending constant references to the sole instance of the singleton object.
       *
       * @return A constant reference to the one and only instance of the singleton object.
       */
      static const T &const_instance()
      {
        return *SingletonDynamic< T, ConcurrencyPolicy >::get_instance();
      }
    
    protected:
      /** Default constructor */
      SingletonDynamic() {}
    
      /** Destructor */
      virtual ~SingletonDynamic() 
      {
        delete SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
      }
    
    private:
      /** The sole instance of the singleton object */
      static T *pInstance_;
    
      /** Flag indicating whether the singleton object has been created */
      static volatile bool flag_;
    
      /** Private copy constructor to prevent copy construction */
      SingletonDynamic( SingletonDynamic const & );
    
      /** Private operator to prevent assignment */
      SingletonDynamic &operator=( SingletonDynamic const & );
    
    
      /** 
       * Fetches a pointer to the singleton object, after creating it if necessary
       *
       * @return A pointer to the one and only instance of the singleton object.
       */
      static T *get_instance()
      {
        if( SingletonDynamic< T, ConcurrencyPolicy >::flag_ == false ) {
          /* acquire lock */
          ConcurrencyPolicy::lock_mutex();
    
          /* create the singleton object if this is the first time */
          if( SingletonDynamic< T, ConcurrencyPolicy >::pInstance_ == NULL ) {
            pInstance_ = new T();
          }
    
          /* release lock */
          ConcurrencyPolicy::unlock_mutex();
    
          /* enforce all prior I/O to be completed */
          ConcurrencyPolicy::memory_barrier();
    
          /* set flag to indicate singleton has been created */
          SingletonDynamic< T, ConcurrencyPolicy >::flag_ = true;
    
          return SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
        } else {
          /* enforce all prior I/O to be completed */
          ConcurrencyPolicy::memory_barrier();
    
          return SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
        }
      }
    };
    
    /* Initialize the singleton instance pointer */
    template< typename T, typename ConcurrencyPolicy >
    T *SingletonDynamic< T , ConcurrencyPolicy >::pInstance_        = NULL;
    
    /* Initialize the singleton flag */
    template< typename T, typename ConcurrencyPolicy >
    volatile bool SingletonDynamic< T , ConcurrencyPolicy >::flag_  = false;
    
    4 回复  |  直到 14 年前
        1
  •  1
  •   Omnifarious    14 年前

    这里与并发相关的代码的正确性很难评估。在我看来,这个实现是在试图变得有点太聪明了。

    one definition rule .

    pInstance 在适当的.cpp文件中。

    还有人建议依赖编译器特定的行为来初始化静态局部变量。我不认为这是一个可怕的建议,但当您不能依赖编译器来做正确的事情时,有一个选择可能会很好。

        2
  •  3
  •   IanH    14 年前

    模板类的静态成员必须在头文件中初始化,并且最近的C++编译器及其链接器必须正确处理。

    但你是对的,一些非常旧的编译器对此有问题。

    在这些情况下,对于使用单例模板的每种类型,在任意编译单元中只初始化静态成员一次是一种解决方法。

    http://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html .

    显然一个非常坏的主意,当它来存储一个单一的。。。。

    更糟糕的是,库(第三方框架)中使用的唯一单例是一些通常以相同方式初始化的配置对象,因此只有在运行时更改配置时才会出现错误。 跟踪这个bug花了好几天时间,直到我们最终在反汇编中看到“同一”成员在不同的内存区域被访问。

        4
  •  1
  •   Matthieu M.    14 年前

    目前,在C++中,在多线程环境中懒惰地创建一个单体是不可能的。

    许多大师(其中包括赫伯萨特)都承认,标准的当前状态并不能保证任何东西。各种编译器都有黑客攻击,boost提供了 once 不过,它是一个由编译器特定指令组成的杂七杂八的集合。。。它不是标准C++(它是线程不知道的)。

    目前唯一有效的解决方案(根据标准)是在启动多个线程之前初始化单例,或者在进程的某个部分中,保证只有一个线程访问它。

    static MyType& Instance() { static Instance MExemplar; return MExemplar; }
    

    在这种情况下,根本不需要单例模板类。