|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2021 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: #include <vector>
8: #include <tuple>
9: #include <functional>
10: #include "mystring.h"
11:
12: // デバッグ文字列を返す
13: // プリミティブ型
14: template <class T>
15: std::string
16: ToDebugString(T x) {
17: return std::to_string(x);
18: }
19:
20: // デバッグ文字列を返す
21: // bool 型
22: static std::string
23: ToDebugString(bool x)
24: {
25: return x ? "true" : "false";
26: }
27:
28: // デバッグ文字列を返す
29: // string 型
30: static std::string
31: ToDebugString(const std::string& x)
32: {
33: std::string rv { '"' };
34: // TODO: escape
35: rv += x;
36: rv += '"';
37: return rv;
38: }
39:
1.1.1.2 ! root 40: static std::string __unused
1.1 root 41: ToDebugString(const char *x)
42: {
43: return ToDebugString(std::string { x });
44: }
45:
1.1.1.2 ! root 46: // デバッグ文字列を返す
! 47: // std::vector<std::string>
! 48: static std::string
! 49: ToDebugString(const std::vector<std::string>& x)
! 50: {
! 51: std::string rv;
! 52:
! 53: for (const auto& item : x) {
! 54: if (rv.empty()) {
! 55: rv += "{";
! 56: } else {
! 57: rv += ",";
! 58: }
! 59: rv += ToDebugString(item);
! 60: }
! 61: rv += "}";
! 62: return rv;
! 63: }
! 64:
1.1 root 65: // Tuple with ToDebugString
66:
67: // 前方宣言
68: template <class... Args>
69: class TestTuple;
70:
71: // デバッグ文字列を返す
72: // TestTuple 型
73: template <class... Args>
74: std::string
75: ToDebugString(const TestTuple<Args...>& x)
76: {
77: return x.ToDebugString();
78: }
79:
80: // オレオレタプル
81: template <class... Args>
82: class TestTuple : public std::tuple<Args...>
83: {
84: // 継承コンストラクタ宣言
85: using std::tuple<Args...>::tuple;
86:
87: public:
88: // デバッグ文字列を返す
89: std::string ToDebugString() const {
90: return ToD(std::index_sequence_for<Args...>{});
91: }
92:
93: // std::get のメンバ関数化
94: // 型が決まらないといけないので [] には出来ない。
95: template <size_t i>
96: auto get() const {
97: return std::get<i>(*this);
98: }
99:
100: private:
101: // このへん参照
102: // https://theolizer.com/cpp-school2/cpp-school2-21/
103:
104: template <size_t... indices>
105: std::string ToD(std::index_sequence<indices...>) const {
106: // get<> にはテンプレート引数を渡さないといけないので
107: // 配列要素数がコンパイル時展開になるようにする。
108: // これでタプルの各要素が型ごとに異なる ToDebugString()
109: // の実体関数を呼び出して、結果の文字列が配列に入っている
110: // 状態になる、ようだ。
111: std::string a[] = {
112: (::ToDebugString(get<indices>()))...
113: };
114:
115: // a[] をよしなに連結すれば良い
116: std::string rv { '{' };
117: bool first = true;
118: for (const auto& s : a) {
119: if (first) {
120: first = false;
121: } else {
122: rv += ',';
123: }
124: rv += s;
125: }
126: rv += '}';
127: return rv;
128: }
129: };
130:
131: // テストデータ用テーブル
132: template <class... Args>
133: class TestTable : public std::vector<TestTuple<Args...> >
134: {
135: // 継承コンストラクタ宣言
136: using std::vector<TestTuple<Args... > >::vector;
137: };
138:
139:
140: // Test
141:
142: class TestClass
143: {
144: public:
145: virtual ~TestClass() {}
146:
147: template <class T, class R>
148: void Fail(T expectobj, R real) {
149: printf("Failed at: %s but %s\n",
150: ToDebugString(expectobj).c_str(),
151: ToDebugString(real).c_str());
152: exit(1);
153: }
154:
155: void Test() {
156: Init();
157:
158: for (auto&& t : testlist) {
159: printf("Testing: %s ", t.name.c_str());
160: t.func();
161: printf("Ok\n");
162: }
163: }
164:
165: protected:
1.1.1.2 ! root 166: // 継承して、TESTDEF マクロを使って testlist を作ること。
1.1 root 167: virtual void Init() = 0;
168:
169: // テスト関数のリスト
170: class Entry {
171: public:
172: std::string name;
173: std::function<void(void)> func;
174: };
175: std::vector<Entry> testlist;
176:
177: #define TESTDEF(x) testlist.push_back({ #x, [&]() { x(); } })
178:
179:
180: template <class T, class TWhere>
181: void xp_eq(T expect, T actual, const TWhere& where)
182: {
183: // eq なので == で比較。演算子オーバーロード対策。
184: if (expect == actual) {
185: return;
186: }
187:
188: Fail(where, actual);
189: }
190: };
191:
192: class Test_MyString : public TestClass
193: {
194: protected:
195: void Init() override {
1.1.1.2 ! root 196: // TESTDEF マクロに、テスト実行関数の識別子を渡すこと。
1.1 root 197: TESTDEF(string_ltrim_1);
1.1.1.2 ! root 198: TESTDEF(string_rtrim_1);
! 199: TESTDEF(rtrim_1);
! 200: TESTDEF(string_trim_1);
! 201: TESTDEF(startwith_ignorecase_1);
! 202: TESTDEF(string_split_1);
! 203: TESTDEF(string_split_2);
! 204: }
! 205:
! 206: void string_ltrim_1() {
! 207: TestTable<std::string, std::string> table = {
! 208: { "", "" },
! 209: { "a", "a" },
! 210: { "abc", "abc" },
! 211: { "a ", "a " },
! 212: { " a", "a" },
! 213: { "a \t", "a \t" },
! 214: { "\t a", "a" },
! 215: { " a ", "a " },
! 216: { " abc ", "abc " },
! 217: { "\r\na\r\n", "a\r\n" }, // 行頭の改行も空白文字になる
! 218: { " ", "" },
! 219: };
! 220:
! 221: for (const auto& t : table) {
! 222: const auto& src = t.get<0>();
! 223: const auto& exp = t.get<1>();
! 224:
! 225: xp_eq(exp, string_ltrim(src), t);
! 226: }
! 227: }
! 228:
! 229: void string_rtrim_1() {
! 230: TestTable<std::string, std::string> table = {
! 231: { "", "" },
! 232: { "a", "a" },
! 233: { "abc", "abc" },
! 234: { "a ", "a" },
! 235: { " a", " a" },
! 236: { "a \t", "a" },
! 237: { "\t a", "\t a" },
! 238: { " a ", " a" },
! 239: { " abc ", " abc" },
! 240: { "\r\na\r\n", "\r\na" },
! 241: { " ", "" },
! 242: };
! 243:
! 244: for (const auto& t : table) {
! 245: auto src = t.get<0>();
! 246: const auto& exp = t.get<1>();
! 247:
! 248: string_rtrim(src);
! 249: xp_eq(exp, src, t);
! 250: }
! 251: }
! 252:
! 253: void rtrim_1() {
! 254: TestTable<std::string, std::string> table = {
! 255: { "", "" },
! 256: { "a", "a" },
! 257: { "abc", "abc" },
! 258: { "a ", "a" },
! 259: { " a", " a" },
! 260: { "a \t", "a" },
! 261: { "\t a", "\t a" },
! 262: { " a ", " a" },
! 263: { " abc ", " abc" },
! 264: { "\r\na\r\n", "\r\na" },
! 265: { " ", "" },
! 266: };
! 267:
! 268: for (const auto& t : table) {
! 269: const auto& src = t.get<0>();
! 270: const auto& exp = t.get<1>();
! 271:
! 272: std::vector<char> buf(src.size() + 1);
! 273: memcpy(buf.data(), src.c_str(), buf.size());
! 274: rtrim(buf.data());
! 275: xp_eq(exp, std::string(buf.data()), t);
! 276: }
! 277: }
! 278:
! 279: void string_trim_1() {
! 280: TestTable<std::string, std::string> table = {
! 281: { "", "" },
! 282: { "a", "a" },
! 283: { "abc", "abc" },
! 284: { "a ", "a" },
! 285: { " a", "a" },
! 286: { "a \t", "a" },
! 287: { "\t a", "a" },
! 288: { " a ", "a" },
! 289: { " abc ", "abc" },
! 290: { "\r\na\r\n", "a" },
! 291: { " ", "" },
! 292: };
! 293:
! 294: for (const auto& t : table) {
! 295: const auto& src = t.get<0>();
! 296: const auto& exp = t.get<1>();
! 297:
! 298: xp_eq(exp, string_trim(src), t);
! 299: }
1.1 root 300: }
301:
302: void startwith_ignorecase_1() {
303: TestTable<std::string, std::string, bool> table = {
1.1.1.2 ! root 304: { "ABCDEF", "A", true },
! 305: { "ABCDEF", "B", false },
! 306: { "ABCDEF", "AB", true },
! 307: { "ABCDEF", "BC", false },
! 308: { "ABCDEF", "a", true },
! 309: { "ABCDEF", "b", false },
! 310: { "ABCDEF", "ab", true },
! 311: { "ABCDEF", "bc", false },
! 312: { "aBCDEF", "a", true },
! 313: { "aBCDEF", "b", false },
! 314: { "aBCDEF", "ab", true },
! 315: { "aBCDEF", "ABcd", true },
! 316: { "", "A", false },
! 317: { "", "", true }, // ??? これはまだわかる
! 318: { "A", "", true }, // ??? 概念的には true だが果たして?
1.1 root 319: };
320:
321: for (auto t : table) {
322: auto arg0 = t.get<0>();
323: auto arg1 = t.get<1>();
324: auto expect = t.get<2>();
325:
326: xp_eq(expect, starts_with_ignorecase(arg0, arg1), t);
327: }
328: }
329:
1.1.1.2 ! root 330: void string_split_1() {
! 331: TestTable<std::string, std::vector<std::string>> table = {
! 332: { "", { } },
! 333: { "ab", { "ab" } },
! 334: { "ab,c", { "ab", "c" } },
! 335: { "ab,", { "ab", "" } }, // separator が末尾
! 336: { ",a,,", { "", "a", "", "" } }, // separator は連続しても独立
! 337: { ",", { "", "" } }, // separator のみ
! 338: };
! 339:
! 340: for (const auto& t : table) {
! 341: const auto& src = t.get<0>();
! 342: const auto& exp = t.get<1>();
! 343:
! 344: xp_eq(exp, string_split(src, ','), t);
! 345: }
! 346: }
! 347:
! 348: void string_split_2() {
! 349: TestTable<std::string, int, std::vector<std::string>> table = {
! 350: // 念のため等価のはず
! 351: { "", 0, { } },
! 352: { "ab", 0, { "ab" } },
! 353: { "ab,c", 0, { "ab", "c" } },
! 354: { "ab,", 0, { "ab", "" } },
! 355: { ",a,,", 0, { "", "a", "", "" } },
! 356: { ",", 0, { "", "" } },
! 357:
! 358: { "", 1, { } },
! 359: { "ab", 1, { "ab" } },
! 360: { "ab,c", 1, { "ab,c" } },
! 361: { "ab,", 1, { "ab," } },
! 362: { ",a,,", 1, { ",a,," } },
! 363: { ",", 1, { "," } },
! 364:
! 365: { "", 2, { } },
! 366: { "ab", 2, { "ab" } },
! 367: { "ab,c", 2, { "ab", "c" } },
! 368: { "ab,", 2, { "ab", "" } },
! 369: { "a,b,c", 2, { "a", "b,c" } },
! 370: { ",a,,", 2, { "", "a,," } },
! 371: { ",", 2, { "", "" } },
! 372: };
! 373:
! 374: for (const auto& t : table) {
! 375: const auto& src = t.get<0>();
! 376: auto num = t.get<1>();
! 377: const auto& exp = t.get<2>();
! 378:
! 379: xp_eq(exp, string_split(src, ',', num), t);
! 380: }
1.1 root 381: }
382: };
383:
384: int
385: main(int ac, char *av[])
386: {
387: printf("compiled by ");
388: #if defined(__clang_version__)
389: printf("clang %s", __clang_version__);
390: #elif defined(__GNUC__) && defined(__VERSION__)
391: printf("gcc %s", __VERSION__);
392: #else
393: printf("unknown compiler");
394: #endif
395: printf("\n");
396:
397: Test_MyString{}.Test();
398: return 0;
399: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.