|
|
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);
1.1.1.3 ! root 204: TESTDEF(format_number_1);
1.1.1.2 root 205: }
206:
207: void string_ltrim_1() {
208: TestTable<std::string, std::string> table = {
209: { "", "" },
210: { "a", "a" },
211: { "abc", "abc" },
212: { "a ", "a " },
213: { " a", "a" },
214: { "a \t", "a \t" },
215: { "\t a", "a" },
216: { " a ", "a " },
217: { " abc ", "abc " },
218: { "\r\na\r\n", "a\r\n" }, // 行頭の改行も空白文字になる
219: { " ", "" },
220: };
221:
222: for (const auto& t : table) {
223: const auto& src = t.get<0>();
224: const auto& exp = t.get<1>();
225:
226: xp_eq(exp, string_ltrim(src), t);
227: }
228: }
229:
230: void string_rtrim_1() {
231: TestTable<std::string, std::string> table = {
232: { "", "" },
233: { "a", "a" },
234: { "abc", "abc" },
235: { "a ", "a" },
236: { " a", " a" },
237: { "a \t", "a" },
238: { "\t a", "\t a" },
239: { " a ", " a" },
240: { " abc ", " abc" },
241: { "\r\na\r\n", "\r\na" },
242: { " ", "" },
243: };
244:
245: for (const auto& t : table) {
246: auto src = t.get<0>();
247: const auto& exp = t.get<1>();
248:
249: string_rtrim(src);
250: xp_eq(exp, src, t);
251: }
252: }
253:
254: void rtrim_1() {
255: TestTable<std::string, std::string> table = {
256: { "", "" },
257: { "a", "a" },
258: { "abc", "abc" },
259: { "a ", "a" },
260: { " a", " a" },
261: { "a \t", "a" },
262: { "\t a", "\t a" },
263: { " a ", " a" },
264: { " abc ", " abc" },
265: { "\r\na\r\n", "\r\na" },
266: { " ", "" },
267: };
268:
269: for (const auto& t : table) {
270: const auto& src = t.get<0>();
271: const auto& exp = t.get<1>();
272:
273: std::vector<char> buf(src.size() + 1);
274: memcpy(buf.data(), src.c_str(), buf.size());
275: rtrim(buf.data());
276: xp_eq(exp, std::string(buf.data()), t);
277: }
278: }
279:
280: void string_trim_1() {
281: TestTable<std::string, std::string> table = {
282: { "", "" },
283: { "a", "a" },
284: { "abc", "abc" },
285: { "a ", "a" },
286: { " a", "a" },
287: { "a \t", "a" },
288: { "\t a", "a" },
289: { " a ", "a" },
290: { " abc ", "abc" },
291: { "\r\na\r\n", "a" },
292: { " ", "" },
293: };
294:
295: for (const auto& t : table) {
296: const auto& src = t.get<0>();
297: const auto& exp = t.get<1>();
298:
299: xp_eq(exp, string_trim(src), t);
300: }
1.1 root 301: }
302:
303: void startwith_ignorecase_1() {
304: TestTable<std::string, std::string, bool> table = {
1.1.1.2 root 305: { "ABCDEF", "A", true },
306: { "ABCDEF", "B", false },
307: { "ABCDEF", "AB", true },
308: { "ABCDEF", "BC", false },
309: { "ABCDEF", "a", true },
310: { "ABCDEF", "b", false },
311: { "ABCDEF", "ab", true },
312: { "ABCDEF", "bc", false },
313: { "aBCDEF", "a", true },
314: { "aBCDEF", "b", false },
315: { "aBCDEF", "ab", true },
316: { "aBCDEF", "ABcd", true },
317: { "", "A", false },
318: { "", "", true }, // ??? これはまだわかる
319: { "A", "", true }, // ??? 概念的には true だが果たして?
1.1 root 320: };
321:
322: for (auto t : table) {
323: auto arg0 = t.get<0>();
324: auto arg1 = t.get<1>();
325: auto expect = t.get<2>();
326:
327: xp_eq(expect, starts_with_ignorecase(arg0, arg1), t);
328: }
329: }
330:
1.1.1.2 root 331: void string_split_1() {
332: TestTable<std::string, std::vector<std::string>> table = {
333: { "", { } },
334: { "ab", { "ab" } },
335: { "ab,c", { "ab", "c" } },
336: { "ab,", { "ab", "" } }, // separator が末尾
337: { ",a,,", { "", "a", "", "" } }, // separator は連続しても独立
338: { ",", { "", "" } }, // separator のみ
339: };
340:
341: for (const auto& t : table) {
342: const auto& src = t.get<0>();
343: const auto& exp = t.get<1>();
344:
345: xp_eq(exp, string_split(src, ','), t);
346: }
347: }
348:
349: void string_split_2() {
350: TestTable<std::string, int, std::vector<std::string>> table = {
351: // 念のため等価のはず
352: { "", 0, { } },
353: { "ab", 0, { "ab" } },
354: { "ab,c", 0, { "ab", "c" } },
355: { "ab,", 0, { "ab", "" } },
356: { ",a,,", 0, { "", "a", "", "" } },
357: { ",", 0, { "", "" } },
358:
359: { "", 1, { } },
360: { "ab", 1, { "ab" } },
361: { "ab,c", 1, { "ab,c" } },
362: { "ab,", 1, { "ab," } },
363: { ",a,,", 1, { ",a,," } },
364: { ",", 1, { "," } },
365:
366: { "", 2, { } },
367: { "ab", 2, { "ab" } },
368: { "ab,c", 2, { "ab", "c" } },
369: { "ab,", 2, { "ab", "" } },
370: { "a,b,c", 2, { "a", "b,c" } },
371: { ",a,,", 2, { "", "a,," } },
372: { ",", 2, { "", "" } },
373: };
374:
375: for (const auto& t : table) {
376: const auto& src = t.get<0>();
377: auto num = t.get<1>();
378: const auto& exp = t.get<2>();
379:
380: xp_eq(exp, string_split(src, ',', num), t);
381: }
1.1 root 382: }
1.1.1.3 ! root 383:
! 384: void format_number_1() {
! 385: TestTable<uint64, std::string> table = {
! 386: { 0, "0" },
! 387: { 1, "1" },
! 388: { 999, "999" },
! 389: { 1000, "1,000" },
! 390: { 12345, "12,345" },
! 391: { 123456, "123,456" },
! 392: { 9876543, "9,876,543" },
! 393: { 987654321, "987,654,321" },
! 394: { 1000000000, "1,000,000,000" },
! 395: };
! 396:
! 397: for (const auto& t : table) {
! 398: auto val = t.get<0>();
! 399: const auto& exp = t.get<1>();
! 400:
! 401: xp_eq(exp, format_number(val), t);
! 402: }
! 403: }
1.1 root 404: };
405:
406: int
407: main(int ac, char *av[])
408: {
409: printf("compiled by ");
410: #if defined(__clang_version__)
411: printf("clang %s", __clang_version__);
412: #elif defined(__GNUC__) && defined(__VERSION__)
413: printf("gcc %s", __VERSION__);
414: #else
415: printf("unknown compiler");
416: #endif
417: printf("\n");
418:
419: Test_MyString{}.Test();
420: return 0;
421: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.