Note that there are some explanatory texts on larger screens.

plurals
  1. POOpaque Data Type in C without opaque pointer
    primarykey
    data
    text
    <p>I'd like to create an opaque type while still allowing users of the type to instantiate it e.g. on the stack by typing</p> <pre><code>struct Foo obj; /*...*/ Foo_init(&amp;obj); Foo_do_something(&amp;obj, param); /*...*/ </code></pre> <p>The customary opaque pointer approach doesn't allow the first line of this example. As a workaround I am placing a public but opaque "data" array inside the public header file with a fixed size. This seems to work for a few examples but I'm a bit unsure about two points:</p> <ol> <li><p>Is it safe to cast the address of the data array as is done in Foo_get_bar and Foo_set_bar? This works okay in my tests but it looks questionable.</p></li> <li><p>If FOO_DATA_SIZE remains fixed is it reasonable to expect ABI compatibility in user code?</p></li> </ol> <hr> <h2>main.c (Example user code)</h2> <pre><code>#include &lt;stdio.h&gt; #include &lt;limits.h&gt; #include "foo.h" int main() { struct Foo foo; Foo_init(&amp;foo); Foo_set_bar(&amp;foo, INT_MAX); int bar = Foo_get_bar(&amp;foo); printf("Got bar: %d\n", bar); } </code></pre> <hr> <h2>foo.h (Public header)</h2> <pre><code>#pragma once #include &lt;inttypes.h&gt; #define FOO_DATA_SIZE (64) struct Foo { uint8_t data[FOO_DATA_SIZE]; }; void Foo_init(struct Foo *f); void Foo_set_bar(struct Foo *f, int barval); int Foo_get_bar(struct Foo *f); </code></pre> <hr> <h2>foo.c (Implementation)</h2> <pre><code>#include "foo.h" #include &lt;stdio.h&gt; #include &lt;string.h&gt; #include &lt;inttypes.h&gt; typedef int64_t bar_t; struct Foo_private { bar_t bar; }; _Static_assert(sizeof(struct Foo_private) &lt;= FOO_DATA_SIZE, "FOO_DATA_SIZE is insufficient for struct Foo_private"); void Foo_init(struct Foo *foo) { struct Foo_private foodata; foodata.bar = (bar_t)0; memcpy(foo-&gt;data, &amp;foodata, sizeof(struct Foo_private)); } void Foo_set_bar(struct Foo *foo, int barval) { struct Foo_private *foodata = (void*)&amp;(foo-&gt;data); foodata-&gt;bar = (bar_t)barval; int stored = (int)foodata-&gt;bar; if (stored != barval) { fprintf(stderr, "Foo_set_bar(%"PRId64"): warning: bar rounded to %"PRId64"\n", (int64_t)barval, (int64_t)stored); } } int Foo_get_bar(struct Foo *foo) { struct Foo_private *foodata = (void*)&amp;(foo-&gt;data); bar_t bar = foodata-&gt;bar; return (int)bar; } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload