Никогда не определенная структура - PullRequest
12 голосов
/ 19 июля 2010

Есть ли какая-то польза в том, что в C никогда не определены структуры?

Пример в исходном коде SQLite:

/* struct sqlite3_stmt is never defined */
typedef struct sqlite3_stmt sqlite3_stmt;

И объектом манипулируют так:

typedef struct Vdbe Vdbe;
struct Vdbe {
    /* lots of members */
};


int sqlite3_step(sqlite3_stmt *pStmt) {
    Vdbe *v = (Vdbe*) pStmt;
    /* do stuff with v... */
}

Так почему бы просто не использовать обычный абстрактный тип с фактической структурой, определяемой в частном порядке в foo.c источнике и общедоступной typedef в foo.h заголовке?

Ответы [ 2 ]

15 голосов
/ 19 июля 2010

Это определено так, чтобы скрыть детали реализации sqlite3_stmt от пользователя, таким образом избегая путаницы внутренних состояний. См. Непрозрачный указатель .

(Это также вынуждает пользователя использовать тип только в качестве указателя, поскольку сама структура sqlite3_stmt имеет неполную реализацию.)


Редактировать: VDBE (механизм виртуальной базы данных) - это просто "конец" API SQLite3. Я считаю, что серверная часть является изменчивой, поэтому sqlite3_stmt* не обязательно Vdbe*. Не показывать Vdbe* в API, потому что детали сервера не должны раскрываться.

9 голосов
/ 19 июля 2010

Чтобы уточнить: вы спрашиваете, почему SQLite делает вышеупомянутое вместо того, чтобы делать это:

Заголовочный файл:

typedef struct sqlite3_stmt sqlite3_stmt;

C файл:

struct sqlite3_stmt {
    /* lots of members */
};


int sqlite3_step(sqlite3_stmt *pStmt) {
    /* do stuff with pStmt... */
}

(Это каноническая форма шаблона "непрозрачный указатель", на который ссылается ответ KennyTM.)

Единственная веская причина, по которой я могу подумать, почему SQLite делает то, что делает, это следующее:

Код бэкэнда, я полагаю, предшествовал API и использовал имя Vdbe - имя, вероятно, означает что-то, связанное с реализацией, в духе «записи в виртуальную базу данных» (здесь мы угадаем).

Когда пришло время создавать API, кто-то понял, что параметр, требуемый для sqlite3_step, является Vdbe, но это не совсем то имя, которое будет много говорить пользователю API. Следовательно, с точки зрения пользователя, Vdbe упоминается как sqlite3_stmt.

Смысл здесь состоит в том, чтобы различать два вида одного и того же элемента: бэкэнд мыслит в терминах Vdbe s (какими бы они ни были), потому что это имя имеет смысл в контексте осуществление . API говорит о sqlite3_stmt s, потому что это имя имеет смысл в контексте интерфейса .

Редактировать: Как указывает Амаргхош, почему бы просто не сделать это для достижения того же эффекта?

typedef struct Vdbe sqlite3_stmt;

KennyTM указывает на вескую возможную причину (пожалуйста, проголосуйте за него, я не хочу отсеивать здесь его повторение): VDBE - только один из нескольких возможных бэкэндов; интерфейс использует «универсальный» sqlite3_stmt, и затем он приводится к тому, что бэкэнд использует для его реализации.

...