標籤:

c++如何在編譯期判斷一個對象是否是字元串字面值?

c++如何在編譯期實現判斷是否是字元串字面值呢?


最多只能判斷是不是char const()[X]


1. 你具體想實現什麼效果?

2. gcc 有 __builtin_constant_p


按照余士業 給出的鏈接跳轉中...,應該有一個比較簡單的實現:(BY Chris Lutz,我改了下)

如果要兼容M$的UNICODE標記L,和C++ 11 utf8標記u8,多判斷幾個就可以了。

#include &

constexpr bool IsLiteralImpl(const char* s) {
return *s == " || strncmp(s, "u8"", 3) == 0
|| strncmp(s, "U"", 2) == 0 || strncmp(s, "u"", 2) == 0
|| strncmp(s, "L"", 2) == 0;
}

#define IsLiteral(x) IsLiteralImpl(#x)

int main(void) {
const char* t = "def";
const char s[] = "sdf";
static_assert(IsLiteral("abc"), "Must be true");
static_assert(IsLiteral(L"abc"), "Must be true");
static_assert(IsLiteral(u8"abc"), "Must be true");
static_assert(IsLiteral(t), "Must be false"); // 編譯錯誤
static_assert(IsLiteral(s), "Must be false"); // 編譯錯誤

return 0;
}

gcc 4.6.3上測試通過,VS2013沒有constexpr,估計得VS2015。

原鏈接中的答案還判斷了給定參數是不是一個合法的字元串(即以雙引號結尾),但是還需要處理轉義和非拉丁文字元的情況,感覺有點麻煩。。

===================================

如果只是寫一個函數(宏),限制C99風格字面值傳入的話,可以簡單點,像lua中的lua_pushliteral那樣:

#define lua_pushliteral(L, x) lua_pushcstring(L, ""x)

如果傳入的不是字面量,那麼編譯會報錯。這對優化字元串的內部存儲很有好處。


在 StackOverflow 上搜到的,原文鏈接 http://stackoverflow.com/questions/5691232/,套路有點偏……

這個是簡化版本,轉義符和寬字元都沒有處理,實用版本留給讀者思考好了【滾

#include &

#include &

#define is_literal_(x) is_literal_f(#x, sizeof(#x) - 1)
#define is_literal(x) is_literal_(x)

constexpr inline bool is_literal_f_se(char const* s, std::size_t len)
{
return len == 0;
}

constexpr inline bool is_literal_f_s1(char const* s, std::size_t len)
{
return len == 0 ? false :
(*s == " ? is_literal_f_se(s + 1, len - 1) :
is_literal_f_s1(s + 1, len - 1));
}

constexpr inline bool is_literal_f(char const* s, std::size_t len)
{
return len == 0 ? false :
(*s == " ? is_literal_f_s1(s + 1, len - 1) :
false);
}

int main()
{
constexpr bool b1 = is_literal("abc");
constexpr bool b2 = is_literal("");
char const (s3)[4] = "abc";
constexpr bool b3 = is_literal(s3);
std::cout &<&< b1 &<&< &<&< b2 &<&< &<&< b3 &<&< std::endl; }


不太明白題主想實現什麼。。字面量 的意思就是 寫的是啥編譯出就是啥

所以只要是個能正常工作的編譯器肯定能判斷出是不是字元串字面量。。

最簡單方法就是遇到字元串字面量起始標誌時進入「解析字元串」的狀態,遇到轉義時進入解析轉義字元的狀態,讀一個字元再回到「解析字元串」的狀態。在「解析字元串」狀態時遇到字元串結束符號就跳出「解析字元串」的狀態並生成一個STRING類型的token。

如果說想實現java這樣編譯期把 加號拼接字面量 轉換為 字元串常量,就在語意分析里做吧。連續的二元運算就是二叉樹嘛,遞歸一下也沒啥難度。


使用用戶定義字面量

inline String operator"" s(char const* s, std::size_t l)
{
return String::Wrap(s);
}

auto s = "hello world"s; //字元串字面量加s後綴會通過上面的運算符創建String

如果只是拿到一個 char* 或者 const char* 之類的來判斷的話應該是沒有辦法(或許Windows下可以通過獲取內存屬性來判斷?)


如果是為了性能優化而要盡量使用靜態數據的話,推薦另一種適應性更好的形式:大部分成熟的程序員都不太會在真實項目中大肆使用具體表現由於 STL 實現不同而差異巨大的 std::string。而程序員自行實現的優化版 string 類基本都有直接接受字面值的「靜態」構造優化版本。

比如我可以實現一個 string 類型,包含如下構造函數:

// StaticData 是一個全局常量,幫助 stringEx 選擇正確版本的構造函數
stringEx myStr("1234", StaticData);

使用 StaticData 佔位符構造的 myStr 對象不會執行任何內存分配及內存拷貝動作,也不擁有對傳入指針("1234")的所有權(即:析構時不會試圖釋放該內存),僅僅複製指針地址。因此常被稱為「靜態」構造。

類似的優化技巧在產品級的代碼中十分常見。它一方面儘可能優化了性能(避免了堆內存分配和內存拷貝),另一方面也提供了更廣泛的適應性(任何使用字元串對象的地方都能應用此類優化)。並且也更方便——使用時對於(被調用者等)用戶透明,一舉多得。


推薦閱讀:

C++的std::thread是怎麼進行參數傳遞的?
怎麼正確書寫C++高階函數?
你怎麼看 C++11(新的 C++ 標準)裡面的變化?

TAG:C | 元編程 | C11 |