Bagaimana saya bisa mencegah header hell?

45

Kami memulai proyek baru, dari awal. Sekitar delapan pengembang, selusin atau lebih subsistem, masing-masing dengan empat atau lima file sumber.

Apa yang bisa kita lakukan untuk mencegah "sundulan neraka", AKA "sundulan spaghetti"?

  • Satu tajuk per file sumber?
  • Ditambah satu per subsistem?
  • Pisahkan typdefs, stucts & enums dari prototipe fungsi?
  • Pisahkan subsistem internal dari subsistem eksternal?
  • Bersikeras bahwa setiap file tunggal, apakah header atau sumber harus dapat dikompilasi secara mandiri?

Saya tidak meminta cara “terbaik”, cukup tunjukkan apa yang harus diperhatikan dan apa yang dapat menyebabkan kesedihan, sehingga kita dapat mencoba menghindarinya.

Ini akan menjadi proyek C ++, tetapi info C akan membantu pembaca masa depan.

Mawg
sumber
16
Dapatkan salinan Desain Perangkat Lunak C ++ Skala Besar , itu tidak hanya mengajarkan untuk menghindari masalah dengan header, tetapi banyak lagi masalah tentang ketergantungan fisik antara file sumber & objek dalam proyek C ++.
Doc Brown
6
Semua jawaban di sini luar biasa. Saya ingin menambahkan bahwa dokumentasi untuk menggunakan objek, metode, fungsi, harus dalam file header. Saya masih melihat dokumen di file sumber. Jangan buat saya membaca sumbernya. Itulah inti dari file header. Saya tidak perlu membaca sumbernya kecuali saya pelaksana.
Bill Door
1
Saya yakin bahwa saya telah bekerja dengan Anda sebelumnya. Seringkali :-(
Mawg
5
Apa yang Anda gambarkan bukanlah proyek besar. Desain yang baik selalu diterima, tetapi Anda mungkin tidak akan pernah menghadapi masalah "Sistem Skala Besar".
Sam
2
Boost sebenarnya memiliki pendekatan yang mencakup semuanya. Setiap fitur individual memiliki file header sendiri, tetapi setiap modul yang lebih besar juga memiliki header yang mencakup semuanya. Ini ternyata sangat ampuh untuk meminimalkan header-hell tanpa memaksa Anda untuk menyertakan beberapa ratus file setiap kali.
Cort Ammon

Jawaban:

39

Metode sederhana: Satu tajuk per file sumber. Jika Anda memiliki subsistem lengkap di mana pengguna tidak diharapkan tahu tentang file sumber, minta satu header untuk subsistem termasuk semua file header yang diperlukan.

File header apa pun harus dapat dikompilasi sendiri (atau katakanlah file sumber termasuk header tunggal mana pun harus dikompilasi). Sungguh menyakitkan jika saya menemukan file header yang berisi apa yang saya inginkan, dan kemudian saya harus memburu file header lainnya. Cara sederhana untuk menegakkan ini adalah meminta setiap file sumber menyertakan file header-nya terlebih dahulu (terima kasih doug65536, saya pikir saya melakukan itu sebagian besar waktu tanpa menyadarinya).

Pastikan Anda menggunakan alat yang tersedia untuk menjaga waktu kompilasi tetap rendah - setiap header harus disertakan hanya sekali, gunakan header yang dikompilasi sebelumnya untuk menjaga waktu kompilasi tetap rendah, gunakan modul yang telah dikompilasi jika mungkin untuk menjaga waktu kompilasi lebih jauh ke bawah.

gnasher729
sumber
Yang menjadi rumit adalah panggilan fungsi lintas-subsistem, dengan parameter tipe yang dideklarasikan di subsistem lainnya.
Mawg
6
Tricky or not, "#include <subsystem1.h>" harus dikompilasi. Bagaimana Anda mencapainya, terserah Anda. @ Frankpuffer: Mengapa?
gnasher729
13
@Mawg Itu menunjukkan Anda perlu memerlukan subsistem yang terpisah dan dibagi yang mencakup kesamaan dari subsistem yang berbeda, atau Anda perlu header "antarmuka" yang disederhanakan untuk setiap subsistem (yang kemudian digunakan oleh header implementasi, baik internal maupun lintas-sistem) . Jika Anda tidak dapat menulis header antarmuka tanpa menyertakan silang, desain subsistem Anda kacau, dan Anda perlu mendesain ulang hal-hal sehingga subsistem Anda lebih mandiri. (Yang mungkin termasuk mengeluarkan subsistem umum sebagai modul ketiga.)
RM
8
Teknik yang baik untuk memastikan tajuk independen adalah memiliki aturan bahwa file sumber selalu menyertakan tajuknya sendiri terlebih dahulu . Ini akan menangkap kasus di mana Anda perlu memindahkan dependensi termasuk dari file implementasi ke file header.
doug65536
4
@FrankPuffer: Tolong jangan menghapus komentar Anda, terutama jika orang lain menanggapinya, karena itu membuat tanggapannya tanpa konteks. Anda selalu dapat memperbaiki pernyataan Anda dalam komentar baru. Terima kasih! Saya tertarik untuk mengetahui apa yang sebenarnya Anda katakan, tetapi sekarang tidak ada :(
MPW
18

Sejauh ini persyaratan yang paling penting adalah mengurangi ketergantungan antara file sumber Anda. Dalam C ++ itu umum untuk menggunakan satu file sumber dan satu header per kelas. Oleh karena itu, jika Anda memiliki desain kelas yang bagus, Anda bahkan tidak akan mendekati header hell.

Anda juga dapat melihat ini sebaliknya: Jika Anda sudah memiliki header header di proyek Anda, Anda dapat yakin bahwa desain perangkat lunak perlu ditingkatkan.

Untuk menjawab pertanyaan spesifik Anda:

  • Satu tajuk per file sumber? → Ya, ini berfungsi dengan baik dalam banyak kasus dan membuatnya lebih mudah untuk menemukan barang. Tapi jangan menjadikannya agama.
  • Ditambah satu per subsistem? → Tidak, mengapa Anda ingin melakukan ini?
  • Pisahkan typdefs, stucts & enums dari prototipe fungsi? → Tidak, fungsi dan tipe terkait menjadi satu.
  • Pisahkan subsistem internal dari subsistem eksternal? → Ya, tentu saja. Ini akan mengurangi ketergantungan.
  • Bersikeras bahwa setiap file tunggal, apakah header atau sumber dengan standalones kompatibel? → Ya, tidak pernah mengharuskan tajuk apa pun harus dimasukkan sebelum tajuk lainnya.
Frank Puffer
sumber
12

Selain rekomendasi lainnya, sejalan dengan pengurangan ketergantungan (terutama berlaku untuk C ++):

  1. Hanya sertakan apa yang benar-benar Anda butuhkan, di mana Anda membutuhkannya (level serendah mungkin). E. g. jangan sertakan dalam tajuk jika Anda hanya membutuhkan panggilan di sumbernya.
  2. Gunakan deklarasi maju di header sedapat mungkin (header hanya berisi pointer atau referensi ke kelas lain).
  3. Bersihkan termasuk setelah setiap refactoring (komentar mereka, lihat di mana kompilasi gagal, pindahkan di sana, hapus baris menyertakan masih berkomentar).
  4. Jangan kemas terlalu banyak fasilitas umum ke dalam file yang sama; membaginya dengan fungsionalitas (misalnya Logger adalah satu kelas, sehingga satu header dan satu file sumber; SystemHelper dito. dll.).
  5. Tetap berpegang pada prinsip OO, bahkan jika yang Anda dapatkan adalah kelas yang hanya terdiri dari metode statis (bukan fungsi mandiri) - atau gunakan namespace sebagai gantinya .
  6. Untuk fasilitas umum tertentu, pola singleton agak berguna, karena Anda tidak perlu meminta instance dari objek lain yang tidak terkait.
Murphy
sumber
5
Pada # 3, alat ini termasuk apa yang Anda gunakan dapat membantu, menghindari pendekatan tebak-dan-periksa manual.
RM
1
Bisakah Anda menjelaskan apa manfaat dari lajang dalam konteks ini? Saya benar-benar tidak mengerti.
Frank Puffer
@ Frankpuffer Alasan saya adalah ini: Tanpa singleton beberapa instance dari kelas biasanya memiliki instance dari kelas pembantu, seperti seorang Logger. Jika kelas ketiga ingin menggunakannya, ia perlu meminta referensi kelas helper dari pemilik, artinya Anda menggunakan dua kelas, dan tentu saja menyertakan header mereka - bahkan jika pengguna tidak memiliki bisnis dengan pemilik sebaliknya. Dengan singleton Anda hanya perlu menyertakan header dari kelas helper dan dapat meminta instance langsung dari itu. Apakah Anda melihat cacat dalam logika ini?
Murphy
1
# 2 (penerusan deklarasi) dapat membuat perbedaan besar dalam waktu kompilasi dan pengecekan ketergantungan. Seperti yang ditunjukkan oleh jawaban ini ( stackoverflow.com/a/9999752/509928 ), ini berlaku untuk C ++ dan C
Dave Compton
3
Anda dapat menggunakan deklarasi maju ketika melewati nilai juga, asalkan fungsi tersebut tidak didefinisikan inline. Deklarasi fungsi bukan konteks di mana definisi tipe lengkap diperlukan (definisi fungsi atau panggilan fungsi adalah konteks seperti itu).
StoryTeller - Unslander Monica
6

Satu header per file sumber, yang mendefinisikan apa yang mengimplementasikan / ekspor file sumbernya.

Sebanyak mungkin file header, termasuk dalam setiap file sumber (dimulai dengan header sendiri).

Hindari memasukkan (meminimalkan penyertaan) file header dalam file header lainnya (untuk menghindari dependensi melingkar). Untuk detail, lihat jawaban ini untuk "dapatkah dua kelas saling melihat menggunakan C ++?"

Ada seluruh buku tentang hal ini, Desain Perangkat Lunak C ++ Skala Besar oleh Lakos. Ini menggambarkan memiliki "lapisan" perangkat lunak: lapisan tingkat tinggi menggunakan lapisan tingkat yang lebih rendah bukan sebaliknya, yang lagi-lagi menghindari dependensi melingkar.

ChrisW
sumber
4

Saya akan mengklaim pertanyaan Anda pada dasarnya tidak dapat dijawab, karena ada dua jenis header header:

  • Jenis tempat Anda perlu memasukkan sejuta tajuk yang berbeda, dan siapa di neraka yang bahkan dapat mengingat semuanya? Dan memelihara daftar header itu? Ugh.
  • Jenis tempat Anda memasukkan satu hal, dan mengetahui bahwa Anda telah memasukkan seluruh Menara Babel (atau haruskah saya katakan menara Boost? ...)

masalahnya adalah, jika Anda mencoba untuk menghindari yang pertama Anda akhirnya, sampai batas tertentu, dengan yang terakhir, dan sebaliknya.

Ada juga jenis neraka ketiga, yaitu dependensi melingkar. Ini mungkin muncul jika Anda tidak hati-hati ... menghindarinya tidak terlalu rumit, tetapi Anda perlu meluangkan waktu untuk memikirkan bagaimana melakukannya. Lihat John Lakos berbicara tentang Levelisasi di CppCon 2016 (atau hanya slide ).

einpoklum - mengembalikan Monica
sumber
1
Anda tidak selalu dapat menghindari ketergantungan sirkular. Contohnya adalah model di mana entitas merujuk satu sama lain. Setidaknya Anda dapat mencoba membatasi sirkularitas dalam subsistem, ini berarti, jika Anda menyertakan header subsistem, Anda meringkaskan sirkularitas.
nalply
2
@nalply: Maksud saya menghindari ketergantungan melingkar dari tajuk, bukan kode ... jika Anda tidak menghindari ketergantungan tajuk melingkar Anda mungkin tidak akan bisa membangun. Tapi ya, poin diambil, +1.
einpoklum - mengembalikan Monica
1

Decoupling

Ini pada akhirnya tentang decoupling kepada saya pada akhir hari di tingkat desain yang paling mendasar tanpa nuansa karakteristik kompiler dan linker kami. Maksud saya Anda dapat melakukan hal-hal seperti membuat setiap header mendefinisikan hanya satu kelas, menggunakan pimpl, meneruskan deklarasi ke jenis yang hanya perlu dideklarasikan, tidak didefinisikan, bahkan mungkin menggunakan header yang hanya berisi deklarasi maju (mis .:) <iosfwd>, satu header per file sumber , mengatur sistem secara konsisten berdasarkan jenis hal yang dinyatakan / didefinisikan, dll.

Teknik untuk Mengurangi "Ketergantungan Waktu Kompilasi"

Dan beberapa teknik dapat membantu sedikit tetapi Anda dapat menemukan diri Anda melelahkan praktik ini dan masih menemukan file sumber rata-rata di sistem Anda perlu pembukaan dua halaman dari #includearahan untuk melakukan sesuatu yang sedikit bermakna dengan waktu pembuatan yang melambung tinggi jika Anda terlalu fokus mengurangi ketergantungan waktu kompilasi di tingkat tajuk tanpa mengurangi ketergantungan logis pada desain antarmuka Anda, dan sementara itu mungkin tidak dianggap "spageti header" dengan tegas, saya Masih akan mengatakan itu diterjemahkan ke masalah merugikan serupa dengan produktivitas dalam praktek. Pada akhirnya, jika unit kompilasi Anda masih membutuhkan banyak informasi untuk dapat melakukan apa saja, maka itu akan diterjemahkan untuk menambah waktu pembuatan dan mengalikan alasan Anda harus berpotensi kembali dan harus mengubah hal-hal sambil membuat pengembang merasa seperti mereka headbutting sistem hanya mencoba menyelesaikan koding harian mereka. Saya t'

Anda dapat, misalnya, membuat setiap subsistem menyediakan satu file header dan antarmuka yang sangat abstrak. Tetapi jika subsistem tidak dipisahkan satu sama lain, maka Anda mendapatkan sesuatu yang menyerupai spaghetti lagi dengan antarmuka subsistem tergantung pada antarmuka subsistem lainnya dengan grafik dependensi yang terlihat seperti berantakan untuk bekerja.

Teruskan Deklarasi ke Tipe Eksternal

Dari semua teknik yang saya gunakan untuk mendapatkan basis kode sebelumnya yang membutuhkan waktu dua jam untuk membangun sementara para pengembang kadang-kadang menunggu 2 hari untuk giliran mereka di CI pada server build kami (Anda hampir dapat membayangkan mesin-mesin itu sebagai binatang buangan yang kelelahan karena mencoba dengan panik. untuk mengikuti dan gagal saat pengembang mendorong perubahan mereka), yang paling dipertanyakan bagi saya adalah meneruskan menyatakan jenis yang didefinisikan dalam header lain. Dan saya berhasil mendapatkan basis kode itu hingga 40 menit atau lebih setelah bertahun-tahun melakukan ini dalam langkah-langkah tambahan sedikit ketika mencoba untuk mengurangi "header spaghetti", praktik yang paling dipertanyakan di belakang (seperti membuat saya melupakan sifat dasar dari desain sementara tunnel visioned pada saling ketergantungan header) maju menyatakan jenis didefinisikan dalam header lainnya.

Jika Anda membayangkan Foo.hpptajuk yang memiliki sesuatu seperti:

#include "Bar.hpp"

Dan itu hanya menggunakan Bardi header cara yang membutuhkannya deklarasi, bukan definisi. maka mungkin tampak seperti no-brainer untuk menyatakan class Bar;untuk menghindari membuat definisi yang Barterlihat di header. Kecuali dalam praktek sering Anda akan menemukan sebagian besar unit kompilasi yang menggunakan Foo.hppmasih akhirnya perlu Bardidefinisikan pula dengan beban tambahan karena harus memasukkan Bar.hppdiri mereka di atas Foo.hpp, atau Anda mengalami skenario lain di mana itu benar-benar membantu dan 99 % dari unit kompilasi Anda dapat bekerja tanpa termasuk Bar.hpp, kecuali kemudian menimbulkan pertanyaan desain yang lebih mendasar (atau setidaknya saya pikir seharusnya saat ini) mengapa mereka perlu melihat deklarasi Bardan mengapaFoo bahkan perlu repot untuk mengetahuinya jika itu tidak relevan dengan kebanyakan kasus penggunaan (mengapa membebani desain dengan dependensi ke yang lain yang hampir tidak pernah digunakan?).

Karena secara konseptual kita belum benar-benar dipisahkan Foodari Bar. Kami baru saja membuatnya sehingga tajuk Footidak perlu banyak info tentang tajuk Bar, dan itu tidak hampir sama pentingnya dengan desain yang benar-benar membuat keduanya sepenuhnya independen satu sama lain.

Script Tertanam

Ini benar-benar untuk basis kode skala yang lebih besar tetapi teknik lain yang saya temukan sangat berguna adalah dengan menggunakan bahasa skrip tertanam untuk setidaknya bagian paling tinggi dari sistem Anda. Saya menemukan saya dapat menanamkan Lua dalam satu hari dan secara seragam dapat memanggil semua perintah di sistem kami (untungnya perintah itu abstrak, untungnya). Sayangnya saya menemui hambatan di mana para devs tidak mempercayai pengenalan bahasa lain dan, mungkin yang paling aneh, dengan kinerja sebagai kecurigaan terbesar mereka. Namun sementara saya dapat memahami masalah lain, kinerja seharusnya bukan masalah jika kita hanya menggunakan skrip untuk menjalankan perintah ketika pengguna mengklik tombol, misalnya, yang tidak melakukan loop sendiri yang berat (apa yang kami coba lakukan, khawatir tentang perbedaan nanodetik dalam waktu respons untuk klik tombol?).

Contoh

Sementara itu cara paling efektif yang pernah saya saksikan setelah teknik melelahkan untuk mengurangi waktu kompilasi dalam basis kode besar adalah arsitektur yang benar-benar mengurangi jumlah informasi yang diperlukan untuk satu hal dalam sistem untuk bekerja, tidak hanya memisahkan satu header dari yang lain dari kompiler. perspektif tetapi mengharuskan pengguna antarmuka ini untuk melakukan apa yang perlu mereka lakukan sambil mengetahui (baik dari sudut pandang manusia dan kompiler, benar decoupling yang melampaui dependensi kompiler) minimal.

ECS hanyalah salah satu contoh (dan saya tidak menyarankan Anda menggunakan salah satunya), tetapi dengan melihatnya menunjukkan kepada saya bahwa Anda dapat memiliki beberapa basis kode yang benar-benar epik yang masih dibangun dengan sangat cepat sambil dengan senang hati menggunakan templat dan banyak barang lainnya karena ECS, oleh alam, menciptakan arsitektur yang sangat terpisah dimana sistem hanya perlu tahu tentang database ECS dan biasanya hanya segelintir jenis komponen (kadang-kadang hanya satu) untuk melakukan hal mereka:

masukkan deskripsi gambar di sini

Desain, Desain, Desain

Dan jenis-jenis desain arsitektur yang terpisah pada tingkat manusia, konseptual ini lebih efektif dalam hal meminimalkan waktu kompilasi daripada teknik apa pun yang saya eksplorasi di atas ketika basis kode Anda tumbuh dan tumbuh dan tumbuh, karena pertumbuhan itu tidak sesuai dengan rata-rata Anda unit kompilasi mengalikan informasi jumlah yang diperlukan pada saat kompilasi dan waktu tautan untuk bekerja (sistem apa pun yang mengharuskan pengembang rata-rata Anda untuk memasukkan banyak hal untuk melakukan apa pun juga mengharuskan mereka dan bukan hanya kompiler untuk mengetahui banyak informasi untuk melakukan apa pun ). Ini juga memiliki lebih banyak manfaat daripada mengurangi waktu pembuatan dan mengurai tajuk, karena itu juga berarti pengembang Anda tidak perlu tahu banyak tentang sistem di luar apa yang diperlukan segera untuk melakukan sesuatu dengannya.

Jika, misalnya, Anda dapat menyewa pengembang fisika ahli untuk mengembangkan mesin fisika untuk game AAA Anda yang menjangkau jutaan LOC, dan ia dapat memulai dengan sangat cepat sambil mengetahui informasi minimum mutlak yang absolut sejauh hal-hal seperti jenis dan antarmuka yang tersedia serta konsep sistem Anda, maka itu akan secara alami diterjemahkan ke mengurangi jumlah informasi untuknya dan kompiler yang diperlukan untuk membangun mesin fisika, dan juga menerjemahkan ke pengurangan besar dalam waktu membangun sementara umumnya menyiratkan bahwa tidak ada yang menyerupai spaghetti di mana saja dalam sistem. Dan itulah yang saya sarankan untuk memprioritaskan di atas semua teknik lain ini: bagaimana Anda merancang sistem Anda. Melelahkan teknik lain akan menjadi icing di atas jika Anda melakukannya sementara, jika tidak,

Energi Naga
sumber
1
Jawaban yang sangat bagus! Althoguh saya harus menggali sedikit untuk menemukan apa jerawat :-)
Mawg
0

Ini masalah pendapat. Lihat jawaban ini dan yang itu . Dan itu juga sangat tergantung pada ukuran proyek (jika Anda yakin Anda akan memiliki jutaan baris sumber dalam proyek Anda, itu tidak sama dengan memiliki beberapa lusin ribu dari mereka).

Berbeda dengan jawaban lain, saya merekomendasikan satu header publik (agak besar) per sub-sistem (yang dapat menyertakan header "pribadi", mungkin memiliki file terpisah untuk implementasi banyak fungsi yang digarisbawahi). Anda bahkan dapat mempertimbangkan tajuk yang hanya memiliki beberapa #include arahan.

Saya tidak berpikir bahwa banyak file header direkomendasikan. Secara khusus, saya tidak merekomendasikan satu file header per kelas, atau banyak file header kecil masing-masing beberapa lusin baris.

(Jika Anda memiliki banyak file kecil yang besar, Anda harus memasukkan banyak file di setiap unit terjemahan kecil , dan keseluruhan waktu pembangunan mungkin akan berkurang)

Yang Anda inginkan adalah mengidentifikasi, untuk setiap sub-sistem dan file, pengembang utama yang bertanggung jawab untuknya.

Akhirnya, untuk proyek kecil (mis. Kurang dari seratus ribu baris kode sumber), itu tidak terlalu penting. Selama proyek Anda akan cukup mudah untuk melakukan refactor kode dan mengatur ulang dalam file yang berbeda. Anda hanya akan menyalin & menempelkan potongan kode ke file baru (header), bukan masalah besar (yang lebih sulit adalah merancang dengan bijak bagaimana Anda akan mengatur ulang file Anda, dan itu adalah proyek khusus).

(preferensi pribadi saya adalah untuk menghindari file yang terlalu besar dan terlalu kecil; Saya sering memiliki file sumber masing-masing beberapa ribu baris; dan saya tidak takut dengan file header -termasuk definisi fungsi yang diuraikan- dari ratusan baris atau bahkan beberapa ribuan dari mereka)

Perhatikan bahwa jika Anda ingin menggunakan header yang telah dikompilasi sebelumnya dengan GCC (yang kadang - kadang merupakan pendekatan yang masuk akal untuk menurunkan waktu kompilasi) Anda memerlukan file header tunggal (termasuk semua yang lainnya, dan header sistem juga).

Perhatikan bahwa di C ++, file header standar menarik banyak kode . Sebagai contoh #include <vector>adalah menarik lebih dari sepuluh ribu baris pada GCC 6 saya di Linux (18100 baris). Dan #include <map> berkembang hingga hampir 40KLOC. Oleh karena itu, jika Anda memiliki banyak file header kecil termasuk header standar, Anda akhirnya menguraikan ribuan baris selama pembuatan, dan waktu kompilasi Anda akan terganggu. Inilah sebabnya saya tidak suka memiliki banyak baris sumber C ++ kecil (dari beberapa ratus baris paling banyak), tetapi lebih suka memiliki lebih sedikit tetapi lebih besar file C ++ (dari beberapa ribu baris).

(sehingga memiliki ratusan file C ++ kecil yang selalu menyertakan -bahkan secara tidak langsung- beberapa file header standar memberi waktu pembuatan yang besar, yang mengganggu pengembang)

Dalam kode C, cukup sering header file meluas ke sesuatu yang lebih kecil, sehingga pertukarannya berbeda.

Lihat juga, untuk inspirasi, ke dalam praktik sebelumnya dalam proyek perangkat lunak gratis yang ada (misalnya di github ).

Perhatikan bahwa dependensi dapat ditangani dengan sistem otomasi build yang baik . Pelajari dokumentasi pembuatan GNU . Waspadai berbagai -Mflag preprocessor ke GCC (berguna untuk menghasilkan dependensi secara otomatis).

Dengan kata lain, proyek Anda (dengan kurang dari seratus file dan selusin pengembang) mungkin tidak cukup besar untuk benar-benar prihatin dengan "header hell", sehingga kekhawatiran Anda tidak dibenarkan . Anda hanya dapat memiliki selusin file header (atau bahkan lebih sedikit), Anda dapat memilih untuk memiliki satu file header per unit terjemahan, Anda bahkan dapat memilih untuk memiliki satu file header tunggal, dan apa pun yang Anda pilih untuk dilakukan tidak akan menjadi "header hell" (dan refactoring dan reorganisasi file Anda akan tetap cukup mudah, sehingga pilihan awal tidak terlalu penting ).

(Jangan memfokuskan upaya Anda pada "sundulan neraka" - yang bukan masalah bagi Anda -, tetapi fokuskanlah mereka untuk merancang arsitektur yang baik)

Basile Starynkevitch
sumber
Teknis yang Anda sebutkan mungkin benar. Namun, seperti yang saya pahami, OP meminta petunjuk bagaimana meningkatkan pemeliharaan kode dan organisasi, bukan waktu kompilasi. Dan saya melihat konflik langsung antara dua tujuan ini.
Murphy
Tapi itu masih masalah pendapat. Dan OP tampaknya memulai proyek yang tidak terlalu besar.
Basile Starynkevitch