Seberapa jauh untuk mengetik dengan tipe primitif seperti int

14

Saya telah melihat kode C ++ seperti berikut dengan banyak typedefs.

Apa manfaat menggunakan banyak typedefs seperti ini dibandingkan dengan menggunakan primitif C ++? Apakah ada pendekatan lain yang mungkin juga mencapai manfaat itu?

Pada akhirnya, semua data disimpan dalam memori atau dikirim melalui kabel sebagai bit dan byte, apakah itu penting?

types.h:

typedef int16_t Version;
typedef int32_t PacketLength;
typedef int32_t Identity;
typedef int32_t CabinetNumber;
typedef int64_t Time64;
typedef int64_t RFID;
typedef int64_t NetworkAddress;
typedef int64_t PathfinderAddress;
typedef int16_t PathfinderPan;
typedef int16_t PathfinderChannel;
typedef int64_t HandsetSerialNumber;
typedef int16_t PinNumber;
typedef int16_t LoggingInterval;
typedef int16_t DelayMinutes;
typedef int16_t ReminderDelayMinutes;
typedef int16_t EscalationDelayMinutes;
typedef float CalibrationOffset;
typedef float AnalogValue;
typedef int8_t PathfinderEtrx;
typedef int8_t DampingFactor;
typedef int8_t RankNumber;
typedef int8_t SlavePort;
typedef int8_t EventLevel;
typedef int8_t Percent;
typedef int8_t SensorNumber;
typedef int8_t RoleCode;
typedef int8_t Hour;
typedef int8_t Minute;
typedef int8_t Second;
typedef int8_t Day;
typedef int8_t Month;
typedef int16_t Year;
typedef int8_t EscalationLevel;

Tampaknya logis untuk mencoba dan memastikan tipe yang sama selalu digunakan untuk hal tertentu untuk menghindari kelebihan, tetapi saya sering melihat kode di mana "int" baru saja digunakan di mana-mana. The typedefing sering tidak menyebabkan kode yang terlihat sedikit seperti ini meskipun:

DoSomething(EscalationLevel escalationLevel) {
    ...
}

Yang kemudian membuat saya bertanya-tanya token mana yang sebenarnya menggambarkan parameter: tipe parameter atau nama parameter?

Menandai
sumber
2
IMHO, sepertinya latihan yang cukup sia-sia, tapi saya yakin beberapa yang lain tidak akan setuju ...
Nim
1
Jenis-jenis itu terlihat seperti nama variabel.
Kapten Giraffe
11
Perhatikan bahwa ini menciptakan kesan bahwa itu adalah tipe-aman, tetapi tidak sama sekali - typedefs hanya membuat alias, tetapi tidak ada yang menghentikan Anda dari melewatinya misalnya Minuteke fungsi yang argumennya dideklarasikan sebagai tipe Second.
Jesper
2
@ Mark: lihat dengan cara lain. Jika Anda membuat kesalahan dalam menentukan tipe integer, atau persyaratan baru muncul di masa depan, dan Anda ingin mengubahnya, apakah Anda ingin mengubah satu typedef atau Anda ingin mencari kode untuk setiap fungsi yang memanipulasi setahun, dan ubah tanda tangannya? 640k sudah cukup untuk siapa saja, dan semua itu. Kelemahan yang sesuai dengan typedef adalah bahwa orang-orang secara tidak sengaja atau sengaja menulis kode yang bergantung pada fakta bahwa Year adalah tepat 16 bit, kemudian ia berubah dan kode mereka rusak.
Steve Jessop
1
@Steve Jessop: Saya tidak bisa memutuskan apakah Anda pikir itu ide yang baik atau buruk :-) Bagian pertama tampaknya mendukung, yang terakhir menentang. Saya kira itu pro dan kontra.

Jawaban:

13

Nama parameter harus menggambarkan apa artinya - dalam kasus Anda tingkat eskalasi. Jenisnya adalah bagaimana nilainya diwakili - menambahkan typedefs seperti dalam contoh Anda mengaburkan bagian dari tanda tangan fungsi ini, jadi saya tidak akan merekomendasikannya.

Typedef berguna untuk templat, atau jika Anda ingin mengubah jenis yang digunakan untuk parameter tertentu, misalnya ketika bermigrasi dari platform 32bit ke 64bit.

Björn Pollex
sumber
Ini tampaknya menjadi konsensus umum saat itu. Jadi apakah Anda umumnya hanya akan tetap pada "int"? Saya harus sangat efisien dalam aplikasi ini dalam hal mentransfer data, jadi apakah ini hanya konversi ke int16_t (atau apa pun tipe representasi terbesar yang diperlukan untuk elemen tertentu) selama serialisasi?
@ Mark: Ya, itulah yang harus Anda lakukan. Gunakan typedef untuk mengekspresikan ukuran tipe data yang digunakan, tetapi jangan membedakan tipe yang sama yang digunakan dalam konteks yang berbeda.
Björn Pollex
Terima kasih - dan hanya untuk memperjelas, Anda tidak akan repot menggunakan int8_t daripada int umumnya dalam kode .. Saya kira kekhawatiran utama saya adalah sesuatu seperti "Identity" yang sebenarnya adalah identitas yang dihasilkan oleh database. Saat ini 32bit tapi saya belum yakin apakah itu akhirnya menjadi 64bit. Juga, bagaimana dengan int32_t vs int? int biasanya sama dengan int32_t, tetapi mungkin tidak selalu saya kira pada platform yang berbeda? Saya pikir saya harus tetap berpegang pada "int" secara umum, dan "int64_t" di mana perlu .. terima kasih :-)
@ Mark: Hal penting tentang typedefs seperti int32_tadalah Anda harus memastikan semuanya benar ketika mengkompilasi pada platform yang berbeda. Jika Anda mengharapkan rentang Identityuntuk berubah di beberapa titik, saya pikir saya lebih suka untuk membuat perubahan secara langsung di semua kode yang terpengaruh. Tetapi saya tidak yakin, karena saya perlu tahu lebih banyak tentang desain spesifik Anda. Anda mungkin ingin menjadikan itu pertanyaan terpisah.
Björn Pollex
17

Pada awalnya saya berpikir "Mengapa tidak" tetapi kemudian terpikir oleh saya bahwa jika Anda akan berusaha keras untuk memisahkan jenis-jenis seperti itu, maka manfaatkanlah bahasa dengan lebih baik. Alih-alih menggunakan alias, sebenarnya tentukan jenis:

class AnalogueValue
{
public:
    // constructors, setters, getters, etc..
private:
    float m_value;
};

Tidak ada perbedaan kinerja antara:

typedef float AnalogueValue;
AnalogValue a = 3.0f;
CallSomeFunction (a);

dan:

AnalogValue a (3.0f); // class version
CallSomeFunction (a);

dan Anda juga memiliki kelebihan menambahkan validasi parameter dan keamanan tipe. Misalnya, pertimbangkan kode yang berhubungan dengan uang menggunakan tipe primitif:

float amount = 10.00;
CallSomeFunction(amount);

Selain masalah pembulatan, ini juga memungkinkan semua jenis yang dapat dikonversi menjadi pelampung:

int amount = 10;
CallSomeFunction(amount);

Dalam hal ini ini bukan masalah besar, tetapi konversi implisit dapat menjadi sumber bug yang sulit untuk dijabarkan. Menggunakan a typedeftidak membantu di sini, karena mereka hanyalah tipe alias.

Menggunakan tipe baru sepenuhnya berarti tidak ada konversi implisit kecuali jika Anda memberi kode pada operator casting, yang merupakan ide buruk secara khusus karena memungkinkan konversi implisit. Anda juga dapat merangkum data tambahan:

class Money {
  Decimal amount;
  Currency currency;
};

Money m(Decimal("10.00"), Currency.USD);
CallSomeFunction(m);

Tidak ada hal lain yang cocok dengan fungsi itu kecuali kita menulis kode untuk mewujudkannya. Konversi yang tidak disengaja tidak mungkin. Kita juga dapat menulis tipe yang lebih kompleks sesuai kebutuhan tanpa banyak kesulitan.

Mendesis
sumber
1
Anda bahkan dapat menulis beberapa makro mengerikan untuk melakukan semua kreasi kelas ini untuk Anda. (Ayo, Skizz. Kamu tahu kamu mau.)
1
Untuk beberapa di antaranya, pustaka unit tipe-aman mungkin bermanfaat ( tuoml.sourceforge.net/html/scalar/scalar.html ), daripada menulis kelas khusus untuk masing-masingnya.
Steve Jessop
@ Chris - Jelas bukan makro, tetapi mungkin kelas templat. Seperti yang ditunjukkan Steve, kelas-kelas itu sudah ditulis.
kevin cline
4
@ Chris: Makro BOOST_STRONG_TYPEDEFsebenarnya disebut ;)
Matthieu M.
3

Menggunakan typedef untuk tipe primitif seperti itu lebih mirip kode gaya C.

Di C ++ Anda akan mendapatkan kesalahan yang menarik segera setelah Anda mencoba untuk membebani fungsi untuk, katakanlah, EventLeveldan Hour. Itu membuat nama tipe ekstra tidak berguna.

Bo Persson
sumber
2

Kami (di perusahaan kami) banyak melakukannya di C ++. Ini membantu memahami dan memelihara kode. Yang bagus saat memindahkan orang antar tim atau melakukan refactoring. Contoh:

typedef float Price;
typedef int64_t JavaTimestmap;

void f(JavaTimestamp begin, JavaTimestamp end, Price income);

Kami percaya ini adalah praktik yang baik untuk membuat typedef ke nama dimensi dari tipe representasi. Nama baru ini mewakili peran umum dalam suatu perangkat lunak. Nama parameter adalah peran lokal . Seperti di User sender, User receiver. Di beberapa tempat itu bisa jadi berlebihan, seperti void register(User user), tapi saya tidak menganggapnya sebagai masalah.

Kemudian orang mungkin memiliki gagasan yang floatbukan yang terbaik untuk mewakili harga karena aturan pembulatan khusus pemesanan, jadi seseorang mengunduh atau mengimplementasikan tipe BCDFloat(desimal berkode biner) dan mengubah typedef. Tidak ada pencarian dan mengganti bekerja dari floatke BCDFloatmana akan mengeras oleh fakta bahwa ada lebih mungkin banyak mengapung dalam kode Anda.

Ini bukan peluru perak dan memiliki peringatan sendiri, tetapi kami pikir itu jauh lebih baik menggunakannya daripada tidak.

Tidak dalam daftar
sumber
Atau seperti yang disarankan Mathieu M. di pos Skizz orang bisa suka BOOST_STRONG_TYPEDEF(float, Price), tapi saya tidak akan sejauh itu pada proyek rata-rata. Atau mungkin saya akan melakukannya. Saya harus tidur di situ. :-)
Notinlist
1

typedefpada dasarnya memungkinkan Anda untuk memberikan alias untuk type.
Ini memberi Anda fleksibilitas untuk menghindari mengetik type nameslagi dan lagi dan membuat Anda typelebih mudah dibaca dimana nama alias menunjukkan maksud atau tujuan dari type.

Ini lebih merupakan masalah pilihan jika Anda ingin memiliki lebih banyak nama yang dapat dibaca melalui typedefproyek Anda.
Biasanya, saya menghindari penggunaan typedefpada tipe primitif, kecuali mereka terlalu panjang untuk diketik. Saya menjaga agar nama parameter saya lebih bersifat indikatif.

Alok Simpan
sumber
0

Saya tidak akan pernah melakukan hal seperti ini. Memastikan mereka semua memiliki ukuran yang sama adalah satu hal - tetapi Anda hanya perlu menyebutnya sebagai tipe yang tidak terpisahkan.

DeadMG
sumber
0

Menggunakan typedef seperti ini tidak masalah selama siapa pun yang menggunakannya tidak perlu tahu apa-apa tentang representasi yang mendasarinya . Misalnya, jika Anda ingin meneruskan PacketLengthobjek ke salah satu printfatau scanf, Anda harus tahu jenis sebenarnya sehingga Anda dapat memilih specifier konversi yang tepat. Dalam kasus seperti itu, typedef hanya menambah tingkat kebingungan tanpa membeli imbalan apa pun; Anda mungkin baru saja mendefinisikan objek sebagai int32_t.

Jika Anda perlu menerapkan semantik khusus untuk setiap jenis (seperti rentang atau nilai yang diizinkan), maka Anda lebih baik membuat tipe data abstrak dan fungsi untuk beroperasi pada tipe itu, daripada hanya membuat typedef.

John Bode
sumber