You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
<p>This is a key concept, and I want to emphasize it here: relations between objects (such as parent-child) are not stored in the objects, but in a separate worldwide relationship table.</p>
137
137
<h1><aclass="anchor" id="autotoc_md4"></a>
138
138
Binary relations are everywhere</h1>
139
-
<p>Once you get the hang of storing relationships outside of the object, you will find uses for it everywhere. For example, game objects can be member of multiple groups. That’s a many-to-many. Given the group’s handle, you can look up all its members. And when you have a game object, you can get a list of the groups it belongs to.</p>
139
+
<p>Once you get the hang of storing relationships outside of the objects, you will find uses for it everywhere. For example, game objects can be member of multiple groups. That’s a many-to-many. Given the group’s handle, you can look up all its members. And when you have a game object, you can get a list of the groups it belongs to.</p>
140
140
<p>Perhaps a more surprising example is this. Say you have an object type to classify people, vehicles, and buildings. This, too, is a binary relation. In this case it’s a one-to-many. Given an object handle, you can look up what type it is. Given an object type, you can get a list of all objects of that type.</p>
</div><!-- fragment --><h1><aclass="anchor" id="autotoc_md5"></a>
157
157
The API</h1>
158
-
<p>The library is located in the BinaryRelations directory. It consists of a single C++ header file. There are three class templates that you need to know of. The classes are: OneToMany, ManyToMany, and OneToOne.</p>
158
+
<p>The library is located in the BinaryRelations directory. It consists of a single C++ header file. There are three class templates that you need to know of. The classes are: <code>OneToMany</code>, <code>ManyToMany</code>, and <code>OneToOne</code>.</p>
159
159
<p>Each binary relation type is a template, with type arguments <code>LeftType</code> and <code>RightType</code>. <b>Both types need to be small, hashable, and immutable.</b> I recommend that you only use simple types, such as <code>int</code> and <code>enum</code>, and possibly <code>std::string</code>.</p>
160
-
<p>This is the entire OneToMany API. OneToOne and ManyToMany are near identical. <ahref="https://ronpieket.github.io/BinaryRelations">Click here for the full documentation.</a></p>
160
+
<p>This is the entire <code>OneToMany</code> API. <code>OneToOne</code> and <code>ManyToMany</code> are near identical. <ahref="https://ronpieket.github.io/BinaryRelations">Click here for the full documentation.</a></p>
</div><!-- fragment --><h1><aclass="anchor" id="autotoc_md8"></a>
247
-
Naming</h1>
248
-
<p>This is a suggestion.</p>
249
-
<p>In the code example, note that the name of the OneToMany has the form of “SingleToPlural”, like “VehicleToOccupants”. Similarly, ManyToMany names would be “PluralToPlural”, OneToOne would be “SingleToSingle”.</p>
247
+
How it works</h1>
248
+
<p>This section is for those who want to venture into the code.</p>
249
+
<p>I will show you, and talk you through the structure of the <code>OneToMany</code> template class. The <code>OneToOne</code> and <code>ManyToMany</code> template classes are structured along the same lines.</p>
<p>I used the above diagram to write the code. The <code>l2r_it</code> style labels refer to local variable names in the code. <code>m_LeftToRight</code> and <code>m_RightToLeft</code> are both hash tables. Each entry on the <code>m_LeftToRight</code> table contains an <code>std::pair</code> with a left value in the <code>first</code> slot, and a pointer to an <code>std::vector</code> in the <code>second</code>. The <code>vector</code> has one or more <code>right</code> values in it, sorted by value. The <code>m_RightToLeft</code> hash table contains <code>pair</code>s with a <code>right</code> value in the <code>first</code> slot, and a <code>left</code> value in the <code>second</code> slot.</p>
252
+
<p>When <code>findRight()</code> is called, the <code>OneToMany</code> is returns the pointer to the <code>vector</code> of <code>right</code> values from the <code>second</code> slot in the <code>m_LeftToRight</code> hash table. When <code>findLeft()</code> is called, it returns the <code>left</code> value from the <code>second</code> slot in the <code>m_RightToLeft</code> hash table.</p>
250
253
<h1><aclass="anchor" id="autotoc_md9"></a>
251
-
Efficiency</h1>
252
-
<p>The efficiency for lookup such as <code>FindLeft()</code> and <code>FindRight()</code> is constant time. All operations on a OneToOne are also constant.</p>
253
-
<p>Things get more complicated with OneToMany and ManyToMany. They maintain sorted arrays. Insertion and removal of elements in an array involves shifting everything between the point of insertion/removal and the end of the array. In practice, at least in the context of our world editor, this has not been a problem. That’s probably because insert and remove operations are relatively infrequent when compared to lookups.</p>
254
+
Naming of template specializations</h1>
255
+
<p>In the code example, note that the name of the <code>OneToMany</code> has the form of “SingularToPlural”, like “ParentToChildren”. Similarly, ManyToMany names would be “PluralToPlural”, OneToOne would be “SingularToSingular”.</p>
254
256
<h1><aclass="anchor" id="autotoc_md10"></a>
257
+
Efficiency</h1>
258
+
<p>The efficiency for lookup such as <code>FindLeft()</code> and <code>FindRight()</code> is constant time. All operations on a <code>OneToOne</code> are also constant.</p>
259
+
<p>Things get more complicated with <code>OneToMany</code> and <code>ManyToMany</code>. They maintain sorted arrays. Insertion and removal of elements in an array involves shifting everything between the point of insertion/removal and the end of the array. In practice, at least in the context of our world editor, this has not been a problem. That’s probably because insert and remove operations are relatively infrequent when compared to lookups.</p>
260
+
<h1><aclass="anchor" id="autotoc_md11"></a>
255
261
Performance</h1>
256
262
<p>Performance measurements in Xcode on an iMac M1.</p>
257
-
<h2><aclass="anchor" id="autotoc_md11"></a>
263
+
<h2><aclass="anchor" id="autotoc_md12"></a>
258
264
Worst case insert</h2>
259
265
<p>The “worst case” is inserting random numbers on the right with the same left value. Results in milliseconds.</p>
<p><code>std::unordered_map</code> is not the fastest hash map. I’m aware of faster ones, but all those I have found have a license that is more restrictive than the MIT license.</p>
317
323
<p>The array insert operation, which requires shifting half the array on average, is definitely a performance liability. This can be improved by segmenting the array, so that you only have to shift half of the segment.</p>
['best_20case_20insert_1',['Best case insert',['../index.html#autotoc_md12',1,'']]],
4
+
['best_20case_20insert_1',['Best case insert',['../index.html#autotoc_md13',1,'']]],
5
5
['binary_20relations_20are_20everywhere_2',['Binary relations are everywhere',['../index.html',1,'Binary relations are everywhere'],['../index.html#autotoc_md4',1,'Binary relations are everywhere']]]
0 commit comments