]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Allow using objects in Table and AlignedVector that are not copyable.
authorWolfgang Bangerth <bangerth@colostate.edu>
Tue, 3 Oct 2017 01:02:21 +0000 (19:02 -0600)
committerWolfgang Bangerth <bangerth@colostate.edu>
Wed, 4 Oct 2017 03:23:26 +0000 (21:23 -0600)
include/deal.II/base/aligned_vector.h
include/deal.II/base/table.h

index 82479be127b42b83889a79cd6ab41f49200ca2fb..fc54b7bc16c87010cf76a947020767d8364e0bcc 100644 (file)
@@ -131,14 +131,35 @@ public:
 
   /**
    * Change the size of the vector. It keeps old elements previously
-   * available, and initializes each element with the specified data. If the
-   * new vector size is shorter than the old one, the memory is not released
-   * unless the new size is zero.
+   * available, and initializes each newly added element to a
+   * default-constructed object of type @p T.
+   *
+   * If the new vector size is shorter than the old one, the memory is
+   * not immediately released unless the new size is zero; however,
+   * the size of the current object is of course set to the requested
+   * value.
+   *
+   * @dealiiOperationIsMultithreaded
+   */
+  void resize (const size_type size_in);
+
+  /**
+   * Change the size of the vector. It keeps old elements previously
+   * available, and initializes each newly added element with the
+   * provided initializer.
+   *
+   * If the new vector size is shorter than the old one, the memory is
+   * not immediately released unless the new size is zero; however,
+   * the size of the current object is of course set to the requested
+   * value.
+   *
+   * @note This method can only be invoked for classes that define the copy
+   * assignment operator. Otherwise, compilation will fail.
    *
    * @dealiiOperationIsMultithreaded
    */
   void resize (const size_type size_in,
-               const T        &init = T());
+               const T        &init);
 
   /**
    * Reserve memory space for @p size elements. If the argument @p size is set
@@ -181,6 +202,17 @@ public:
   void insert_back (ForwardIterator begin,
                     ForwardIterator end);
 
+  /**
+   * Fills the vector with size() copies of a default constructed object.
+   *
+   * @note Unlike the other fill() function, this method can also be
+   * invoked for classes that do not define a copy assignment
+   * operator.
+   *
+   * @dealiiOperationIsMultithreaded
+   */
+  void fill ();
+
   /**
    * Fills the vector with size() copies of the given input.
    *
@@ -363,7 +395,7 @@ namespace internal
           {
             // initialize memory (copy construct by placement new), and
             // destruct the source
-            new (&destination_[i]) T(source_[i]);
+            new (&destination_[i]) T(std::move(source_[i]));
             source_[i].~T();
           }
       else
@@ -468,6 +500,97 @@ namespace internal
     }
   };
 
+
+
+  /**
+   * Class that issues the set commands for AlignedVector.
+   *
+   * @tparam initialize_memory Sets whether the set command should
+   * initialize memory (with a call to the copy constructor) or rather use the
+   * copy assignment operator. A template is necessary to select the
+   * appropriate operation since some classes might define only one of those
+   * two operations.
+   *
+   * @relates AlignedVector
+   */
+  template <typename T, bool initialize_memory>
+  class AlignedVectorDefaultInitialize : private parallel::ParallelForInteger
+  {
+    static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1;
+  public:
+    /**
+     * Constructor. Issues a parallel call if there are sufficiently many
+     * elements, otherwise work in serial.
+     */
+    AlignedVectorDefaultInitialize (const std::size_t size,
+                                    T *destination)
+      :
+      destination_ (destination),
+      trivial_element (false)
+    {
+      if (size == 0)
+        return;
+
+      // do not use memcmp for long double because on some systems it does not
+      // completely fill its memory and may lead to false positives in
+      // e.g. valgrind
+      if (std::is_trivial<T>::value == true &&
+          std::is_same<T,long double>::value == false)
+        {
+          const unsigned char zero [sizeof(T)] = {};
+          // cast element to (void*) to silence compiler warning for virtual
+          // classes (they will never arrive here because they are
+          // non-trivial).
+          T element {};
+          if (std::memcmp(zero, (void *)&element, sizeof(T)) == 0)
+            trivial_element = true;
+        }
+      if (size < minimum_parallel_grain_size)
+        apply_to_subrange (0, size);
+      else
+        apply_parallel (0, size, minimum_parallel_grain_size);
+    }
+
+    /**
+     * This initializes elements on a subrange given by two integers.
+     */
+    virtual void apply_to_subrange (const std::size_t begin,
+                                    const std::size_t end) const
+    {
+      // for classes with trivial assignment of zero can use memset. cast
+      // element to (void*) to silence compiler warning for virtual
+      // classes (they will never arrive here because they are
+      // non-trivial).
+      if (std::is_trivial<T>::value == true && trivial_element)
+        std::memset ((void *)(destination_+begin), 0, (end-begin)*sizeof(T));
+      else
+        default_construct_or_assign(begin, end,
+                                    std::integral_constant<bool, initialize_memory>());
+    }
+
+  private:
+    mutable T *destination_;
+    bool trivial_element;
+
+    // copy assignment operation
+    void default_construct_or_assign(const std::size_t begin,
+                                     const std::size_t end,
+                                     std::integral_constant<bool, false>) const
+    {
+      for (std::size_t i=begin; i<end; ++i)
+        destination_[i] = std::move(T());
+    }
+
+    // copy constructor (memory initialization)
+    void default_construct_or_assign(const std::size_t begin,
+                                     const std::size_t end,
+                                     std::integral_constant<bool, true>) const
+    {
+      for (std::size_t i=begin; i<end; ++i)
+        new (&destination_[i]) T;
+    }
+  };
+
 } // end of namespace internal
 
 
@@ -593,7 +716,31 @@ AlignedVector<T>::resize_fast (const size_type size_in)
   // need to still set the values in case the class is non-trivial because
   // virtual classes etc. need to run their (default) constructor
   if (std::is_trivial<T>::value == false && size_in > old_size)
-    dealii::internal::AlignedVectorSet<T,true> (size_in-old_size, T(), _data+old_size);
+    dealii::internal::AlignedVectorDefaultInitialize<T,true> (size_in-old_size, _data+old_size);
+}
+
+
+
+template < class T >
+inline
+void
+AlignedVector<T>::resize (const size_type size_in)
+{
+  const size_type old_size = size();
+  if (std::is_trivial<T>::value == false && size_in < old_size)
+    {
+      // call destructor on fields that are released. doing it backward
+      // releases the elements in reverse order as compared to how they were
+      // created
+      while (_end_data != _data+size_in)
+        (--_end_data)->~T();
+    }
+  reserve (size_in);
+  _end_data = _data + size_in;
+
+  // finally set the desired init values
+  if (size_in > old_size)
+    dealii::internal::AlignedVectorDefaultInitialize<T,true> (size_in-old_size, _data+old_size);
 }
 
 
@@ -745,6 +892,16 @@ AlignedVector<T>::insert_back (ForwardIterator begin,
 
 
 
+template < class T >
+inline
+void
+AlignedVector<T>::fill ()
+{
+  dealii::internal::AlignedVectorDefaultInitialize<T,false> (size(), _data);
+}
+
+
+
 template < class T >
 inline
 void
index cfebb63c632cffe249f00197b4fbd80b93684479..8cfc6ba8dc967aac0e27df4c7debf743a82a682a 100644 (file)
@@ -492,12 +492,12 @@ public:
   void reset_values ();
 
   /**
-   * Set the dimensions of this object to the sizes given in the argument, and
-   * newly allocate the required memory. If
-   * <tt>omit_default_initialization</tt> is set to <tt>false</tt>, all
-   * elements of the table are set to a default constructed object for the
-   * element type. Otherwise the memory is left in an uninitialized or
-   * otherwise undefined state.
+   * Set the dimensions of this object to the sizes given in the first
+   * argument, and allocate the required memory for table entries to
+   * accommodate these sizes. If @p omit_default_initialization
+   * is set to @p false, all elements of the table are set to a
+   * default constructed object for the element type. Otherwise the
+   * memory is left in an uninitialized or otherwise undefined state.
    */
   void reinit (const TableIndices<N> &new_size,
                const bool             omit_default_initialization = false);
@@ -1908,11 +1908,11 @@ TableBase<N,T>::reinit (const TableIndices<N> &new_sizes,
   if (!omit_default_initialization)
     {
       if (values.empty())
-        values.resize(new_size, T());
+        values.resize(new_size);
       else
         {
           values.resize_fast(new_size);
-          values.fill(T());
+          values.fill();
         }
     }
   else

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.