Saya ingin membandingkan struct dengan cara yang umum dan saya telah melakukan sesuatu seperti ini (saya tidak dapat membagikan sumber yang sebenarnya, jadi tanyakan lebih detail jika perlu):
template<typename Data>
bool structCmp(Data data1, Data data2)
{
void* dataStart1 = (std::uint8_t*)&data1;
void* dataStart2 = (std::uint8_t*)&data2;
return memcmp(dataStart1, dataStart2, sizeof(Data)) == 0;
}
Ini sebagian besar berfungsi sebagaimana mestinya, kecuali kadang-kadang mengembalikan false meskipun dua contoh inst memiliki anggota yang identik (saya sudah memeriksa dengan eclipse debugger). Setelah beberapa pencarian saya menemukan bahwa memcmp
bisa gagal karena struct yang digunakan sedang diisi.
Apakah ada cara yang lebih tepat untuk membandingkan memori yang acuh tak acuh dengan bantalan? Saya tidak dapat memodifikasi struct yang digunakan (mereka adalah bagian dari API yang saya gunakan) dan banyak struct yang berbeda digunakan memiliki beberapa anggota yang berbeda dan dengan demikian tidak dapat dibandingkan secara individual dengan cara yang umum (setahu saya).
Sunting: Saya sayangnya terjebak dengan C ++ 11. Seharusnya menyebutkan ini sebelumnya ...
memcmp
Anda memasukkan bit padding dalam perbandingan Anda.==
operator. Menggunakanmemcmp
tidak dapat diandalkan, dan cepat atau lambat Anda akan berhadapan dengan beberapa kelas yang harus "melakukannya sedikit berbeda dari yang lain." Sangat bersih dan efisien untuk mengimplementasikannya pada operator. Perilaku aktual akan menjadi polimorfik tetapi kode sumbernya akan bersih ... dan, jelas.Jawaban:
Tidak,
memcmp
tidak cocok untuk melakukan ini. Dan refleksi dalam C ++ tidak cukup untuk melakukan ini pada saat ini (akan ada kompiler eksperimental yang mendukung refleksi yang cukup kuat untuk melakukan ini, dan c ++ 23 mungkin memiliki fitur yang Anda butuhkan).Tanpa refleksi bawaan, cara termudah untuk menyelesaikan masalah Anda adalah dengan melakukan refleksi manual.
Ambil ini:
kami ingin melakukan jumlah pekerjaan minimal sehingga kami dapat membandingkan dua di antaranya.
Jika kita memiliki:
atau
untuk c ++ 11 , maka:
melakukan pekerjaan yang cukup baik.
Kita dapat memperluas proses ini menjadi rekursif dengan sedikit pekerjaan; alih-alih membandingkan ikatan, bandingkan setiap elemen yang dibungkus dengan templat, dan templat itu
operator==
secara rekursif menerapkan aturan ini (membungkus elemenas_tie
untuk membandingkan) kecuali elemen tersebut sudah memiliki kerja==
, dan menangani array.Ini akan membutuhkan sedikit perpustakaan (100ish baris kode?) Bersama dengan menulis sedikit data "refleksi" manual per anggota. Jika jumlah struct yang Anda miliki terbatas, mungkin lebih mudah untuk menulis kode per-struct secara manual.
Mungkin ada cara untuk mendapatkannya
untuk menghasilkan
as_tie
struktur menggunakan macro yang mengerikan. Tetapias_tie
cukup sederhana. Dalam c ++ 11 pengulangan itu menjengkelkan; ini berguna:dalam situasi ini dan banyak lainnya. Dengan
RETURNS
, menulisas_tie
adalah:menghapus pengulangan.
Berikut ini adalah cara membuatnya rekursif:
c ++ 17 refl_tie (array) (sepenuhnya rekursif, bahkan mendukung array-of-array):
Contoh langsung .
Di sini saya menggunakan
std::array
darirefl_tie
. Ini jauh lebih cepat daripada tuple refl_tie saya sebelumnya pada waktu kompilasi.Juga
menggunakan di
std::cref
sini bukannyastd::tie
bisa menghemat waktu kompilasi, seperticref
kelas yang jauh lebih sederhana daripadatuple
.Akhirnya, Anda harus menambahkan
yang akan mencegah anggota array dari peluruhan ke pointer dan jatuh kembali pada pointer-kesetaraan (yang Anda mungkin tidak inginkan dari array).
Tanpa ini, jika Anda meneruskan array ke struct yang tidak direfleksikan, ia jatuh kembali ke pointer ke struct yang tidak direfleksikan
refl_tie
, yang berfungsi dan mengembalikan omong kosong.Dengan ini, Anda berakhir dengan kesalahan waktu kompilasi.
Dukungan untuk rekursi melalui tipe perpustakaan cukup rumit. Anda dapat
std::tie
melakukannya:tetapi itu tidak mendukung rekursi melalui itu.
sumber
as_tie
. Mulai dari C ++ 14 ini disimpulkan secara otomatis. Anda dapat menggunakanauto as_tie (some_struct const & s) -> decltype(std::tie(s.x, s.d1, s.d2, s.c));
di C ++ 11. Atau secara eksplisit nyatakan tipe pengembalian.as_tie
dukungan, secara otomatis bekerja) dan anggota array dukungan tidak dirinci, tetapi mungkin.inline
kunci seharusnya membuat kesalahan definisi banyak hilang. Gunakan tombol [tanyakan] setelah Anda mendapatkan contoh minimal yang dapat direproduksiAnda benar bahwa padding menghalangi cara Anda membandingkan tipe arbitrer dengan cara ini.
Ada beberapa langkah yang dapat Anda ambil:
Data
maka misalnya gcc__attribute__((packed))
. Ini berdampak pada kinerja, tetapi mungkin patut untuk dicoba. Meskipun, saya harus mengakui bahwa saya tidak tahu apakahpacked
memungkinkan Anda untuk melarang padding sepenuhnya. Gcc doc mengatakan:Data
maka setidaknyastd::has_unique_object_representations<T>
dapat memberi tahu Anda jika perbandingan Anda akan menghasilkan hasil yang benar:dan selanjutnya:
PS: Saya hanya membahas padding, tapi jangan lupa bahwa tipe yang dapat membandingkan sama untuk contoh dengan representasi berbeda dalam memori sama sekali tidak jarang (misalnya
std::string
,std::vector
dan banyak lainnya).sumber
memcmp
pada struct tanpa bantalan danoperator==
hanya menerapkan bila diperlukan.Singkatnya: Tidak mungkin secara umum.
Masalahnya
memcmp
adalah padding mungkin berisi data yang berubah-ubah dan karenanyamemcmp
mungkin gagal. Jika ada cara untuk mencari tahu di mana padding berada, Anda bisa membidik bit-bit itu dan kemudian membandingkan representasi data, yang akan memeriksa kesetaraan jika anggota sebanding secara sepele (yang bukan kasus yaitu untukstd::string
karena dua string dapat berisi pointer yang berbeda, tetapi dua array char-runcing sama). Tapi saya tahu tidak ada cara untuk mendapatkan pad dari struct. Anda dapat mencoba untuk memberitahu kompiler Anda untuk mengemas struct, tetapi ini akan membuat akses lebih lambat dan tidak benar-benar dijamin untuk bekerja.Cara terbersih untuk mengimplementasikan ini adalah membandingkan semua anggota. Tentu saja ini tidak benar-benar mungkin dengan cara yang umum (sampai kita mendapatkan refleksi waktu kompilasi dan kelas meta di C ++ 23 atau lebih baru). Dari C ++ 20 dan seterusnya, seseorang dapat menghasilkan default
operator<=>
tapi saya pikir ini juga hanya mungkin sebagai fungsi anggota jadi, sekali lagi ini tidak benar-benar berlaku. Jika Anda beruntung dan semua struct yang ingin Anda bandingkan telahoperator==
ditentukan, Anda tentu saja dapat menggunakannya. Tapi itu tidak dijamin.EDIT: Ok, sebenarnya ada cara yang benar-benar rumit dan agak umum untuk agregat. (Saya hanya menulis konversi ke tuple, yang memiliki operator perbandingan default). godbolt
sumber
C ++ 20 mendukung comaparisons default
sumber
==
atau<=>
operator hanya dapat dilakukan di lingkup kelas.Dengan asumsi data POD, operator penugasan standar hanya menyalin byte anggota. (sebenarnya tidak 100% yakin tentang itu, jangan mengambil kata saya untuk itu)
Anda dapat menggunakan ini untuk keuntungan Anda:
sumber
Saya percaya Anda mungkin dapat mendasarkan solusi pada voodoo Antony Polukhin yang luar biasa licik di
magic_get
perpustakaan - untuk struct, bukan untuk kelas yang kompleks.Dengan pustaka itu, kita dapat mengulangi bidang-bidang yang berbeda dari sebuah struct, dengan tipe yang sesuai, dalam kode yang murni umum. Antony telah menggunakan ini, misalnya, untuk dapat melakukan streaming struct sewenang-wenang ke aliran output dengan jenis yang benar, sepenuhnya umum. Masuk akal bahwa perbandingan mungkin juga merupakan aplikasi yang mungkin dari pendekatan ini.
... tetapi Anda membutuhkan C ++ 14. Setidaknya itu lebih baik daripada C ++ 17 dan saran selanjutnya dalam jawaban lain :-P
sumber