Never defined structure
Is there any benefit in having never-defined structures in C ?
Example in SQLite source code :
/* struct sqlite3_stmt is never defined */
typedef struct sqlite3_stmt sqlite3_stmt;
And the object is manipulated like so :
typedef struct Vdbe Vdbe;
struct Vdbe {
/* lots of members */
};
int sqlite3_step(sqlite3_stmt *pStmt) {
Vdbe *v = (Vdbe*) pStmt;
/* do stuff with v... */
}
So why not just use a usual abstract type, with the actual st开发者_如何学编程ructure privately defined in foo.c
source and a public typedef
in foo.h
header ?
It is defined like this to hide the implementation detail of sqlite3_stmt
from the user, thus avoiding the internal states from being messed around. See Opaque pointer.
(This also forces the user only to use the type as a pointer since the structure sqlite3_stmt
itself has incomplete implementation.)
Edit: VDBE (virtual database engine) is just "a" back-end of the SQLite3 API. I believe the back-end is changeable, thus a sqlite3_stmt*
is not necessarily a Vdbe*
. Not exposing Vdbe*
in the API because the back-end detail should not be exposed.
To clarify: What you're asking is why SQLite does the above instead of doing this:
Header file:
typedef struct sqlite3_stmt sqlite3_stmt;
C file:
struct sqlite3_stmt {
/* lots of members */
};
int sqlite3_step(sqlite3_stmt *pStmt) {
/* do stuff with pStmt... */
}
(This is the canonical form of the "opaque pointer" pattern linked to in KennyTM's answer.)
The only good reason I can think of why SQLite does what it does is the following:
The backend code, I'm speculating, came before the API and used the name Vdbe
-- the name probably means something related to the implementation along the lines of "virtual database entry" (guessing wildly here).
When time came to create the API, someone realized that the parameter required by sqlite3_step
was a Vdbe
but that this was not exactly a name that would convey a lot to the user of the API. Hence, from the user's point of view, a Vdbe
is referred to as an sqlite3_stmt
.
The point here, then, is to differentiate between two views of the same item: The backend thinks in terms of Vdbe
s (whatever they are) because that's a name that makes sense in the context of the implementation. The API talks about sqlite3_stmt
s because that's a name that makes sense in the context of the interface.
Edit: As Amarghosh points out, why not just do this to achieve the same effect?
typedef struct Vdbe sqlite3_stmt;
KennyTM points out a good possible reason (please vote him up, I don't want to siphon off his rep here): VDBE is only one of several possible backends; the interface uses a "generic" sqlite3_stmt
, and this is then cast to whatever the backend uses to implement it.
精彩评论