What is an "opaque value" in C++?
An example for an Opaque Value is FILE
(from the C library):
#include <stdio.h>
int main()
{
FILE * fh = fopen( "foo", "r" );
if ( fh != NULL )
{
fprintf( fh, "Hello" );
fclose( fh );
}
return 0;
}
You get a FILE
pointer from fopen()
, and use it as a parameter for other functions, but you never bother with what it actually points to.
FILE
, you just pass it around. If you couldn't figure out its contents in headers you could still reverse-engineer it, but you don't have to, because its structure is of no importance. The library implementer could change its internals without any further notice, and it shouldn't make a difference. (Unless, of course, you did hack into its internal structure.) Don't confuse ease of access with accessibility. C++ private
data members aren't "invisible" either... –
Wenzel stdlib.h
–
Hydromel FILE
, fopen
, NULL
, fprintf
, fclose
... sorry, that's all in stdio.h
, which I included. –
Wenzel fopen
function is in stdlib.h
file in college exam and I've got marks for that. Had to confirm with the lecturer. –
Hydromel FILE *x
would work but not FILE x
. Since the standard mandates only the first for stdio
, that's okay. But, even if it weren't, you could still define FILE
as a char array to hide the content and cast it to a real structure within fread/fwrite/etc
. That would reveal only the size. –
Cordovan struct file;
is a declaration (I declare that this thing exists, and will define it elsewhere) rather than a definition (I define the meaning of this thing here). –
Cordovan FILE
needs to be obfuscated in the first place, and think it's misleading and unnecessarily complicating the issue. That's the point: I don't feel your edits add to the answer. Feel free to add an answer of your own. –
Wenzel "Opaque" is defined, in English, as "not able to be seen through; not transparent". In Computer Science, this means a value which reveals no details other then the type of the value itself.
People often use the C type FILE
as the classic example but often this is not opaque - the details are revealed in stdio.h
for anyone to see and they simply rely on the user of the type to not fiddle with the internals. That's fine as long as people stick to the rules, only passing such values to functions like fread()
and fclose()
but the problem with revealing information is that people sometimes (foolishly) begin to rely on it.
For example, glibc
publishes its FILE
structure (as struct _IO_FILE
) in libio.h
so that type is not technically opaque.
Note that part of the definition at the front: "not able" rather than "not willing". Opacity requires the information to be hidden rather than just enacting a "gentleman's agreement" not to use it.
Opaque pointers, done correctly, should reveal no information other than the type name itself and you can implement that in C relatively easily. Consider the following header file prog2.h
for obtaining and releasing xyzzy
objects:
struct xyzzy;
struct xyzzy *xyzzyOpen (void);
void xyzzyClose (struct xyzzy *fh);
This is all that clients of the code see, an incomplete type struct xyzzy
and some functions to allocate and release objects of that type (they don't get to see prog2.c
detailed below). Note that pointers to an incomplete type are fine but you cannot instantiate an object of that type since you don't know its internals. So the code:
struct xyzzy myvar;
would cause an error along the lines of:
prog1.c: In function ‘main’:
prog1.c:3:15: error: storage size of 'myvar' isn't known
Now you can quite happily use those functions from a program prog1.c
without knowing the internals of the structure:
#include "prog2.h"
int main (void) {
//struct xyzzy myvar; // will error
struct xyzzy *num1 = xyzzyOpen();
struct xyzzy *num2 = xyzzyOpen();
struct xyzzy *num3 = xyzzyOpen();
xyzzyClose (num1);
xyzzyClose (num3); // these two intentionally
xyzzyClose (num2); // reversed.
return 0;
}
And the implementation of the calls, prog2.c
, actually controls and knows the internals, so can use them quite freely:
#include <stdio.h>
#include <stdlib.h>
#include "prog2.h"
struct xyzzy { int payload; };
static int payloadVal = 42;
struct xyzzy *xyzzyOpen (void) {
struct xyzzy *plugh = malloc (sizeof (struct xyzzy));
plugh->payload = payloadVal++;
printf ("xyzzyOpen payload = %d\n", plugh->payload);
return plugh;
}
void xyzzyClose (struct xyzzy *plugh) {
printf ("xyzzyClose payload = %d\n", plugh->payload);
free (plugh);
}
The printf
calls are there simply to show that it can use the internals, and you'd probably want to add checking of the return value from malloc
in production-ready code but that's not relevant to the purpose of this example.
When you compile prog1.c
and prog2.c
into a single executable and run it, the output is:
xyzzyOpen payload = 42
xyzzyOpen payload = 43
xyzzyOpen payload = 44
xyzzyClose payload = 42
xyzzyClose payload = 44
xyzzyClose payload = 43
as you would expect from the main function.
prog2.c
, I would be able to access the internals of the struct. Is the struct still "opaque" by your definition, then? Or does true opacity require a closed-source programming model? (Just pulling your leg there, trying to point to my definition of "opacity" looking at the documentation, not the header source. ;-) ) –
Wenzel prog2.c
, you could examine the assembly code to figure it out. In fact, the only way to truly hide it is to not release it, which rather damages its usefulness. And a fully defined FILE
within stdio.h could be considered opaque to people who have no clue how to find the header :-) –
Cordovan FILE
is opaque (according to paxdiablo's and your definition) is entirely up to the implementation (because you could readily apply paxdiablo's technique to FILE
as well). You might also note that, while disagreeing on the subject, paxdiablo and myself were able to do so without getting rude and calling each other "wrong" or "obstinate". Plus, my answer predates this one by a comfortable margin, so there is nothing I have to "admit" here. –
Wenzel An example for an Opaque Value is FILE
(from the C library):
#include <stdio.h>
int main()
{
FILE * fh = fopen( "foo", "r" );
if ( fh != NULL )
{
fprintf( fh, "Hello" );
fclose( fh );
}
return 0;
}
You get a FILE
pointer from fopen()
, and use it as a parameter for other functions, but you never bother with what it actually points to.
FILE
, you just pass it around. If you couldn't figure out its contents in headers you could still reverse-engineer it, but you don't have to, because its structure is of no importance. The library implementer could change its internals without any further notice, and it shouldn't make a difference. (Unless, of course, you did hack into its internal structure.) Don't confuse ease of access with accessibility. C++ private
data members aren't "invisible" either... –
Wenzel stdlib.h
–
Hydromel FILE
, fopen
, NULL
, fprintf
, fclose
... sorry, that's all in stdio.h
, which I included. –
Wenzel fopen
function is in stdlib.h
file in college exam and I've got marks for that. Had to confirm with the lecturer. –
Hydromel FILE *x
would work but not FILE x
. Since the standard mandates only the first for stdio
, that's okay. But, even if it weren't, you could still define FILE
as a char array to hide the content and cast it to a real structure within fread/fwrite/etc
. That would reveal only the size. –
Cordovan struct file;
is a declaration (I declare that this thing exists, and will define it elsewhere) rather than a definition (I define the meaning of this thing here). –
Cordovan FILE
needs to be obfuscated in the first place, and think it's misleading and unnecessarily complicating the issue. That's the point: I don't feel your edits add to the answer. Feel free to add an answer of your own. –
Wenzel That's similar to opaque pointer - a value that doesn't store data your code could interpret or provide access to data, but only identifies some other data. A typical example is a Win32 handle like HBITMAP
bitmap handle - you can only pass it to relevant functions, but you can't do anything to the underlying bitmap directly.
FILE* is a good example of an opaque value. You don't use it directly; it's a single "blob" which you can't interpret or manipulate. Instead, you use a set of functions (fopen, fwrite, fprintf, etc.) which know how to manipulate it.
Being opaque in this way is common to many situations (and in many APIs) where you have a "magical" handle: a black box.
from Wikipedia
In computer science, an opaque data type is a data type that is incompletely defined in an interface, so that its values can only be manipulated by calling subroutines that have access to the missing information. The concrete representation of the type is hidden from its users
Typical examples of opaque data types include handles for resources provided by an operating system to application software.
Some languages, such as C, allow the declaration of opaque records (structs), whose size and fields are hidden from the client. The only thing that the client can do with an object of such a type is to take its memory address, to produce an opaque pointer.
If the information provided by the interface is sufficient to determine the type's size, then clients can declare variables, fields, and arrays of that type, assign their values, and possibly compare them for equality. This is usually the case for opaque pointers.
© 2022 - 2024 — McMap. All rights reserved.