intのようなプリミティブ型をtypedefで処理する方法


14

私は次のようなC ++コードをたくさん見ましたtypedef

typedefC ++プリミティブを使用するのとは対照的に、このような多くのを使用する利点は何ですか?これらの利点も達成できる別のアプローチはありますか?

最終的に、データはすべてメモリに保存されるか、ビットとバイトとしてワイヤを介して送信されますが、本当に重要ですか?

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;

オーバーフローを回避するために特定の事柄に常に同じ型が使用されるようにすることは論理的に思えますが、代わりに「int」がほとんどどこでも使用されているコードをよく見かけます。typedefINGのは、しばしばかかわらビット、次のようになり、そのコードにリードを行います。

DoSomething(EscalationLevel escalationLevel) {
    ...
}

それで、どのトークンが実際にパラメーターを記述しているのか、つまりパラメーターのタイプまたはパラメーター名を疑問に思いますか?


2
私見では、かなり無意味な運動のように思えるが、私は確かにいくつか他の人が...そうは思わないよ
ニム

1
これらの型は変数名のように見えます。
キャプテンキリン

11
これはタイプセーフであるという印象を与えますが、まったくそうではないことに注意してください-typedefはエイリアスを作成するだけですが、たとえばMinutetypeとして宣言された引数を持つ関数に渡すことを妨げるものは何もありませんSecond
ジェスパー

2
@マーク:別の方法で見てください。整数型の決定を間違えた場合、または将来新しい要件が出現し、それを変更する場合は、1つのtypedefを変更するか、年を操作するすべての関数のコードを検索しますか?署名を変更しますか?誰でも640kで十分です。typedefの対応する欠点は、人々が偶然または故意に、Yearが正確に16ビットであるという事実に依存するコードを書くと、コードが変更されてコードが壊れることです。
スティーブジェソップ

1
@Steve Jessop:良いアイデアか悪いアイデアかを判断することはできません:-)前者は好意的で、後者は賛成のようです。賛否両論があると思います。

回答:


13

パラメータの名前は、それが何を意味するのかを説明する必要があります-あなたの場合、エスカレーションレベル。タイプは値の表現方法です。例のようにtypedefを追加すると、関数署名のこの部分が難読化されるため、お勧めしません。

Typedefは、テンプレート、または特定のパラメーターに使用される型を変更する場合、たとえば32ビットから64ビットプラットフォームに移行する場合に便利です。


これが当時の一般的なコンセンサスのようです。それで、あなたは一般に「int」に固執しますか?データの転送に関しては、このアプリで非常に効率的である必要があるため、シリアル化中にint16_t(または特定の要素に必要な最大の表現型)に変換するだけの場合でしょうか?

@マーク:はい、それがあなたのやり方です。typedefを使用して、使用されるデータ型のサイズを表現しますが、異なるコンテキストで使用される同じ型を区別しないでください。
ビョルンポレックス

おかげで-明確にするために、一般的にコードでintの代わりにint8_tを使用することはありません。現時点では32ビットですが、最終的に64ビットになるかどうかはまだわかりません。また、int32_tとintはどうですか?intは通常 int32_tと同じですが、別のプラットフォームで常に推測されるとは限りませんか?一般に「int」、必要な場合は「int64_t」に固執する必要があると考えています。ありがとう:

@Mark:typedefのような重要なことint32_tは、異なるプラットフォームでコンパイルするときにそれらが正しいことを確認する必要があるということです。Identityある時点で範囲が変わると予想される場合は、影響を受けるすべてのコードに直接変更を加えたいと思います。しかし、あなたの特定のデザインについてもっと知る必要があるので、私は確信していません。それを別の質問にしたいかもしれません。
ビョルンポレックス

17

最初は「なぜ」と思っていましたが、そのような型を分離するためにそのような長さまで行こうとすると、言語をよりよく利用することになりました。エイリアスを使用する代わりに、実際にタイプを定義します。

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

次の間にパフォーマンスの違いはありません。

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

そして:

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

また、パラメーターの検証と型の安全性を追加できるという利点もあります。たとえば、プリミティブ型を使用してお金を処理するコードを考えます。

float amount = 10.00;
CallSomeFunction(amount);

丸めの問題とは別に、フロートに変換できる任意の型も許可します。

int amount = 10;
CallSomeFunction(amount);

この場合、大したことではありませんが、暗黙的な変換はバグを突き止めるのが難しいバグの原因になる可能性があります。a typedefは型エイリアスに過ぎないため、ここでは役立ちません。

新しい型を完全に使用するということは、キャスト演算子をコーディングしない限り、暗黙的な変換がないことを意味します。これは、暗黙的な変換を許可するため、特に悪い考えです。追加データをカプセル化することもできます:

class Money {
  Decimal amount;
  Currency currency;
};

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

それを実現するためのコードを書かない限り、他の何もその関数に適合しません。偶発的な変換は不可能です。必要に応じて、面倒なことなくより複雑な型を書くこともできます。


1
このクラス作成のすべてを行う恐ろしいマクロを書くことさえできます。(、Skizzさああなたがしたい知っている。。)

1
これらの中には、それぞれにカスタムクラスを記述するよりも、タイプセーフなユニットライブラリ(tuoml.sourceforge.net/html/scalar/scalar.html)が役立つ場合があります。
スティーブジェソップ

@Chris-間違いなくマクロではなく、おそらくテンプレートクラスです。スティーブが指摘するように、これらのクラスはすでに作成されています。
ケビンクライン

4
@クリス:マクロはBOOST_STRONG_TYPEDEF実際に呼び出されます;)
マシューM.

3

そのようなプリミティブ型にtypedefを使用すると、Cスタイルコードのように見えます。

C ++では、たとえば、EventLevelなどの関数をオーバーロードしようとすると、すぐに興味深いエラーが発生しますHour。これにより、余分な型名はほとんど役に立たなくなります。


2

私たちは(私たちの会社で)C ++でたくさんやっています。コードの理解と維持に役立ちます。チーム間で人を移動する場合やリファクタリングを行う場合に適しています。例:

typedef float Price;
typedef int64_t JavaTimestmap;

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

リプレゼンテーションタイプからディメンション名にtypedefを作成することをお勧めします。この新しい名前は、ソフトウェアの一般的な役割を表しています。パラメータの名前はローカルロールです。のようにUser sender, User receiver。いくつかの場所では、のようvoid register(User user)に冗長になる場合がありますが、問題ではないと思います。

後でfloat、予約の特別な丸め規則のために価格を表すのに最適ではないという考えがあるかもしれません。そのため、1つをダウンロードまたは実装してBCDFloat(バイナリコード10進数)タイプを変更し、typedefを変更します。そこには、検索ではありませんから作業を置き換えるfloatBCDFloatあなたのコード内で、おそらくより多くの山車があるという事実によって硬化されるであろう。

これは特効薬ではなく、独自の注意事項がありますが、使用するよりも使用する方がずっと良いと思います。


あるいは、Mathieu M.がSkizzの投稿で示唆したようにBOOST_STRONG_TYPEDEF(float, Price)、人は次のようにすることができますが、私は平均的なプロジェクトにはそれほど行きません。または多分私は。寝ないといけない。:-)
Notinlist

1

typedef基本的に、のエイリアスを指定できますtype
これにより、何度も何度も入力type namesするtype必要がなくなり、エイリアス名がの意図や目的を示す読みやすくなりますtype

typedefプロジェクトでより読みやすい名前を使用する場合は、選択の問題です。
通常、typedefプリミティブ型を使用することは避けますが、入力するのが異常に長い場合を除きます。パラメーター名をよりわかりやすく示しています。


0

私はこのようなことは決してしません。それらがすべて同じサイズであることを確認することは一つのことです-しかし、あなたはそれらを整数型として参照する必要があります。


0

このようにtypedefを使用すること、それらを使用することをやめる者がその基礎となる表現について何も知る必要がない限り大丈夫です。あなたが渡したい場合たとえば、PacketLengthどちらかにオブジェクトをprintfscanf、あなたは右の変換指定子を選ぶことができますように、その実際の型を知っておく必要があります。そのような場合、typedefは、見返りに何も購入せずに難読化のレベルを追加するだけです。オブジェクトをとして定義したかもしれませんint32_t

各型に固有のセマンティクス(許容範囲や値など)を適用する必要がある場合は、typedefを作成するだけでなく、その型を操作する抽象データ型と関数を作成することをお勧めします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.