1 Star 0 Fork 0

mobangjack / memory

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
md_doc_external_usage.html 19.33 KB
一键复制 编辑 原始数据 按行查看 历史
Jonathan Müller 提交于 2020-05-05 17:04 . Update documentation
<!-- HTML header for doxygen 1.8.16-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.18"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>memory: Using RawAllocators in STL containers, smart pointers, etc.</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">memory
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div id="main-nav">
<ul class="sm sm-dox" id="main-menu">
<li><a href="index.html">Main Page</a></li>
<li><a href="md_doc_tutorial.html">Tutorial</a></li>
<li><a href="namespacefoonathan_1_1memory.html">Index</a></li>
<li><a href="files.html">Files</a></li>
</ul>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.18 -->
</div><!-- top -->
<div class="PageDoc"><div class="header">
<div class="headertitle">
<div class="title">Using RawAllocators in STL containers, smart pointers, etc. </div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><h1><a class="anchor" id="autotoc_md47"></a>
1. Using Containers with RawAllocators</h1>
<p>The following listing shows how to use a <a class="el" href="classfoonathan_1_1memory_1_1memory__pool.html">memory_pool</a> with a <code>std::list</code> container:</p>
<div class="fragment"><div class="line"><span class="preprocessor">#include &lt;<a class="code" href="container_8hpp.html">memory/container.hpp</a>&gt;</span> <span class="comment">// for list, list_node_size</span></div>
<div class="line"><span class="preprocessor">#include &lt;<a class="code" href="memory__pool_8hpp.html">memory/memory_pool.hpp</a>&gt;</span> <span class="comment">// for memory_pool</span></div>
<div class="line"> </div>
<div class="line"><span class="keywordtype">int</span> main()</div>
<div class="line">{</div>
<div class="line"> <a class="code" href="classfoonathan_1_1memory_1_1memory__pool.html">memory::memory_pool&lt;&gt;</a> pool(<a class="code" href="structfoonathan_1_1memory_1_1list__node__size.html">memory::list_node_size&lt;int&gt;::value</a>, 1024);</div>
<div class="line"> <a class="code" href="classfoonathan_1_1memory_1_1list.html">memory::list</a>&lt;int, decltype(pool)&gt; list(pool);</div>
<div class="line"> <span class="comment">// use list as normal</span></div>
<div class="line">}</div>
</div><!-- fragment --><p>The first line in <code>main()</code> declares a <a class="el" href="classfoonathan_1_1memory_1_1memory__pool.html">memory_pool</a>. A memory pool is a special kind of allocator that takes a big memory block and separates it into many smaller <a href="md_doc_concepts.html#concept_node">nodes</a> of a given size. Free nodes are put onto a list and can be retrieved in constant time. This structures allows (de-)allocations in any order, but only for a fixed size. But this is exactly the memory footprint for a node based container, like <code>std::list</code>: Each element has the same size and they can be created and destroyed at any time.</p>
<p><a class="el" href="classfoonathan_1_1memory_1_1memory__pool.html">memory_pool</a> is templated but the default parameters are just fine for the most use cases so they are used. Its constructor takes two parameters: The first one is the fixed size of each node. The pool will be used two allocate the nodes for a list of int, but <code>sizeof(int)</code> isn't enough, since each node also stores the two pointers to the next and previous node in the list. To avoid guessing its size which also varies between STL implementations, they are automatically obtained on building and stored in integral constants of the from <code>&lt;container&gt;_node_size&lt;T&gt;</code>. In this case it is a list of <code>int</code> and thus <code>list_node_size&lt;int&gt;value</code> is the node size we need. The second parameter is simply the size of big block that will be seperated. All allocators that are working on bigger memory blocks can grow, if their initial capacity is exhausted, but it is better to use a big size at the beginning.</p>
<p>The second line then actually declares the list. Since <a href="md_doc_concepts.html#concept_rawallocator">RawAllocator</a> provides a conceptually very different interface than <code>Allocator</code>, they cannot be used directly, but need to be wrapped in the class <a class="el" href="classfoonathan_1_1memory_1_1std__allocator.html">std_allocator</a>. It takes a <code>RawAllocator</code> and provides the interface of an <code>Allocator</code> forwarding to the underlying raw allocator and taking care of rebinding, container copying and threading. The raw allocator itself is only stored as reference, not directly embedded. This is required by the <code>Allocator</code> model which wants to copy allocators, but <code>RawAllocator</code> objects are only moveable. In addition, the <code>get_allocator()</code> function of containers only return a copy of the allocator, access to the direct allocator isn't possible. By storing a reference (actually a pointer) inside the <code>std_allocator</code>, copying is enabled and the raw allocator can be accessed either directly or via getter function of the <code>std_allocator</code> object.</p>
<p>For simplicity, template aliases are provided in <code>container.hpp</code> that do the wrapping. The above <code>memory::list&lt;...&gt;</code> is equivalent to <code>std::list&lt;int, memory::std_allocator&lt;int, decltype(pool)&gt;&gt;</code>. Due to the nature of the <code>Allocator</code> model, the <code>value_type</code> has to be repeated twice, also the <code>Allocator</code> is the last template parameter, leading to a very verbose <code>std::unordered_map&lt;Key, Value, std::hash&lt;Key&gt;, std::equal_to&lt;Key&gt;, memory::std_allocator&lt;std::pair&lt;const Key, Value&gt;, RawAllocator&gt;&gt;</code> as opposed to <code>memory::unordered_map&lt;Key, Value, RawAllocator&gt;</code>. But of course the verbose form can be used, in this case <code>std_allocator.hpp</code> has to be included to get <code>std_allocator</code>.</p>
<p>Since the type of <code>list</code> is a normal <code>std::list</code>, just with a special allocator, it provides all the normal functions. The constructor used is the one taking the allocator object, an automatic conversion to <code>std_allocator</code> allows to pass it the pool directly. Then the object can be used as normal, passed to algorithms and freely copied, moved or swapped. <code>std_allocator</code> ensures that each copy uses the same <a class="el" href="classfoonathan_1_1memory_1_1memory__pool.html">memory_pool</a> as its allocator.</p>
<p>The same procedure - create a <a href="md_doc_concepts.html#concept_rawallocator">RawAllocator</a>, wrap it inside a <a class="el" href="classfoonathan_1_1memory_1_1std__allocator.html">std_allocator</a> and pass it to a container, optionally the last two steps combined - can be used for all other STL containers, <code>basic_string</code> or any class taking an <code>Allocator</code> object. Node size values are provided for all node based STL containers (lists, sets and maps).</p>
<h1><a class="anchor" id="autotoc_md48"></a>
2. RawAllocators as Deleter classes</h1>
<p>But not all STL classes require the full <code>Allocator</code>, some only need a <code>Deleter</code>. A <code>Deleter</code> is just a function object that can be called with a pointer and should free it. Like <a class="el" href="classfoonathan_1_1memory_1_1std__allocator.html">std_allocator</a> is a wrapper to provide the <code>Allocator</code> interface, there are two kinds of deleter wrappers defined in <code>deleter.hpp</code>: <a class="el" href="classfoonathan_1_1memory_1_1allocator__deallocator.html">allocator_deallocator</a> and <a class="el" href="classfoonathan_1_1memory_1_1allocator__deleter.html">allocator_deleter</a>. The former just deallocates the memory without calling destructors, the latter does call destructors. They also store a reference instead of the actual allocator for the same reason as in <code>std_allocator</code> and take care of synchronization. And like the container typedefs, there is an easier way to handle the most common use case of deleters: smart pointers.</p>
<p>The following excerpt shows the handling of smart pointers:</p>
<div class="fragment"><div class="line"><span class="preprocessor">#include &lt;<a class="code" href="smart__ptr_8hpp.html">memory/smart_ptr.hpp</a>&gt;</span> <span class="comment">// for allocate_XXX</span></div>
<div class="line">...</div>
<div class="line"><span class="comment">// assume we have a RawAllocator alloc</span></div>
<div class="line">auto unique_ptr = memory::allocate_unique&lt;int&gt;(alloc, 5); <span class="comment">// (1)</span></div>
<div class="line"><span class="keyword">auto</span> array_unique_ptr = memory::allocate_unique&lt;int[]&gt;(alloc, 10u) <span class="comment">// (2)</span></div>
<div class="line"><span class="keyword">auto</span> shared_ptr = memory::allocate_shared&lt;int&gt;(alloc, 7) <span class="comment">// (3)</span></div>
</div><!-- fragment --><p>At (1) a <code>std::unique_ptr</code> is created storing a dynamically allocated <code>int</code> of value <code>5</code> via a <code>RawAllocator</code> <code>alloc</code>. It is another great use case for C++11's <code>auto</code>, the actual type would be <code>std::unique_ptr&lt;int, memory::allocator_deleter&lt;int, RawAllocator&gt;&gt;</code>. The deleter and function also works with arrays, of course, as (2) shows: It creates an array of <code>10</code> value-initialized integers. A similar function is provided for <code>std::shared_ptr</code> used in (3). It uses the <code>std::allocate_shared</code> function internally and thus guarantees the efficient allocation. Like in the standard library, there is no array version for shared pointers. And since the results are instantiations of the actual standard library pointers, they can be used as usual. Especially <code>std::shared_ptr</code> can be very easily integrated, since the actual allocator or deleter is type erased.</p>
<h1><a class="anchor" id="autotoc_md49"></a>
3. Temporary allocations</h1>
<p>The third big use case for allocators besides containers or single objects are temporary allocations. Sometimes an algorithm needs a temporary buffer to store some results. Variable length arrays - although currently not part of the C++ standard - are a common solution. There are either compiler extensions allowing normal variables to be used as array size directly or the more low level approach via <code>alloca()</code>. <code>alloca()</code> allocates memory by simply adjusting the top pointer of the stack. The resulting memory is thus available directly on the stack and will be automatically freed on function exit. The allocation is also much faster than heap allocation directly.</p>
<p>But although <code>alloca()</code> is available on many platforms, it is not portable. In addition, out of memory cannot be reported, since it leads to a stack overflow and nothing can be done then. Thus it is not recommended to use it. Instead, use the <a class="el" href="classfoonathan_1_1memory_1_1temporary__allocator.html">temporary_allocator</a> class available in <code>temporary_allocator.hpp</code>. It does not use the real program stack for the allocation, but its own, separate stack for each thread obtained from the heap.</p>
<p>Below is a simple implementation of a merge sort that uses a temporary buffer:</p>
<div class="fragment"><div class="line"><span class="preprocessor">#include &lt;algorithm&gt;</span></div>
<div class="line"><span class="preprocessor">#include &lt;iterator&gt;</span></div>
<div class="line"> </div>
<div class="line"><span class="preprocessor">#include &lt;<a class="code" href="container_8hpp.html">memory/container.hpp</a>&gt;</span> <span class="comment">// for memory::vector</span></div>
<div class="line"><span class="preprocessor">#include &lt;<a class="code" href="temporary__allocator_8hpp.html">memory/temporary_allocator.hpp</a>&gt;</span> <span class="comment">// for memory::temporary_allocator</span></div>
<div class="line"> </div>
<div class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> RAIter&gt;</div>
<div class="line"><span class="keywordtype">void</span> merge_sort(RAIter begin, RAIter end)</div>
<div class="line">{</div>
<div class="line"> <span class="keyword">using</span> value_type = <span class="keyword">typename</span> std::iterator_traits&lt;RAIter&gt;::value_type;</div>
<div class="line"> </div>
<div class="line"> <span class="keywordflow">if</span> (end - begin &lt;= 1)</div>
<div class="line"> <span class="keywordflow">return</span>;</div>
<div class="line"> <span class="keyword">auto</span> mid = begin + (end - begin) / 2;</div>
<div class="line"> </div>
<div class="line"> <a class="code" href="classfoonathan_1_1memory_1_1temporary__allocator.html">memory::temporary_allocator</a> alloc; <span class="comment">// (1)</span></div>
<div class="line"> <a class="code" href="classfoonathan_1_1memory_1_1vector.html">memory::vector&lt;value_type, memory::temporary_allocator&gt;</a> first(begin, mid, alloc),</div>
<div class="line"> second(mid, end, alloc); <span class="comment">// (2)</span></div>
<div class="line"> </div>
<div class="line"> merge_sort(first.begin(), first.end());</div>
<div class="line"> merge_sort(second.begin(), second.end());</div>
<div class="line"> std::merge(first.begin(), first.end(), second.begin(), second.end(), begin);</div>
<div class="line">}</div>
</div><!-- fragment --><p>The usage of <a class="el" href="classfoonathan_1_1memory_1_1temporary__allocator.html">temporary_allocator</a> is just as usual: At (1), the allocator is created. Then it can be used to create the vectors as usual in (2).</p>
<p>Behind the scenes, a little bit of more work is done. As mentioned the allocator uses its own internal memory stack, one per thread. By default a lot of magic ensures that there is a stack object created when needed and destroyed on thread exit. This internal stack is the <a class="el" href="classfoonathan_1_1memory_1_1temporary__stack.html">temporary_stack</a> and you can access it for the current thread through the <code>get_temporary_stack()</code> function, which is also called by the default constructor of the <a class="el" href="classfoonathan_1_1memory_1_1temporary__allocator.html">temporary_allocator</a>. If the stack wasn't already created for the current thread it will be by calling this function. Once a stack is created it will also be destroyed on thread exit.</p>
<blockquote class="doxtable">
<p>This isn't quite true. On some platforms it might only be destroyed on full program exit, if thread exit can't be created. </p>
</blockquote>
<p>You can also use explicit lifetime control of the stack through the <code>temporary_stack_initializer</code> class. Its constructor will create the stack and the destructor will destroy it. This gives you more control than the "magic" done to ensure the destruction.</p>
<p>Because the per-thread stack managment can has a little overhead, you can control it with the <code>FOONATHAN_MEMORY_TEMPORARY_STACK_MODE</code> variable. If <code>2</code>, the behavior is as described here, with the fully automated managment. If <code>1</code>, you have to use the <code>temporary_stack_initializer</code> to ensure the destructor call, because the automated managment is disabled. And if <code>0</code>, there is no per-thread stack at all, calling <code>get_temporary_stack()</code> is not allowed; you have to create one yourself and pass it to the constructor of <a class="el" href="classfoonathan_1_1memory_1_1temporary__allocator.html">temporary_allocator</a>.</p>
<p>The allocator itself now saves the current top of the stack in its constructor. Allocation will simply move the stack pointer and is such extremely fast. Deallocations are a no-op, because the destructor of the allocator will unwind to the saved position. You cannot move a temporary allocator, it is such not really a <a href="md_doc_concepts.html#concept_rawallocator">RawAllocator</a>. Because of the automatic unwinding in the destructor, you must not allocate from an allocator that isn't the last created object. If the internal stack is exhausted, it can grow although this may lead to a slow heap allocation and can thus be controled by a growth handler. </p>
</div></div><!-- contents -->
</div><!-- PageDoc -->
<div class="ttc" id="aclassfoonathan_1_1memory_1_1temporary__allocator_html"><div class="ttname"><a href="classfoonathan_1_1memory_1_1temporary__allocator.html">foonathan::memory::temporary_allocator</a></div><div class="ttdoc">A stateful RawAllocator that handles temporary allocations.</div><div class="ttdef"><b>Definition:</b> temporary_allocator.hpp:221</div></div>
<div class="ttc" id="aclassfoonathan_1_1memory_1_1vector_html"><div class="ttname"><a href="classfoonathan_1_1memory_1_1vector.html">foonathan::memory::vector</a></div><div class="ttdoc">Alias template for an STL container that uses a certain RawAllocator.</div><div class="ttdef"><b>Definition:</b> container.hpp:47</div></div>
<div class="ttc" id="aclassfoonathan_1_1memory_1_1memory__pool_html"><div class="ttname"><a href="classfoonathan_1_1memory_1_1memory__pool.html">foonathan::memory::memory_pool</a></div><div class="ttdoc">A stateful RawAllocator that manages nodes of fixed size.</div><div class="ttdef"><b>Definition:</b> memory_pool.hpp:47</div></div>
<div class="ttc" id="astructfoonathan_1_1memory_1_1list__node__size_html"><div class="ttname"><a href="structfoonathan_1_1memory_1_1list__node__size.html">foonathan::memory::list_node_size</a></div><div class="ttdoc">Contains the node size of a node based STL container with a specific type.</div><div class="ttdef"><b>Definition:</b> container.hpp:289</div></div>
<div class="ttc" id="aclassfoonathan_1_1memory_1_1list_html"><div class="ttname"><a href="classfoonathan_1_1memory_1_1list.html">foonathan::memory::list</a></div><div class="ttdoc">Alias template for an STL container that uses a certain RawAllocator.</div><div class="ttdef"><b>Definition:</b> container.hpp:67</div></div>
<div class="ttc" id="atemporary__allocator_8hpp_html"><div class="ttname"><a href="temporary__allocator_8hpp.html">temporary_allocator.hpp</a></div></div>
<div class="ttc" id="asmart__ptr_8hpp_html"><div class="ttname"><a href="smart__ptr_8hpp.html">smart_ptr.hpp</a></div></div>
<div class="ttc" id="acontainer_8hpp_html"><div class="ttname"><a href="container_8hpp.html">container.hpp</a></div></div>
<div class="ttc" id="amemory__pool_8hpp_html"><div class="ttname"><a href="memory__pool_8hpp.html">memory_pool.hpp</a></div></div>
<!-- HTML footer for doxygen 1.8.16-->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.18
</small></address>
</body>
</html>
1
https://gitee.com/mobangjack/memory.git
git@gitee.com:mobangjack/memory.git
mobangjack
memory
memory
gh-pages

搜索帮助