|
|
nono 1.7.0
//
// nono
// Copyright (C) 2021 nono project
// Licensed under nono-license.txt
//
#include <vector>
#include <tuple>
#include <functional>
#include "mystring.h"
// デバッグ文字列を返す
// プリミティブ型
template <class T>
std::string
ToDebugString(T x) {
return std::to_string(x);
}
// デバッグ文字列を返す
// bool 型
static std::string
ToDebugString(bool x)
{
return x ? "true" : "false";
}
// デバッグ文字列を返す
// string 型
static std::string
ToDebugString(const std::string& x)
{
std::string rv { '"' };
// TODO: escape
rv += x;
rv += '"';
return rv;
}
static std::string __unused
ToDebugString(const char *x)
{
return ToDebugString(std::string { x });
}
// デバッグ文字列を返す
// std::vector<std::string>
static std::string
ToDebugString(const std::vector<std::string>& x)
{
std::string rv;
for (const auto& item : x) {
if (rv.empty()) {
rv += "{";
} else {
rv += ",";
}
rv += ToDebugString(item);
}
rv += "}";
return rv;
}
// Tuple with ToDebugString
// 前方宣言
template <class... Args>
class TestTuple;
// デバッグ文字列を返す
// TestTuple 型
template <class... Args>
std::string
ToDebugString(const TestTuple<Args...>& x)
{
return x.ToDebugString();
}
// オレオレタプル
template <class... Args>
class TestTuple : public std::tuple<Args...>
{
// 継承コンストラクタ宣言
using std::tuple<Args...>::tuple;
public:
// デバッグ文字列を返す
std::string ToDebugString() const {
return ToD(std::index_sequence_for<Args...>{});
}
// std::get のメンバ関数化
// 型が決まらないといけないので [] には出来ない。
template <size_t i>
auto get() const {
return std::get<i>(*this);
}
private:
// このへん参照
// https://theolizer.com/cpp-school2/cpp-school2-21/
template <size_t... indices>
std::string ToD(std::index_sequence<indices...>) const {
// get<> にはテンプレート引数を渡さないといけないので
// 配列要素数がコンパイル時展開になるようにする。
// これでタプルの各要素が型ごとに異なる ToDebugString()
// の実体関数を呼び出して、結果の文字列が配列に入っている
// 状態になる、ようだ。
std::string a[] = {
(::ToDebugString(get<indices>()))...
};
// a[] をよしなに連結すれば良い
std::string rv { '{' };
bool first = true;
for (const auto& s : a) {
if (first) {
first = false;
} else {
rv += ',';
}
rv += s;
}
rv += '}';
return rv;
}
};
// テストデータ用テーブル
template <class... Args>
class TestTable : public std::vector<TestTuple<Args...> >
{
// 継承コンストラクタ宣言
using std::vector<TestTuple<Args... > >::vector;
};
// Test
class TestClass
{
public:
virtual ~TestClass() {}
template <class T, class R>
void Fail(T expectobj, R real) {
printf("Failed at: %s but %s\n",
ToDebugString(expectobj).c_str(),
ToDebugString(real).c_str());
exit(1);
}
void Test() {
Init();
for (auto&& t : testlist) {
printf("Testing: %s ", t.name.c_str());
t.func();
printf("Ok\n");
}
}
protected:
// 継承して、TESTDEF マクロを使って testlist を作ること。
virtual void Init() = 0;
// テスト関数のリスト
class Entry {
public:
std::string name;
std::function<void(void)> func;
};
std::vector<Entry> testlist;
#define TESTDEF(x) testlist.push_back({ #x, [&]() { x(); } })
template <class T, class TWhere>
void xp_eq(T expect, T actual, const TWhere& where)
{
// eq なので == で比較。演算子オーバーロード対策。
if (expect == actual) {
return;
}
Fail(where, actual);
}
};
class Test_MyString : public TestClass
{
protected:
void Init() override {
// TESTDEF マクロに、テスト実行関数の識別子を渡すこと。
TESTDEF(string_ltrim_1);
TESTDEF(string_rtrim_1);
TESTDEF(rtrim_1);
TESTDEF(string_trim_1);
TESTDEF(startwith_ignorecase_1);
TESTDEF(string_split_1);
TESTDEF(string_split_2);
TESTDEF(format_number_1);
TESTDEF(SecToStr_1);
TESTDEF(TimeToStr_1);
}
void string_ltrim_1() {
TestTable<std::string, std::string> table = {
{ "", "" },
{ "a", "a" },
{ "abc", "abc" },
{ "a ", "a " },
{ " a", "a" },
{ "a \t", "a \t" },
{ "\t a", "a" },
{ " a ", "a " },
{ " abc ", "abc " },
{ "\r\na\r\n", "a\r\n" }, // 行頭の改行も空白文字になる
{ " ", "" },
};
for (const auto& t : table) {
const auto& src = t.get<0>();
const auto& exp = t.get<1>();
xp_eq(exp, string_ltrim(src), t);
}
}
void string_rtrim_1() {
TestTable<std::string, std::string> table = {
{ "", "" },
{ "a", "a" },
{ "abc", "abc" },
{ "a ", "a" },
{ " a", " a" },
{ "a \t", "a" },
{ "\t a", "\t a" },
{ " a ", " a" },
{ " abc ", " abc" },
{ "\r\na\r\n", "\r\na" },
{ " ", "" },
};
for (const auto& t : table) {
auto src = t.get<0>();
const auto& exp = t.get<1>();
string_rtrim(src);
xp_eq(exp, src, t);
}
}
void rtrim_1() {
TestTable<std::string, std::string> table = {
{ "", "" },
{ "a", "a" },
{ "abc", "abc" },
{ "a ", "a" },
{ " a", " a" },
{ "a \t", "a" },
{ "\t a", "\t a" },
{ " a ", " a" },
{ " abc ", " abc" },
{ "\r\na\r\n", "\r\na" },
{ " ", "" },
};
for (const auto& t : table) {
const auto& src = t.get<0>();
const auto& exp = t.get<1>();
std::vector<char> buf(src.size() + 1);
memcpy(buf.data(), src.c_str(), buf.size());
rtrim(buf.data());
xp_eq(exp, std::string(buf.data()), t);
}
}
void string_trim_1() {
TestTable<std::string, std::string> table = {
{ "", "" },
{ "a", "a" },
{ "abc", "abc" },
{ "a ", "a" },
{ " a", "a" },
{ "a \t", "a" },
{ "\t a", "a" },
{ " a ", "a" },
{ " abc ", "abc" },
{ "\r\na\r\n", "a" },
{ " ", "" },
};
for (const auto& t : table) {
const auto& src = t.get<0>();
const auto& exp = t.get<1>();
xp_eq(exp, string_trim(src), t);
}
}
void startwith_ignorecase_1() {
TestTable<std::string, std::string, bool> table = {
{ "ABCDEF", "A", true },
{ "ABCDEF", "B", false },
{ "ABCDEF", "AB", true },
{ "ABCDEF", "BC", false },
{ "ABCDEF", "a", true },
{ "ABCDEF", "b", false },
{ "ABCDEF", "ab", true },
{ "ABCDEF", "bc", false },
{ "aBCDEF", "a", true },
{ "aBCDEF", "b", false },
{ "aBCDEF", "ab", true },
{ "aBCDEF", "ABcd", true },
{ "", "A", false },
{ "", "", true }, // ??? これはまだわかる
{ "A", "", true }, // ??? 概念的には true だが果たして?
};
for (auto t : table) {
auto arg0 = t.get<0>();
auto arg1 = t.get<1>();
auto expect = t.get<2>();
xp_eq(expect, starts_with_ignorecase(arg0, arg1), t);
}
}
void string_split_1() {
TestTable<std::string, std::vector<std::string>> table = {
{ "", { } },
{ "ab", { "ab" } },
{ "ab,c", { "ab", "c" } },
{ "ab,", { "ab", "" } }, // separator が末尾
{ ",a,,", { "", "a", "", "" } }, // separator は連続しても独立
{ ",", { "", "" } }, // separator のみ
};
for (const auto& t : table) {
const auto& src = t.get<0>();
const auto& exp = t.get<1>();
xp_eq(exp, string_split(src, ','), t);
}
}
void string_split_2() {
TestTable<std::string, int, std::vector<std::string>> table = {
// 念のため等価のはず
{ "", 0, { } },
{ "ab", 0, { "ab" } },
{ "ab,c", 0, { "ab", "c" } },
{ "ab,", 0, { "ab", "" } },
{ ",a,,", 0, { "", "a", "", "" } },
{ ",", 0, { "", "" } },
{ "", 1, { } },
{ "ab", 1, { "ab" } },
{ "ab,c", 1, { "ab,c" } },
{ "ab,", 1, { "ab," } },
{ ",a,,", 1, { ",a,," } },
{ ",", 1, { "," } },
{ "", 2, { } },
{ "ab", 2, { "ab" } },
{ "ab,c", 2, { "ab", "c" } },
{ "ab,", 2, { "ab", "" } },
{ "a,b,c", 2, { "a", "b,c" } },
{ ",a,,", 2, { "", "a,," } },
{ ",", 2, { "", "" } },
};
for (const auto& t : table) {
const auto& src = t.get<0>();
auto num = t.get<1>();
const auto& exp = t.get<2>();
xp_eq(exp, string_split(src, ',', num), t);
}
}
void format_number_1() {
TestTable<uint64, std::string> table = {
{ 0, "0" },
{ 1, "1" },
{ 999, "999" },
{ 1000, "1,000" },
{ 12345, "12,345" },
{ 123456, "123,456" },
{ 9876543, "9,876,543" },
{ 987654321, "987,654,321" },
{ 1000000000, "1,000,000,000" },
};
for (const auto& t : table) {
auto val = t.get<0>();
const auto& exp = t.get<1>();
xp_eq(exp, format_number(val), t);
}
}
void SecToStr_1() {
TestTable<uint64, std::string> table = {
{ 0, "0.000'000'000" },
{ 1, "0.000'000'001" },
{ 999999999, "0.999'999'999" },
{ 4300020001, "4.300'020'001" },
{ 9999999999, "9.999'999'999" },
{ 10000000001, "10.000'000'001" },
{ 100000000002, "100.000'000'002" },
{ 1000000000003, "1000.000'000'003" },
{ 10000000000004, "10000.000'000'004" },
{ 100000000000005, "100000.000'000'005" },
{ 1000000000000006, "1000000.000'000'006" },
{ 10000000000000007, "10000000.000'000'007" },
{ 100000000000000008, "100000000.000'000'008" },
};
for (const auto& t : table) {
auto val = t.get<0>();
const auto& exp = t.get<1>();
xp_eq(exp, SecToStr(val), t);
}
}
void TimeToStr_1() {
TestTable<uint64, std::string> table = {
{ 0, "0.000'000'000" },
{ 1, "0.000'000'001" },
{ 999999999, "0.999'999'999" },
{ 4300020001, "4.300'020'001" },
{ 9999999999, "9.999'999'999" },
{ 10000000001, "10.000'000'001" },
{ 100000000002, " 1:40.000'000'002" },
{ 1000000000003, "16:40.000'000'003" },
{ 10000000000004, " 2:46:40.000'000'004" },
{ 100000000000005, " 1d 03:46:40.000'000'005" },
{ 1000000000000006, " 11d 13:46:40.000'000'006" },
{ 10000000000000007, "115d 17:46:40.000'000'007" },
{ 100000000000000008, "1157d 09:46:40.000'000'008" },
};
for (const auto& t : table) {
auto val = t.get<0>();
const auto& exp = t.get<1>();
xp_eq(exp, TimeToStr(val), t);
}
}
};
int
main(int ac, char *av[])
{
printf("compiled by ");
#if defined(__clang_version__)
printf("clang %s", __clang_version__);
#elif defined(__GNUC__) && defined(__VERSION__)
printf("gcc %s", __VERSION__);
#else
printf("unknown compiler");
#endif
printf("\n");
Test_MyString{}.Test();
return 0;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.