-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
129 lines (111 loc) · 14.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<!DOCTYPE html>
<html lang="english">
<head>
<meta charset="utf-8" />
<meta name="generator" content="Pelican" />
<title>All things computational</title>
<link rel="stylesheet" href="/theme/css/main.css" />
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="/">All things computational</a></h1>
<nav><ul>
<li><a href="/category/fortran.html">Fortran</a></li>
</ul></nav>
</header><!-- /#banner -->
<aside id="featured" class="body">
<article>
<h1 class="entry-title"><a href="/universal-storage-object-in-fortran.html">Universal Storage Object in Fortran</a></h1>
<footer class="post-info">
<abbr class="published" title="2021-07-20T11:00:00+02:00">
Published: Tue 20 July 2021
</abbr>
<address class="vcard author">
By <a class="url fn" href="/author/mobius-eng.html">mobius-eng</a>
</address>
<p>In <a href="/category/fortran.html">Fortran</a>.</p>
<p>tags: <a href="/tag/fortran.html">Fortran</a> <a href="/tag/programming.html">Programming</a> </p>
</footer><!-- /.post-info --><p>Fortran is strictly typed language. And it is <em>really</em> strictly typed. For example, you cannot pass <code>REAL(8)</code> (double precision) variable to where single precision <code>REAL(4)</code> is expected. There is no even <em>type casting</em> in C-sense, but only type conversion. At least this was true until <code>TRANSFER</code> function and C-interoperability were introduced. Now type casting in all its glory and ugliness is possible in Fortran. It must be very explicit though and it does require a few more steps.</p>
<p>This post demonstrates how a universal storage can be created for any Fortran-object. This storage is a building block for a container of objects, such as a list or a dictionary.</p>
<p>A universal storage object, USTORAGE for short, will store any object as a sequence of bytes. It is naturally implemented using an array of one-byte integers. To guarantee the storage size, <code>C_INT8_T</code> integer kind from <code>ISO_C_BINDING</code> intrinsic module is used. Since the size of a stored object is not known in advance, the array either must be allocatable or we must use parameterized derived type (PMT) facility of Fortran 2003. The former will be inefficient for small objects as it will require pointer dereferencing and will reduce memory locality. Thus, we will use the latter. However, there is unfortunately a long-standing bug in GNU Fortran for PMTs. So, the code below does not work for it. It does work with Intel Fortran Classic (<code>ifort</code>) compiler:</p>
<div class="highlight"><pre><span></span><code><span class="o"><</span><span class="p">...</span><span class="o">></span>
<span class="k">use </span><span class="nb">iso_c_binding</span><span class="p">,</span> <span class="k">only</span><span class="p">:</span> <span class="kt">c_int8_t</span><span class="p">,</span> <span class="nb">c_loc</span><span class="p">,</span> <span class="nb">c_f_pointer</span><span class="p">,</span> <span class="kt">c_ptr</span>
<span class="k">implicit none</span>
<span class="k">type</span><span class="p">,</span> <span class="k">public</span> <span class="kd">::</span> <span class="n">ustorage_t</span><span class="p">(</span><span class="nb">len</span><span class="p">)</span>
<span class="kt">integer</span><span class="p">,</span> <span class="nb">len</span> <span class="kd">::</span> <span class="nb">len</span>
<span class="nb"> </span><span class="kt">integer</span><span class="p">(</span><span class="kt">c_int8_t</span><span class="p">),</span> <span class="k">dimension</span><span class="p">(</span><span class="nb">len</span><span class="p">)</span> <span class="kd">::</span> <span class="k">data</span>
<span class="k">contains</span>
<span class="k"> procedure</span><span class="p">,</span> <span class="k">pass</span><span class="p">(</span><span class="n">self</span><span class="p">)</span> <span class="kd">::</span> <span class="n">store</span> <span class="o">=></span> <span class="n">ustorage_store</span>
<span class="k">procedure</span><span class="p">,</span> <span class="k">pass</span><span class="p">(</span><span class="n">self</span><span class="p">)</span> <span class="kd">::</span> <span class="n">retrieve</span> <span class="o">=></span> <span class="n">ustorage_retrieve</span>
<span class="k">end type</span>
<span class="o"><</span><span class="p">...</span><span class="o">></span>
</code></pre></div>
<p>The type supports only two operations, to store the object and to retrieve it. The object is copied bit-by-bit into the storage for the former and retrieved bit-by-bit for the latter.</p>
<p>Let's look at the <code>STORE</code> implementation:</p>
<div class="highlight"><pre><span></span><code><span class="k">subroutine </span><span class="n">ustorage_store</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">item</span><span class="p">)</span>
<span class="k">class</span><span class="p">(</span><span class="n">ustorage_t</span><span class="p">(</span><span class="o">*</span><span class="p">)),</span> <span class="k">intent</span><span class="p">(</span><span class="n">inout</span><span class="p">)</span> <span class="kd">::</span> <span class="n">self</span>
<span class="k">type</span><span class="p">(</span><span class="o">*</span><span class="p">),</span> <span class="k">intent</span><span class="p">(</span><span class="n">in</span><span class="p">)</span> <span class="kd">::</span> <span class="n">item</span>
<span class="kt">integer</span><span class="p">(</span><span class="kt">c_int8_t</span><span class="p">),</span> <span class="k">dimension</span><span class="p">(:),</span> <span class="k">pointer</span> <span class="kd">::</span> <span class="n">pitem</span>
<span class="k">call </span><span class="n">ustorage_get_pointer</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">self</span><span class="p">%</span><span class="nb">len</span><span class="p">,</span> <span class="n">pitem</span><span class="p">)</span>
<span class="n">self</span><span class="p">%</span><span class="k">data</span><span class="p">(:)</span> <span class="o">=</span> <span class="n">pitem</span><span class="p">(:)</span>
<span class="k">end subroutine</span>
</code></pre></div>
<p>The main thing that happens here is the association of <code>PITEM</code> pointer with an <em>assumed-type</em> (<code>TYPE(*)</code>) object <code>ITEM</code> — effectively untyped object. Once this is done, the rest is just a simple copy. This operation is performed in <code>USTORAGE_GET_POINTER</code> subroutine:</p>
<div class="highlight"><pre><span></span><code><span class="k">subroutine </span><span class="n">ustorage_get_pointer</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">pitem</span><span class="p">)</span>
<span class="k">type</span><span class="p">(</span><span class="o">*</span><span class="p">),</span> <span class="k">intent</span><span class="p">(</span><span class="n">in</span><span class="p">),</span> <span class="k">target</span> <span class="kd">::</span> <span class="n">item</span>
<span class="kt">integer</span><span class="p">,</span> <span class="k">intent</span><span class="p">(</span><span class="n">in</span><span class="p">)</span> <span class="kd">::</span> <span class="n">n</span>
<span class="kt">integer</span><span class="p">(</span><span class="kt">c_int8_t</span><span class="p">),</span> <span class="k">dimension</span><span class="p">(:),</span> <span class="k">pointer</span><span class="p">,</span> <span class="k">intent</span><span class="p">(</span><span class="n">inout</span><span class="p">)</span> <span class="kd">::</span> <span class="n">pitem</span>
<span class="k">type</span><span class="p">(</span><span class="kt">c_ptr</span><span class="p">)</span> <span class="kd">::</span> <span class="n">cp</span>
<span class="n">cp</span> <span class="o">=</span> <span class="nb">c_loc</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
<span class="k">call </span><span class="nb">c_f_pointer</span><span class="p">(</span><span class="n">cp</span><span class="p">,</span> <span class="n">pitem</span><span class="p">,</span> <span class="p">[</span><span class="n">n</span><span class="p">])</span>
<span class="k">end subroutine</span>
</code></pre></div>
<p>This routine uses <code>ISO_C_BINDING</code> module extensively as usual Fortran intrinsics will allow neither association nor conversion. So, the trick is to get actual memory address of <code>ITEM</code> using <code>C_LOC</code> function and then convert it to Fortran-pointer. For storage <code>TRANSFER</code> function could also be used. However, that function will not help for retrieval since its use would require the assignment to an assumed-type object. By contrast, <code>USTORAGE_GET_POINTER</code> makes retrieval quite simple:</p>
<div class="highlight"><pre><span></span><code><span class="k">subroutine </span><span class="n">ustorage_retrieve</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">item</span><span class="p">)</span>
<span class="k">class</span><span class="p">(</span><span class="n">ustorage_t</span><span class="p">(</span><span class="o">*</span><span class="p">)),</span> <span class="k">intent</span><span class="p">(</span><span class="n">in</span><span class="p">)</span> <span class="kd">::</span> <span class="n">self</span>
<span class="k">type</span><span class="p">(</span><span class="o">*</span><span class="p">),</span> <span class="k">intent</span><span class="p">(</span><span class="n">inout</span><span class="p">)</span> <span class="kd">::</span> <span class="n">item</span>
<span class="kt">integer</span><span class="p">(</span><span class="kt">c_int8_t</span><span class="p">),</span> <span class="k">dimension</span><span class="p">(:),</span> <span class="k">pointer</span> <span class="kd">::</span> <span class="n">pitem</span>
<span class="k">call </span><span class="n">ustorage_get_pointer</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">self</span><span class="p">%</span><span class="nb">len</span><span class="p">,</span> <span class="n">pitem</span><span class="p">)</span>
<span class="n">pitem</span><span class="p">(:)</span> <span class="o">=</span> <span class="n">self</span><span class="p">%</span><span class="k">data</span><span class="p">(:)</span>
<span class="k">end subroutine</span>
</code></pre></div>
<p>The use of <code>USTORAGE_T</code> object requires one extra step: the <code>LEN</code> parameter needs to be specified by a compile-time constant. <code>SIZEOF</code> intrinsic helps with that. Unfortunately, in contrast to C, one cannot use the type as an argument to this intrinsic but instead need to create a dummy object:</p>
<div class="highlight"><pre><span></span><code><span class="k">type </span><span class="n">point_t</span>
<span class="kt">real</span> <span class="kd">::</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span>
<span class="k">end type</span>
<span class="kt">integer</span><span class="p">,</span> <span class="k">parameter</span> <span class="kd">::</span> <span class="n">point_size</span> <span class="o">=</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">point_t</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">))</span>
<span class="k">type</span><span class="p">(</span><span class="n">ustorage_t</span><span class="p">(</span><span class="n">point_size</span><span class="p">))</span> <span class="kd">::</span> <span class="n">stored_point</span>
<span class="k">type</span><span class="p">(</span><span class="n">point_t</span><span class="p">)</span> <span class="kd">::</span> <span class="n">p</span><span class="p">,</span> <span class="n">q</span>
<span class="n">p</span><span class="p">%</span><span class="n">x</span> <span class="o">=</span> <span class="mf">1.0</span><span class="p">;</span> <span class="n">p</span><span class="p">%</span><span class="n">y</span> <span class="o">=</span> <span class="mf">2.0</span>
<span class="k">call </span><span class="n">stored_point</span><span class="p">%</span><span class="n">store</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="k">call </span><span class="n">stored_point</span><span class="p">%</span><span class="n">retrieve</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
<span class="k">write</span> <span class="p">(</span><span class="o">*</span><span class="p">,</span><span class="o">*</span><span class="p">)</span> <span class="n">q</span><span class="p">%</span><span class="n">x</span><span class="p">,</span> <span class="n">q</span><span class="p">%</span><span class="n">y</span>
</code></pre></div>
<p>After this code, <code>Q</code> will contain a copy of <code>P</code>. Note that the user-side code does not have to deal with pointers and memory addresses — all these details are well hidden.</p> </article>
</aside><!-- /#featured -->
<section id="extras" class="body">
<div class="blogroll">
<h2>links</h2>
<ul>
<li><a href="https://getpelican.com/">Pelican</a></li>
<li><a href="https://www.python.org/">Python.org</a></li>
<li><a href="https://palletsprojects.com/p/jinja/">Jinja2</a></li>
<li><a href="#">You can modify those links in your config file</a></li>
</ul>
</div><!-- /.blogroll -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="#">You can add links in your config file</a></li>
<li><a href="#">Another social link</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">
Proudly powered by <a href="https://getpelican.com/">Pelican</a>, which takes great advantage of <a href="https://www.python.org/">Python</a>.
</address><!-- /#about -->
<p>The theme is by <a href="https://www.smashingmagazine.com/2009/08/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
</footer><!-- /#contentinfo -->
</body>
</html>