最近2018中文字幕在日韩欧美国产成人片_国产日韩精品一区二区在线_在线观看成年美女黄网色视频_国产精品一区三区五区_国产精彩刺激乱对白_看黄色黄大色黄片免费_人人超碰自拍cao_国产高清av在线_亚洲精品电影av_日韩美女尤物视频网站

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
通過宏封裝實(shí)現(xiàn)std::format編譯期檢查參數(shù)數(shù)量是否一致

背景

std::format在傳參數(shù)量少于格式串所需參數(shù)數(shù)量時(shí),會(huì)拋出異常。而在大部分的應(yīng)用場景下,參數(shù)數(shù)量不一致提供編譯報(bào)錯(cuò)更加合適,可以促進(jìn)我們更早發(fā)現(xiàn)問題并進(jìn)行改正。

創(chuàng)新互聯(lián)主要從事做網(wǎng)站、成都做網(wǎng)站、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)景泰,十年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):028-86922220

最終效果

// 測試輸出接口。
template 
void Print(const std::string& _Fmt, const T&... _Args)
{
    cout << std::vformat(_Fmt, std::make_format_args(_Args...)) << endl;
}

// 封裝宏,實(shí)現(xiàn)參數(shù)數(shù)量一致的檢查
#define PRINT(fmt, ...) \
    do { static_assert(GetFormatStringArgsNum(fmt) == decltype(VariableArgsNumHelper(__VA_ARGS__))::value, "Invalid format string or mismatched number of arguments"); Print(fmt, __VA_ARGS__); } while(0)

int main()
{
    PRINT("{}", "hello");
    PRINT("{} {}", "hello");

    return 0;
}

上例代碼中,使用PRINT宏封裝了Print函數(shù),后續(xù)使用PRINT進(jìn)行控制臺(tái)輸出,如果出現(xiàn)參數(shù)數(shù)量不一致,將產(chǎn)生編譯報(bào)錯(cuò):Invalid format string or mismatched number of arguments。

所用技術(shù)

  1. 靜態(tài)斷言: static_assert

  2. 格式串參數(shù)數(shù)量獲?。?/strong> GetFormatStringArgsNum,該接口聲明為constexpr,從而獲得編譯期執(zhí)行的能力。其實(shí)現(xiàn)大致為遍歷字符串,檢查其中{}的數(shù)量。

  3. 傳參數(shù)量的獲?。?/strong> 由于使用宏進(jìn)行封裝,最后其實(shí)就是需要獲得__VA_ARGS__中附帶了幾個(gè)參數(shù),網(wǎng)上可以搜到各種解決方案,這里采用的是聲明一個(gè)模板函數(shù),模板函數(shù)返回integral_constant結(jié)構(gòu)體,其對(duì)不同的參數(shù)數(shù)量,自動(dòng)生成不同的結(jié)構(gòu)體類型,之后使用decltype(VariableArgsNumHelper(__VA_ARGS__))獲得返回值類型,并從返回值類型中獲得代表參數(shù)數(shù)量的常量值,由于運(yùn)行期用不到該函數(shù),因此只提供聲明,不提供實(shí)現(xiàn)。

整體代碼

#include 
#include 
#include 
using namespace std;

constexpr int GetFormatStringArgsNum(const std::string& fmt)
{
	enum STATE
	{
		NORMAL,			// 正在解析普通串
		REPLACEMENT,	// 正在解析大括號(hào)中的內(nèi)容
	};

	// 按標(biāo)準(zhǔn)規(guī)定,格式串中要么都指定參數(shù)編號(hào),要么都不指定
	// 原文:
	// The arg-ids in a format string must all be present or all be omitted. 
	// Mixing manual and automatic indexing is an error.
	enum RULE
	{
		UNKNOWN,		// 格式串規(guī)則
		SPECIFIEDID,	// 指定編號(hào),如{0}
		UNSPECIFIEDID,	// 不指定編號(hào),如{}
	};

	// 指定參數(shù)編號(hào)的最大值
	const int MAX_ARGS_NUM = ;
	// 初始狀態(tài)
	STATE state = NORMAL;
	// 初始規(guī)則
	RULE rule = UNKNOWN;
	// 當(dāng)前參數(shù)編號(hào)
	int nIndex = -1;
	// 參數(shù)數(shù)量
	int nArgsNum = 0;
	for (int i = 0; i < fmt.size(); ++i)
	{
		switch (state)
		{
		case NORMAL:
		{
			// 普通串解析時(shí),遇到左大括號(hào)或右大括號(hào),才有可能改變狀態(tài)
			if (fmt[i] == '{')
			{
				if (i + 1 < fmt.size() && fmt[i + 1] == '{')
				{
					// 遇到 {{,則將他們視為普通字符
					++i;
				}
				else
				{
					// 進(jìn)入替換串狀態(tài)
					state = REPLACEMENT;
				}
			}
			else if (fmt[i] == '}')
			{
				++i;
				if (i >= fmt.size() || fmt[i] != '}')
				{
					// 普通串解析狀態(tài),遇上右大括號(hào)時(shí),只有當(dāng)接下來也是右大括號(hào)時(shí),才屬于合法串
					return -1;
				}
			}
		}
		break;
		case REPLACEMENT:
		{
			// 替換串狀態(tài)下,正常只會(huì)遇到右大括號(hào)、數(shù)字、冒號(hào),其他符號(hào)均為錯(cuò)誤
			if (fmt[i] == '}')
			{
				// 遇到右大括號(hào),則進(jìn)入普通串解析狀態(tài),這里不考慮}},正常{} 中間不應(yīng)該出現(xiàn)}
				state = NORMAL;

				// 如果之前某個(gè){} 已經(jīng)指定參數(shù)編號(hào),則所有參數(shù)都應(yīng)該指定編號(hào)
				if (rule == SPECIFIEDID)
				{
					// 如果這個(gè){} 不指定編號(hào),則視為非法格式串
					if (nIndex == -1)
					{
						return -1;
					}
					// 在指定編號(hào)的情況下,可變參數(shù)的數(shù)量至少要比編號(hào)大1
					nArgsNum = std::max(nArgsNum, nIndex + 1);
					// 重置當(dāng)前編號(hào)
					nIndex = -1;
				}
				else
				{
					// 如果當(dāng)前規(guī)則未明或者當(dāng)前規(guī)則為不指定編號(hào),則參數(shù)數(shù)量進(jìn)行自增。
					state = NORMAL;
					rule = UNSPECIFIEDID;
					++nArgsNum;
				}
			}
			else if (fmt[i] >= '0' && fmt[i] <= '9')
			{
				// 遇到數(shù)字,說明指定了參數(shù)編號(hào)
				if (rule == UNSPECIFIEDID)
				{
					// 如果當(dāng)前規(guī)則已明確為不指定編號(hào),則視為非法格式串
					return -1;
				}
				else
				{
					// 否則,將當(dāng)前規(guī)則改為指定編號(hào),并維護(hù)當(dāng)前編號(hào)
					rule = SPECIFIEDID;
					if (nIndex == -1)
					{
						nIndex = 0;
					}

					nIndex = nIndex * 10 + (fmt[i] - '0');
					if (nIndex >= MAX_ARGS_NUM)
					{
						// 當(dāng)前編號(hào)大于最大上限,則直接視為非法格式串
						return -1;
					}
				}
			}
			else if (fmt[i] == ':')
			{
				// 遇到冒號(hào),說明接下來是格式串規(guī)則,直接跳過
				for (; i + 1 < fmt.size() && fmt[i + 1] != '}'; ++i)
				{
					;
				}
			}
			else
			{
				// 解析替換串時(shí),遇上其他字符,均將格式串視為非法。
				return -1;
			}
		}
		break;
		}
	}

	// 最終狀態(tài)必須為普通串解析狀態(tài)。
	return state == NORMAL ? nArgsNum : -1;
}

// 可變參數(shù)數(shù)量輔助器
template 
std::integral_constant VariableArgsNumHelper(const Args  & ...);

// 測試輸出接口。
template 
void Print(const std::string& _Fmt, const T&... _Args)
{
	cout << std::vformat(_Fmt, std::make_format_args(_Args...)) << endl;
}

// 封裝宏,實(shí)現(xiàn)參數(shù)數(shù)量一致的檢查
#define PRINT(fmt, ...) \
    do { static_assert(GetFormatStringArgsNum(fmt) == decltype(VariableArgsNumHelper(__VA_ARGS__))::value, "Invalid format string or mismatched number of arguments"); Print(fmt, __VA_ARGS__); } while(0)


int main()
{
	PRINT("{} {}", "hello");

	return 0;
}

名稱欄目:通過宏封裝實(shí)現(xiàn)std::format編譯期檢查參數(shù)數(shù)量是否一致
文章鏈接:http://fisionsoft.com.cn/article/dsoigds.html