Memastikan bahwa tajuk secara eksplisit termasuk dalam file CPP

9

Saya pikir itu umumnya praktik yang baik untuk #includeheader untuk semua jenis yang digunakan dalam file CPP, terlepas dari apa yang sudah disertakan melalui file HPP. Jadi saya mungkin #include <string>di HPP dan CPP saya, misalnya, meskipun saya masih bisa mengkompilasi jika saya melewatkannya di CPP. Dengan cara ini saya tidak perlu khawatir tentang apakah HPP saya menggunakan deklarasi maju atau tidak.

Apakah ada alat yang dapat menerapkan #includegaya pengkodean ini ? Haruskah saya menerapkan gaya pengkodean ini?

Karena preprocessor / compiler tidak peduli apakah #includeitu berasal dari HPP atau CPP, saya tidak mendapat umpan balik jika saya lupa mengikuti gaya ini.

M. Dudley
sumber

Jawaban:

7

Ini adalah salah satu dari jenis standar pengkodean "harus" daripada "harus". Alasannya adalah Anda harus menulis parser C ++ untuk menjalankannya.

Aturan yang sangat umum untuk file header adalah mereka harus berdiri sendiri. File tajuk tidak boleh mengharuskan beberapa file tajuk lainnya disertakan # sebelum memasukkan tajuk yang dimaksud. Ini adalah persyaratan yang dapat diuji. Diberikan beberapa tajuk acak foo.hh, yang berikut ini harus dikompilasi dan dijalankan:

#include "foo.hh"
int main () {
   return 0;
}

Aturan ini memiliki konsekuensi berkaitan dengan penggunaan kelas lain di beberapa header. Terkadang konsekuensi tersebut dapat dihindari dengan meneruskan mendeklarasikan kelas-kelas lainnya. Ini tidak mungkin dengan banyak kelas perpustakaan standar. Tidak ada cara untuk meneruskan mendeklarasikan contoh template seperti std::stringatau std::vector<SomeType>. Anda harus memiliki #includetajuk STL di tajuk meskipun hanya menggunakan jenis ini sebagai argumen untuk fungsi.

Masalah lain adalah dengan hal-hal yang secara tidak sengaja Anda seret. Contoh: pertimbangkan yang berikut:

file foo.cc:

#include "foo.hh"
#include "bar.hh"

void Foo::Foo () : bar() { /* body elided */ }

void Foo::do_something (int item) {
   ...
   bar.add_item (item);
   ...
}

Berikut baradalah Foodata anggota kelas yang bertipe Bar. Anda telah melakukan hal yang benar di sini dan telah memasukkan # bar.hh meskipun itu harus dimasukkan dalam header yang mendefinisikan kelas Foo. Namun, Anda belum memasukkan barang yang digunakan oleh Bar::Bar()dan Bar::add_item(int). Ada banyak kasus di mana panggilan ini dapat menghasilkan referensi eksternal tambahan.

Jika Anda menganalisis foo.odengan alat seperti nmitu, akan tampak bahwa fungsi di foo.ccmemanggil semua jenis hal yang belum Anda lakukan sesuai #include. Jadi, haruskah Anda menambahkan #includearahan untuk referensi eksternal insidentil itu foo.cc? Jawabannya sama sekali tidak. Masalahnya adalah sangat sulit untuk membedakan fungsi-fungsi yang dipanggil secara kebetulan dari yang dipanggil secara langsung.

David Hammen
sumber
2

Haruskah saya menerapkan gaya pengkodean ini?

Mungkin tidak. Aturan saya adalah ini: penyertaan file header tidak dapat bergantung pada pesanan.

Anda dapat memverifikasi ini dengan cukup mudah dengan aturan sederhana bahwa file pertama yang disertakan oleh xc adalah xh

kevin cline
sumber
1
Bisakah Anda menguraikan ini? Saya tidak bisa melihat bagaimana itu akan benar-benar memverifikasi independensi pesanan.
detly
1
itu tidak benar-benar menjamin kemandirian ketertiban - itu hanya memastikan bahwa #include "x.h"akan bekerja tanpa memerlukan beberapa sebelumnya #include. Itu cukup baik jika Anda tidak menyalahgunakan #define.
kevin cline
1
Oh begitu. Masih ide bagus kalau begitu.
Detly
2

Jika Anda perlu menegakkan aturan bahwa file header tertentu harus berdiri sendiri, Anda dapat menggunakan alat yang sudah Anda miliki. Buat makefile dasar yang mengkompilasi setiap file header secara terpisah tetapi tidak menghasilkan file objek. Anda akan dapat menentukan mode mana untuk mengkompilasi file header dalam (mode C atau C ++) dan memverifikasi bahwa itu bisa berdiri sendiri. Anda dapat membuat asumsi yang masuk akal bahwa output tidak mengandung false positive, semua dependensi yang diperlukan dinyatakan dan output akurat.

Jika Anda menggunakan IDE, Anda mungkin masih dapat melakukannya tanpa makefile (tergantung pada IDE Anda). Cukup buat proyek tambahan, tambahkan file header yang ingin Anda verifikasi dan ubah pengaturan untuk mengkompilasinya sebagai file C atau C ++. Di MSVC misalnya, Anda akan mengubah pengaturan "Jenis Item" di "Properti Konfigurasi-> Umum".

Kapten Obvlious
sumber
1

Saya tidak berpikir alat seperti itu ada, tetapi saya akan senang jika ada jawaban lain yang menyangkal saya.

Masalah dengan menulis alat seperti itu adalah sangat mudah melaporkan hasil yang salah, jadi saya memperkirakan keuntungan bersih dari alat tersebut mendekati nol.

Satu-satunya cara alat semacam itu bisa bekerja adalah jika alat itu dapat mengatur ulang tabel simbolnya menjadi hanya isi file header yang diproses, tetapi kemudian Anda mengalami masalah bahwa header yang membentuk API eksternal perpustakaan mendelegasikan deklarasi aktual untuk tajuk internal.
Misalnya, <string>dalam implementasi libc ++ GCC tidak mendeklarasikan apa pun, tetapi hanya menyertakan sekelompok header internal yang berisi deklarasi aktual. Jika alat mereset tabel simbolnya menjadi hanya apa yang dideklarasikan dengan <string>sendirinya, itu bukan apa-apa.
Anda bisa membuat alat membedakan antara #include ""dan #include <>, tetapi itu tidak membantu Anda jika pustaka eksternal menggunakan #include ""untuk memasukkan tajuk internal dalam API.

Bart van Ingen Schenau
sumber
1

tidak #Pragma oncemencapai ini? Anda dapat memasukkan sesuatu sebanyak yang Anda inginkan, baik secara langsung atau melalui rantainya, dan selama ada di #Pragma oncesebelah masing-masing, tajuk hanya disertakan sekali.

Untuk menegakkannya, mungkin Anda bisa membuat sistem build yang hanya menyertakan setiap header dengan sendirinya dengan beberapa fungsi utama dummy, hanya untuk memastikannya dikompilasi. #ifdefrantai termasuk untuk hasil terbaik dengan metode pengujian itu.

Ericson2314
sumber
1

Selalu sertakan file header dalam file CPP. Ini tidak hanya mempersingkat waktu kompilasi secara signifikan, tetapi juga menghemat banyak masalah jika Anda memutuskan untuk pergi dengan header yang telah dikompilasi. Dalam pengalaman saya, bahkan melakukan masalah deklarasi maju layak saat latihan. Langgar aturan hanya bila perlu.

Gvozden
sumber
Bisakah Anda menjelaskan bagaimana ini akan "mempersingkat waktu kompilasi secara signifikan"?
Mawg mengatakan mengembalikan Monica
1
Ini memastikan bahwa setiap unit kompilasi (file CPP) hanya menarik minimum file yang disertakan pada waktu kompilasi. Kalau tidak, jika Anda memasukkan menyertakan dalam file H, rantai ketergantungan palsu salah terbentuk dengan cepat, dan Anda akhirnya menarik semua menyertakan dengan setiap kompilasi.
Gvozden
Dengan menyertakan penjaga, saya mungkin mempertanyakan "secara signifikan", tetapi saya kira akses disk (untuk mengetahui bahwa mereka termasuk pengawal) adalah "lambat" sehingga akan menyerahkan intinya,. Terima kasih untuk mengklarifikasi
Mawg kata mengembalikan Monica
0

Saya akan mengatakan ada kelebihan dan kekurangan dari konvensi ini. Di satu sisi senang mengetahui persis apa yang termasuk dalam file .cpp Anda. Di sisi lain, daftar menyertakan dapat dengan mudah tumbuh ke ukuran yang konyol.

Salah satu cara untuk mendorong konvensi ini adalah tidak memasukkan apa pun di header Anda sendiri, tetapi hanya dalam file .cpp. Maka setiap file .cpp yang menggunakan header Anda tidak akan dikompilasi kecuali Anda secara eksplisit menyertakan semua header lain yang menjadi sandarannya.

Mungkin ada kompromi yang masuk akal di sini. Misalnya, Anda dapat memutuskan bahwa memasukkan header perpustakaan standar ke dalam header Anda boleh saja, tetapi tidak lagi.

Dima
sumber
3
Jika Anda meninggalkan termasuk di luar tajuk, maka termasuk pesanan mulai penting ... dan saya pasti ingin menghindarinya.
M. Dudley
1
-1: Menciptakan mimpi buruk ketergantungan. Peningkatan ke xh bisa memerlukan perubahan dalam SETIAP file .cpp yang mencakup xh
kevin cline
1
@ M. Dudley, Seperti yang saya katakan, ada kelebihan dan kekurangan.
Dima