Apa yang seharusnya dan apa yang seharusnya tidak ada dalam file header? [Tutup]

71

Hal-hal apa yang benar-benar tidak boleh dimasukkan dalam file header?

Jika misalnya saya bekerja dengan format standar industri terdokumentasi yang memiliki banyak konstanta, apakah itu praktik yang baik untuk mendefinisikannya dalam file header (jika saya menulis parser untuk format itu)?

Fungsi apa yang harus masuk ke file header?
Fungsi apa yang seharusnya tidak?

Moshe Magnes
sumber
1
Singkat dan tidak menyakitkan: definisi dan pernyataan yang diperlukan dalam lebih dari satu modul.
ott--
21
Menandai pertanyaan ini sebagai "terlalu luas" dan menutup adalah berlebihan yang benar-benar memalukan. Pertanyaan ini persis menanyakan apa yang saya cari - Pertanyaan terbentuk dengan baik dan menanyakan pertanyaan yang sangat jelas: apa praktik terbaik? Jika ini "terlalu luas" untuk rekayasa perangkat lunak .. kita juga bisa menutup seluruh forum ini.
Gewure
TL; DR. Untuk C ++, dalam edisi keempat "Bahasa Pemrograman C ++" yang ditulis oleh Bjarne Stroustrup (pembuatnya), dalam Bagian 15.2.2 dijelaskan apa yang harus dan tidak boleh berisi header. Saya tahu Anda menandai pertanyaan ke C, tetapi beberapa saran juga berlaku. Saya pikir ini adalah pertanyaan yang bagus ...
horro

Jawaban:

57

Apa yang dimasukkan ke dalam tajuk:

  • Set #includearahan minimal yang diperlukan untuk membuat header dapat dikompilasi ketika header dimasukkan dalam beberapa file sumber.
  • Simbol preprosesor definisi hal-hal yang perlu dibagikan dan yang hanya dapat dicapai melalui preprosesor. Bahkan dalam C, simbol preprosesor sebaiknya dijaga agar tetap minimum.
  • Maju deklarasi struktur yang diperlukan untuk membuat definisi struktur, prototipe fungsi, dan deklarasi variabel global dalam tubuh header dapat dikompilasi.
  • Definisi struktur data dan enumerasi yang dibagikan di antara banyak file sumber.
  • Deklarasi untuk fungsi dan variabel yang definisinya akan terlihat oleh penghubung.
  • Definisi fungsi sebaris, tapi hati-hati di sini.

Apa yang tidak termasuk dalam tajuk:

  • #includeArahan serampangan . Mereka serampangan termasuk menyebabkan kompilasi ulang hal-hal yang tidak perlu dikompilasi ulang, dan kadang-kadang bisa membuatnya sehingga sistem tidak dapat dikompilasi. Jangan #includefile dalam header jika header itu sendiri tidak memerlukan file header lainnya.
  • Simbol preprosesor yang niatnya dapat dicapai oleh beberapa mekanisme, mekanisme apa pun, selain preprosesor.
  • Banyak dan banyak definisi struktur. Bagi mereka menjadi header yang terpisah.
  • Definisi fungsi sebaris yang membutuhkan tambahan #include, yang dapat berubah, atau yang terlalu besar. Fungsi-fungsi sebaris harus memiliki sedikit jika ada kipas keluar, dan jika mereka memiliki kipas keluar, itu harus dilokalisasi ke hal-hal yang didefinisikan dalam header.

Apa yang merupakan set minimal #includepernyataan?

Ini ternyata menjadi pertanyaan tidak trivial. Definisi TL; DR: File header harus menyertakan file header yang secara langsung mendefinisikan masing-masing jenis yang digunakan secara langsung atau yang secara langsung mendeklarasikan setiap fungsi yang digunakan dalam file header yang dimaksud, tetapi tidak boleh menyertakan hal lain. Pointer atau tipe referensi C ++ tidak memenuhi syarat sebagai penggunaan langsung; referensi ke depan lebih disukai.

Ada tempat untuk #includearahan gratis , dan ini dalam tes otomatis. Untuk setiap file header dalam paket perangkat lunak, saya secara otomatis menghasilkan dan kemudian mengkompilasi yang berikut:

#include "path/to/random/header_under_test"
int main () { return 0; }

Kompilasi harus bersih (mis. Bebas dari segala peringatan atau kesalahan). Peringatan atau kesalahan mengenai tipe tidak lengkap atau tipe tidak dikenal berarti bahwa file header yang diuji memiliki beberapa #includearahan yang hilang dan / atau hilang deklarasi maju. Catatan baik: Hanya karena tes lulus tidak berarti bahwa set #includearahan cukup, apalagi minimal.

David Hammen
sumber
Jadi, jika saya memiliki perpustakaan yang mendefinisikan struct, disebut A, dan perpustakaan ini, disebut B, menggunakan struct itu, dan perpustakaan B digunakan oleh program C, haruskah saya menyertakan file header library A di header utama library B, atau haruskah Saya hanya maju menyatakannya? perpustakaan A dikompilasi dan dihubungkan dengan perpustakaan B selama kompilasi itu.
MarcusJ
@MarcusJ - Hal pertama yang saya daftarkan di bawah Apa yang tidak termasuk dalam header adalah pernyataan #include serampangan. Jika file header B tidak bergantung pada definisi dalam file header A, jangan # menyertakan file header A dalam file header B. File header bukan tempat untuk menentukan dependensi pihak ketiga atau membuat instruksi. Mereka pergi ke tempat lain seperti file readme tingkat atas.
David Hammen
1
@ MarscJ - Saya memperbarui jawaban saya dalam upaya untuk menjawab pertanyaan Anda. Perhatikan bahwa tidak ada satu jawaban untuk pertanyaan Anda. Saya akan mengilustrasikannya dengan beberapa ekstrem. Kasus 1: Satu-satunya tempat di mana perpustakaan B secara langsung menggunakan fungsionalitas perpustakaan A adalah di file sumber perpustakaan B. Kasus 2: Perpustakaan B adalah ekstensi tipis fungsionalitas di perpustakaan A, dengan file header untuk perpustakaan B secara langsung menggunakan tipe dan / atau fungsi yang didefinisikan di perpustakaan A. Dalam kasus 1, tidak ada alasan untuk mengekspos perpustakaan A di header untuk perpustakaan B. Dalam kasus 2, paparan ini cukup wajib.
David Hammen
Ya itu kasus 2, maaf komentar saya melewatkan fakta bahwa itu menggunakan jenis yang dideklarasikan di perpustakaan A di header B perpustakaan, saya berpikir saya mungkin bisa meneruskan menyatakannya, tapi saya tidak berpikir itu akan berhasil. Terima kasih atas pembaruannya.
MarcusJ
Apakah menambahkan konstanta ke file header besar tidak-tidak?
mding5692
15

Selain apa yang sudah dikatakan.

File H harus selalu mengandung:

  • Dokumentasi kode sumber !!! Minimal, apa tujuan dari berbagai parameter dan mengembalikan nilai fungsi.
  • Pelindung kepala, #ifndef MYHEADER_H #define MYHEADER_H ... #endif

File H tidak boleh mengandung:

  • Segala bentuk alokasi data.
  • Definisi fungsi. Fungsi sebaris mungkin pengecualian langka dalam beberapa kasus.
  • Apa pun yang berlabel static.
  • Typedefs, #define, atau konstanta yang tidak memiliki relevansi dengan aplikasi lainnya.

(Saya juga akan mengatakan bahwa tidak pernah ada alasan untuk menggunakan variabel global / eksternal non-konstan, di mana saja, tapi itu diskusi untuk pos lain.)


sumber
1
Saya setuju dengan semuanya, kecuali apa yang telah Anda soroti. Jika Anda membuat perpustakaan, ya, Anda harus mendokumentasikan atau pengguna perpustakaan Anda. Untuk proyek in-house Anda tidak perlu mengacaukan tajuk Anda dengan dokumentasi, jika Anda menggunakan nama variabel dan fungsi yang baik dan jelas.
martiert
5
@martiert Saya juga dari sekolah "biarkan kode berbicara sendiri". Namun Anda setidaknya harus selalu mendokumentasikan fungsi Anda, bahkan jika tidak ada orang selain Anda yang akan menggunakannya. Hal-hal yang menarik adalah: jika fungsi memiliki penanganan kesalahan, kode kesalahan apa yang dikembalikan dan dalam kondisi apa ia gagal? Apa yang terjadi pada parameter (buffer, pointer dll) jika fungsi gagal? Hal lain yang sangat relevan adalah: apakah parameter pointer mengembalikan sesuatu ke pemanggil, yaitu apakah mereka mengharapkan memori yang dialokasikan? ->
1
Harus jelas bagi penelepon penanganan kesalahan apa yang dilakukan di dalam fungsi dan apa yang tidak dilakukan. Jika fungsi mengharapkan buffer yang dialokasikan, kemungkinan besar akan meninggalkan pemeriksaan di luar batas kepada pemanggil juga. Jika fungsi ini bergantung pada fungsi lain yang akan dieksekusi, ini harus didokumentasikan (yaitu menjalankan link_list_init () sebelum link_list_add ()). Dan akhirnya, jika fungsi tersebut memiliki "efek samping" seperti membuat file, utas, timer atau apa pun, itu harus dinyatakan dalam dokumentasi. ->
1
Mungkin "dokumentasi kode sumber" terlalu luas di sini, ini benar-benar milik kode sumber. "Dokumentasi penggunaan" dengan input dan output, prekondisi dan kondisi akhir serta efek sampingnya harus benar-benar ada, tidak dalam bentuk epik tetapi dalam bentuk singkat .
Amankan
2
Sedikit terlambat, tetapi +1 untuk dokumentasi. Mengapa kelas ini ada? Kode tidak berbicara sendiri. Apa fungsi ini lakukan? RTFC (baca file .cpp yang halus) adalah akronim empat huruf yang cabul. Seseorang seharusnya tidak perlu RTFC untuk memahami. Prototipe dalam header harus merangkum, dalam beberapa komentar yang dapat diekstraksi (mis., Oksigen), apa argumennya dan apa fungsinya. Mengapa anggota data ini ada, apa yang dikandungnya, dan apakah nilainya dalam meter, kaki, atau garis-garis? Itu juga merupakan subjek lain untuk komentar (yang dapat diekstraksi).
David Hammen
4

Saya mungkin tidak akan mengatakan tidak pernah, tetapi pernyataan yang menghasilkan data dan kode seperti diuraikan seharusnya tidak dalam file .h.

Makro, fungsi inline, dan templat mungkin terlihat seperti data atau kode, tetapi makro tidak menghasilkan kode saat diuraikan, tetapi saat digunakan. Barang-barang ini sering perlu digunakan di lebih dari satu .c atau .cpp, sehingga barang-barang tersebut termasuk dalam .h.

Dalam pandangan saya, file header harus memiliki antarmuka praktis minimum untuk .c atau .cpp yang sesuai. Antarmuka dapat mencakup # definisi, kelas, typedef, definisi struktur, prototipe fungsi, dan definisi eksternal yang kurang disukai, untuk variabel global. Namun, jika deklarasi hanya digunakan dalam satu file sumber, itu mungkin harus dikecualikan dari .h dan terkandung dalam file sumber sebagai gantinya.

Beberapa mungkin tidak setuju, tetapi kriteria pribadi saya untuk file .h adalah bahwa mereka # mencantumkan semua file .h lain yang mereka butuhkan untuk dapat dikompilasi. Dalam beberapa kasus, ini bisa menjadi banyak file, jadi kami memiliki beberapa metode yang efektif untuk mengurangi dependensi eksternal seperti meneruskan deklarasi ke kelas yang memungkinkan kami menggunakan pointer ke objek kelas tanpa menyertakan apa yang bisa menjadi pohon besar file include.

Pengembang Don
sumber
3

File header harus memiliki organisasi berikut:

  • jenis dan definisi konstan
  • deklarasi objek eksternal
  • deklarasi fungsi eksternal

File header tidak boleh berisi definisi objek, hanya ketik definisi dan deklarasi objek.

theD
sumber
Bagaimana dengan definisi fungsi sebaris?
Kos
Jika fungsi inline adalah fungsi "pembantu" yang hanya digunakan di dalam satu modul C, maka letakkan di file .c saja. Jika, fungsi inline harus terlihat oleh dua modul atau lebih, letakkan di dalam file header.
theD
Selain itu, jika fungsi tersebut harus terlihat melintasi batas perpustakaan, jangan membuatnya sejajar karena memaksa semua orang yang menggunakan perpustakaan untuk mengkompilasi ulang setiap kali Anda memodifikasi sesuatu.
Donal Fellows
@DonalFellows: Itu solusi backhanded. Aturan yang lebih baik: Jangan letakkan barang di header yang sering mengalami modifikasi. Tidak ada yang salah dengan inlining fungsi kecil pendek di header jika fungsi tidak memiliki fanout dan memiliki definisi yang jelas yang hanya akan berubah jika struktur data yang mendasarinya berubah. Jika definisi fungsi berubah karena definisi struktur yang mendasarinya berubah, ya, Anda harus mengkompilasi ulang semuanya, tetapi Anda harus tetap melakukannya karena definisi struktur berubah.
David Hammen
0

Pernyataan yang menghasilkan data dan kode saat diuraikan, tidak boleh dalam .hfile. Sejauh menyangkut sudut pandang saya, file header seharusnya hanya memiliki antarmuka praktis minimum untuk yang sesuai .catau .cpp.

Ajay Prasad
sumber