第3章:テストとデバッグ
はじめに
本章では、CPP Module 06のテストとデバッグ方法を解説します。
---
1. ScalarConverterのテスト
1.1 文字リテラルのテスト
void testCharLiterals() {
std::cout << "=== Character Literals ===" << std::endl;
// 表示可能文字
ScalarConverter::convert("a");
// char: 'a'
// int: 97
// float: 97.0f
// double: 97.0
ScalarConverter::convert("Z");
// char: 'Z'
// int: 90
// float: 90.0f
// double: 90.0
ScalarConverter::convert("*");
// char: '*'
// int: 42
// float: 42.0f
// double: 42.0
}
1.2 整数リテラルのテスト
void testIntLiterals() {
std::cout << "=== Integer Literals ===" << std::endl;
ScalarConverter::convert("0");
ScalarConverter::convert("42");
ScalarConverter::convert("-42");
ScalarConverter::convert("2147483647"); // INT_MAX
ScalarConverter::convert("-2147483648"); // INT_MIN
ScalarConverter::convert("2147483648"); // オーバーフロー
}
1.3 浮動小数点リテラルのテスト
void testFloatDoubleLiterals() {
std::cout << "=== Float/Double Literals ===" << std::endl;
ScalarConverter::convert("0.0");
ScalarConverter::convert("42.0f");
ScalarConverter::convert("-4.2f");
ScalarConverter::convert("3.14159");
}
1.4 特殊値のテスト
void testSpecialValues() {
std::cout << "=== Special Values ===" << std::endl;
// Float特殊値
ScalarConverter::convert("nanf");
// char: impossible
// int: impossible
// float: nanf
// double: nan
ScalarConverter::convert("+inff");
ScalarConverter::convert("-inff");
// Double特殊値
ScalarConverter::convert("nan");
ScalarConverter::convert("+inf");
ScalarConverter::convert("-inf");
}
1.5 エラーケースのテスト
void testErrorCases() {
std::cout << "=== Error Cases ===" << std::endl;
ScalarConverter::convert(""); // 空文字列
ScalarConverter::convert("hello"); // 無効な文字列
ScalarConverter::convert("42.42.42"); // 複数のドット
ScalarConverter::convert("--42"); // 複数の符号
ScalarConverter::convert("42ff"); // 無効なサフィックス
}
---
2. Serializerのテスト
2.1 基本テスト
void testSerializerBasic() {
std::cout << "=== Serializer Basic Test ===" << std::endl;
Data data;
data.id = 42;
data.name = "Test";
uintptr_t raw = Serializer::serialize(&data);
Data* result = Serializer::deserialize(raw);
// 同じポインタを返すか確認
assert(result == &data);
std::cout << "Pointer comparison: PASSED" << std::endl;
// データが正しいか確認
assert(result->id == 42);
assert(result->name == "Test");
std::cout << "Data integrity: PASSED" << std::endl;
}
2.2 複数データのテスト
void testSerializerMultiple() {
std::cout << "=== Serializer Multiple Test ===" << std::endl;
Data data1, data2, data3;
data1.id = 1;
data2.id = 2;
data3.id = 3;
uintptr_t raw1 = Serializer::serialize(&data1);
uintptr_t raw2 = Serializer::serialize(&data2);
uintptr_t raw3 = Serializer::serialize(&data3);
// それぞれ異なる値
assert(raw1 != raw2);
assert(raw2 != raw3);
assert(raw1 != raw3);
// 正しくデシリアライズ
assert(Serializer::deserialize(raw1)->id == 1);
assert(Serializer::deserialize(raw2)->id == 2);
assert(Serializer::deserialize(raw3)->id == 3);
std::cout << "Multiple data: PASSED" << std::endl;
}
---
3. 型識別のテスト
3.1 ポインタ版のテスト
void testIdentifyPointer() {
std::cout << "=== Identify Pointer Test ===" << std::endl;
A a;
B b;
C c;
Base* pa = &a;
Base* pb = &b;
Base* pc = &c;
std::cout << "Expected A: ";
identify(pa);
std::cout << "Expected B: ";
identify(pb);
std::cout << "Expected C: ";
identify(pc);
}
3.2 参照版のテスト
void testIdentifyReference() {
std::cout << "=== Identify Reference Test ===" << std::endl;
A a;
B b;
C c;
Base& ra = a;
Base& rb = b;
Base& rc = c;
std::cout << "Expected A: ";
identify(ra);
std::cout << "Expected B: ";
identify(rb);
std::cout << "Expected C: ";
identify(rc);
}
3.3 generate()のランダム性テスト
void testGenerate() {
std::cout << "=== Generate Randomness Test ===" << std::endl;
int countA = 0, countB = 0, countC = 0;
for (int i = 0; i < 100; i++) {
Base* ptr = generate();
if (dynamic_cast<A*>(ptr)) countA++;
else if (dynamic_cast<B*>(ptr)) countB++;
else if (dynamic_cast<C*>(ptr)) countC++;
delete ptr;
}
std::cout << "A: " << countA << std::endl;
std::cout << "B: " << countB << std::endl;
std::cout << "C: " << countC << std::endl;
// すべての型が少なくとも1回は生成されているはず
assert(countA > 0);
assert(countB > 0);
assert(countC > 0);
std::cout << "All types generated: PASSED" << std::endl;
}
---
4. よくある間違い
4.1 ScalarConverter: リテラル判定の順序
// 間違い: 整数をfloat/doubleとして判定
if (hasDecimalPoint(literal)) {
// ...
} else {
// intとして処理 - これが先でよい
}
// 正しい順序
// 1. 特殊値(nan, inf)
// 2. 単一文字
// 3. float(末尾がf)
// 4. double(小数点あり)
// 5. int
4.2 ScalarConverter: 精度の問題
// 間違い: setprecisionを忘れる
std::cout << "float: " << f << "f" << std::endl;
// 出力: float: 42f (小数点なし)
// 正しい
std::cout << std::fixed << std::setprecision(1);
std::cout << "float: " << f << "f" << std::endl;
// 出力: float: 42.0f
4.3 Serializer: NULLポインタ
// NULLポインタも正しくシリアライズ
Data* ptr = NULL;
uintptr_t raw = Serializer::serialize(ptr); // 0
Data* result = Serializer::deserialize(raw); // NULL
4.4 identify: dynamic_cast参照版
// 間違い: ポインタ版と同じ方法
A* a = dynamic_cast<A*>(&base);
if (a) { ... } // これはポインタ版
// 正しい: 例外を使用
try {
A& a = dynamic_cast<A&>(base);
std::cout << "A" << std::endl;
return;
} catch (std::bad_cast&) {
// 次の型を試す
}
4.5 仮想デストラクタを忘れる
// 間違い: dynamic_castが動作しない
class Base {
// 仮想関数がない
};
// 正しい
class Base {
public:
virtual ~Base() {} // 仮想デストラクタ
};
---
5. 提出前チェックリスト
ex00
- [ ] 単一文字リテラルを正しく変換
- [ ] 整数リテラルを正しく変換
- [ ] floatリテラル(fサフィックス)を正しく変換
- [ ] doubleリテラルを正しく変換
- [ ] 特殊値(nan, inf)を正しく処理
- [ ] 範囲外の値は"impossible"
- [ ] 非表示文字は"Non displayable"
- [ ] 小数点以下1桁で表示
- [ ] Data構造体に意味のあるメンバ
- [ ] serialize()がポインタをuintptr_tに変換
- [ ] deserialize()がuintptr_tをポインタに変換
- [ ] 元のポインタと同じものが返る
- [ ] Baseに仮想デストラクタ
- [ ] A, B, CがBaseを継承
- [ ] generate()がランダムに生成
- [ ] identify(Base*)がポインタから識別
- [ ] identify(Base&)が参照から識別
- [ ] 参照版はポインタを使用しない
- スカラー変換: すべてのリテラル形式と特殊値
- シリアライズ: ポインタの往復変換
- 型識別: ポインタと参照の両方の版
ex01
ex02
---
まとめ
CPP Module 06のテストでは以下を確認: