Godot 4 ソースコード解析 - フォーマット文字列関数を追加

Godot 4 の主な文字列型は String であり、比較的よく設計されていますが、書式設定があまり考慮されていないという問題があります。

String の format 関数がありますが、この関数にはパラメータが 2 つしかありません。どのように使用すればよいですか?

String String::format(const Variant &values, String placeholder) const {
	String new_string = String(this->ptr());

	if (values.get_type() == Variant::ARRAY) {
		Array values_arr = values;

		for (int i = 0; i < values_arr.size(); i++) {
			String i_as_str = String::num_int64(i);

			if (values_arr[i].get_type() == Variant::ARRAY) { //Array in Array structure [["name","RobotGuy"],[0,"godot"],["strength",9000.91]]
				Array value_arr = values_arr[i];

				if (value_arr.size() == 2) {
					Variant v_key = value_arr[0];
					String key = v_key;

					Variant v_val = value_arr[1];
					String val = v_val;

					new_string = new_string.replace(placeholder.replace("_", key), val);
				} else {
					ERR_PRINT(String("STRING.format Inner Array size != 2 ").ascii().get_data());
				}
			} else { //Array structure ["RobotGuy","Logis","rookie"]
				Variant v_val = values_arr[i];
				String val = v_val;

				if (placeholder.find("_") > -1) {
					new_string = new_string.replace(placeholder.replace("_", i_as_str), val);
				} else {
					new_string = new_string.replace_first(placeholder, val);
				}
			}
		}
	} else if (values.get_type() == Variant::DICTIONARY) {
		Dictionary d = values;
		List<Variant> keys;
		d.get_key_list(&keys);

		for (const Variant &key : keys) {
			new_string = new_string.replace(placeholder.replace("_", key), d[key]);
		}
	} else {
		ERR_PRINT(String("Invalid type: use Array or Dictionary.").ascii().get_data());
	}

	return new_string;
}

使用例を検索します。すべてこの効果があります

一目見てびっくりしました。以前に使用されていた %s %d... の形式はどこにありますか?

自分でやれ

template <typename... Args>
static std::string str_format(const std::string &format, Args... args) {
	auto size_buf = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
	std::unique_ptr<char[]> buf(new (std::nothrow) char[size_buf]);

	if (!buf)
		return std::string("");

	std::snprintf(buf.get(), size_buf, format.c_str(), args...);
	return std::string(buf.get(), buf.get() + size_buf - 1);
}

template <typename... Args>
static String str_format(const std::u32string &format, Args... args) {
	auto size_buf = std::snprintf(nullptr, 0, TDrString::Convert_u32String_stdString(format).c_str(), args...) + 1;
	std::unique_ptr<char[]> buf(new (std::nothrow) char[size_buf]);

	if (!buf)
		return String(U"");

	std::string strFormat = TDrString::Convert_u32String_stdString(format);
	std::snprintf(buf.get(), size_buf, strFormat.c_str(), args...);
	std::string str(buf.get(), buf.get() + size_buf - 1);
	return String(str);
}

std::string と String の 2 つの書式設定効果を提供します。次に、次を使用します。

cofs << U"ERROR" << str_format(U"函数 [%s] 调用失败:参数个数不匹配,形参 [%d] 个,实参 [%d]个", drFunCall.GetHint().c_str(), it->arguments.size(), drFunCall.arguments.size());

ちなみに、ネットで調べてみたところ、fmtライブラリの評価が良いことが分かりました。GitHub - fmtlib/fmtから直接プルして、最新の書式設定ライブラリをソース コードに追加して使用できます。

ただし、fmt の使用法は {} です。これは少し新鮮で、C++ 20 と互換性があるようですので、最初に使用してください。

fmt::format("[{}.Read] > 解析数据{}", prefixType, hint.utf8().ptr());

中国語は直接サポートされています。

このプロセスで、小さな問題を発見しました。 std::string は String に直接変換できません。また、String クラスは多数のコンストラクターを提供しますが、std::string がありません。

	String(const char *p_str);
	String(const wchar_t *p_str);
	String(const char32_t *p_str);
	String(const char *p_str, int p_clip_to_len);
	String(const wchar_t *p_str, int p_clip_to_len);
	String(const char32_t *p_str, int p_clip_to_len);
	String(const StrRange &p_range);

String と std::string の間の相互変換のロジックを便利に追加します。

头文件:
	String(const std::string &str);
	operator std::string();

源文件:
String::String(const std::string &str) {
	std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
	std::wstring formatted_wstring = converter.from_bytes(str);
	copy_from(formatted_wstring.c_str());
}		

String::operator std::string() {
	std::string utf8;
    const char32_t * utf32 = ptr();
	for (char32_t c : utf32) {
		if (c <= 0x7F) {
			utf8.push_back(static_cast<char>(c));
		} else if (c <= 0x7FF) {
			utf8.push_back(static_cast<char>((c >> 6) | 0xC0));
			utf8.push_back(static_cast<char>((c & 0x3F) | 0x80));
		} else if (c <= 0xFFFF) {
			utf8.push_back(static_cast<char>((c >> 12) | 0xE0));
			utf8.push_back(static_cast<char>(((c >> 6) & 0x3F) | 0x80));
			utf8.push_back(static_cast<char>((c & 0x3F) | 0x80));
		} else if (c <= 0x10FFFF) {
			utf8.push_back(static_cast<char>((c >> 18) | 0xF0));
			utf8.push_back(static_cast<char>(((c >> 12) & 0x3F) | 0x80));
			utf8.push_back(static_cast<char>(((c >> 6) & 0x3F) | 0x80));
			utf8.push_back(static_cast<char>((c & 0x3F) | 0x80));
		} else {
			throw std::invalid_argument("Invalid UTF-32 character.");
		}
	}
	return utf8;
}

これは非常に便利です。

たとえば、Variant を std::string に変換するには、ずっと火花と稲妻が発生します。

Variant v;
...
std::string str = v.operator String().operator std::string();

実際には、もっと簡単な使用方法もありますが、コード プロンプトと自動補完の場合は、この方法でコードを記述する方が快適です。

おすすめ

転載: blog.csdn.net/drgraph/article/details/132080673