From c1c7cd95f9be1c0d817601290c0b428cee4c307b Mon Sep 17 00:00:00 2001 From: Wolfgang Bangerth Date: Fri, 20 Feb 2015 17:37:41 -0600 Subject: [PATCH] Figure out and document a way to call the FESystem constructor with multiple base elements. This is non-trivial in C++ before C++11 and in particular if one wants to avoid memory leaks. --- include/deal.II/fe/fe_system.h | 175 ++++++++++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 1 deletion(-) diff --git a/include/deal.II/fe/fe_system.h b/include/deal.II/fe/fe_system.h index 38d68af64f..be154475f8 100644 --- a/include/deal.II/fe/fe_system.h +++ b/include/deal.II/fe/fe_system.h @@ -236,8 +236,181 @@ public: * Same as above but for any number of base elements. Pointers to the base * elements and their multiplicities are passed as vectors to this * constructor. The lengths of these vectors are assumed to be equal. + * + * As above, the finite element objects pointed to by the first argument are + * not actually used other than to create copies internally. Consequently, + * you can delete these pointers immediately again after calling this + * constructor. + * + *

How to use this constructor

+ * + * Using this constructor is a bit awkward at times because you need to pass + * two vectors in a place where it may not be straightforward to construct + * such a vector -- for example, in the member initializer list of a class + * with an FESystem member variable. For example, if your main class looks + * like this: + * @code + * template + * class MySimulator { + * public: + * MySimulator (const unsigned int polynomial_degree); + * private: + * FESystem fe; + * }; + * + * template + * MySimulator::MySimulator (const unsigned int polynomial_degree) + * : + * fe (...) // what to pass here??? + * {} + * @endcode + * + * If your compiler supports the C++11 language standard (or later) and + * deal.II has been configured to use it, then you could do something + * like this to create an element with four base elements and + * multiplicities 1, 2, 3 and 4: + * @code + * template + * MySimulator::MySimulator (const unsigned int polynomial_degree) + * : + * fe (std::vector*> { new FE_Q(1), + * new FE_Q(2), + * new FE_Q(3), + * new FE_Q(4) }, + * std::vector { 1, 2, 3, 4 }) + * {} + * @endcode + * This creates two vectors in place and initializes them using the + * initializer list enclosed in braces { ... }. + * + * This code has a problem: it creates four memory leaks because the first + * vector above is created with pointers to elements that are allocated + * with new but never destroyed. Without C++11, you have another + * problem: brace-initializer don't exist in earlier C++ standards. + * + * The solution to the second of these problems is to create two static + * member functions that can create vectors. Here is an example: + * @code + * template + * class MySimulator { + * public: + * MySimulator (const unsigned int polynomial_degree); + * + * private: + * FESystem fe; + * + * static std::vector*> + * create_fe_list (const unsigned int polynomial_degree); + * + * static std::vector + * create_fe_multiplicities (); + * }; + * + * template + * std::vector*> + * MySimulator::create_fe_list (const unsigned int polynomial_degree) + * { + * std::vector*> fe_list; + * fe_list.push_back (new FE_Q(1)); + * fe_list.push_back (new FE_Q(2)); + * fe_list.push_back (new FE_Q(3)); + * fe_list.push_back (new FE_Q(4)); + * return fe_list; + * } + * + * template + * std::vector + * MySimulator::create_fe_multiplicities () + * { + * std::vector multiplicities; + * multiplicities.push_back (1); + * multiplicities.push_back (2); + * multiplicities.push_back (3); + * multiplicities.push_back (4); + * return multiplicities; + * } + * + * template + * MySimulator::MySimulator (const unsigned int polynomial_degree) + * : + * fe (create_fe_list (polynomial_degree), + * create_fe_multiplicities ()) + * {} + * @endcode + * + * The way this works is that we have two static member functions that + * create the necessary vectors to pass to the constructor of the member + * variable fe. They need to be static because they are called + * during the constructor of MySimulator at a time when the + * *this object isn't fully constructed and, consequently, + * regular member functions cannot be called yet. + * + * The code above does not solve the problem with the memory leak yet, + * though: the create_fe_list() function creates a vector of + * pointers, but nothing destroys these. This is the solution: + * @code + * template + * class MySimulator { + * public: + * MySimulator (const unsigned int polynomial_degree); + * + * private: + * FESystem fe; + * + * struct VectorElementDestroyer { + * const std::vector*> data; + * VectorElementDestroyer (const std::vector*> &pointers); + * ~VectorElementDestroyer (); // destructor to delete the pointers + * const std::vector*> & get_data () const; + * }; + * + * static std::vector*> + * create_fe_list (const unsigned int polynomial_degree); + * + * static std::vector + * create_fe_multiplicities (); + * }; + * + * template + * MySimulator::VectorElementDestroyer:: + * VectorElementDestroyer (const std::vector*> &pointers) + * : data(pointers) + * {} + * + * template + * MySimulator::VectorElementDestroyer:: + * ~VectorElementDestroyer () + * { + * for (unsigned int i=0; i + * const std::vector*> & + * MySimulator::VectorElementDestroyer:: + * get_data () const + * { + * return data; + * } + * + * + * template + * MySimulator::MySimulator (const unsigned int polynomial_degree) + * : + * fe (VectorElementDestroyer(create_fe_list (polynomial_degree)).get_data(), + * create_fe_multiplicities ()) + * {} + * @endcode + * + * In other words, the vector we receive from the + * create_fe_list() is packed into a temporary object of type + * VectorElementDestroyer; we then get the vector from this + * temporary object immediately to pass it to the constructor of + * fe; and finally, the VectorElementDestroyer + * destructor is called at the end of the entire expression (after the + * constructor of fe has finished) and destroys the elements of + * the temporary vector. Voila: not short nor elegant, but it works! */ - FESystem (const std::vector*> &fes, const std::vector &multiplicities); -- 2.39.5