记一个EasyDarwin框架c++11的编译错误error: unable to find string literal operator ‘operator“

问题起因

之前在linux环境中编译流媒体服务器框架EasyDarwin时,遇到了个莫名其妙的编译错误:

error: unable to find string literal operator ‘operator""_U32BITARG_’ with ‘const char [2]’

最后经查证,究其原因是C++11开始就不支持字符串字面量后面直接连接变量名。这就是c++编译中存在的潜在的坑啊。

还有个潜在的坑是返回值的坑,有返回值的函数没有指定return,或是return了却没有给出返回值在gcc进行-O优化等级大于1时,会出现各种稀奇古怪的core。针对返回值的坑,推荐使用GCC编译时开启编译选项:-Werror=return-type。这样,有上述返回值问题的代码就会在编译期间被编译器识别并报错。

这个问题在各个技术交流群里问,竟很少有人知道。这里总结下,分享给有需要的小伙伴。

error: unable to find string literal operator 'operator"

这个感觉像是历史的遗留问题了,从C++11开始就不支持字符串字面量后面直接连接变量名,GCC 4.8.2应该是没有支持该编译检查所以没问题,升级gcc8.2以上的gcc的时候报了类似的错误。

问题复现:

假如有这样的宏定义

#define SCRPRINT(fmt,...)     fprintf(stderr, "[%s]-> "fmt"
", __FUNCTION__, ##__VA_ARGS__)

在linux的c或者windows下的c/c++都没问题,在linux的cpp中编译无法通过,报下面的错误:

error: unable to find string literal operator ‘operator""fmt’ with ‘const char [4]’, ‘long unsigned int’ arguments

当编译器刚好也升级了,导致以前的代码不兼容新的编译器。

错误示例:

#include <iostream>

using namespace std;
#define SCRPRINT(fmt,...)     fprintf(stderr, "[%s]-> "fmt"\n", __FUNCTION__, ##__VA_ARGS__)
int main()
{
    cout<<"Hello World";
    #define VAR_NAME "variable"
    cout << "Value of "VAR_NAME" is: " << 3 << std::endl;
    SCRPRINT("aa");
    return 0;
}

正确做法:

#include <iostream>

using namespace std;
//! 在fmt,和"fmt"中都需要增加空格
#define SCRPRINT(fmt, ...)     fprintf(stderr, "[%s]-> " fmt "\n", __FUNCTION__, ##__VA_ARGS__)
int main()
{
    cout<<"Hello World";
    #define VAR_NAME "variable"
    //! VAR_NAME 前后都需要增加空格
    cout << "Value of " VAR_NAME " is: " << 3 << std::endl;
    SCRPRINT("aa");
    return 0;
}

你说这坑不坑,如此的细节,如若不使用智能些的IDE,很容易出这类错误。 这也是建议项目最好使用强大些的IDE的原因,能给你避免一些潜在的坑。

从C++11开始,不支持直接将字符串字面量与变量名连接。这是因为在C++11中,字符串字面量后面连接变量名的功能是通过自定义的字符串字面量操作符来实现的,而不是通过语言本身的特性。 在C++11之前,可以通过在字符串字面量后面使用宏或其他技巧将其与变量名连接起来。

例如:

#include <iostream>

using namespace std;

int main()
{
    cout<<"Hello World";
    #define VAR_NAME "variable"
    cout << "Value of "VAR_NAME" is: " << 3 << std::endl;
    return 0;
}

以上代码,在c++11之前编译则没问题,在c++11之后编译则报错。好在有的编译器给你明确提示出来的出错原因,有的则直接报错,有的给出个编译警告:

main.cpp:17:13: warning: invalid suffix on literal; C++11 requires a space between literal and string macro [-Wliteral-suffix]
   17 |     cout << "Value of "VAR_NAME" is: " << 3 << std::endl;
      |             ^
Hello WorldValue of variable is: 3

 但有的则直接报错了,比如编译EasyDarwin源码时,

报错:

解决办法:

改源码,在宏定义的前后都增加一个空格。C++11要求当字符串跟变量连接的时候,必须增加一个空格才行。

更改前为:

qtss_sprintf(contentLength, "%"_U32BITARG_"", length_32bit);

更改后正确的为:

qtss_sprintf(contentLength, "%" _U32BITARG_ "", length_32bit);

然而这种方法虽然编译通过,但仍存在一些问题不建议这么用,如宏的作用域和潜在的命名冲突等。因此C++11引入了原生的字符串字面量操作符,以提供更安全和可读性更好的方式来连接字符串字面量和变量名。 在C++11及更高版本中,可以使用自定义的字符串字面量操作符来连接字符串字面量和变量名。

例如: 

constexpr auto VAR_NAME = "variable"s;
std::cout << "Value of " << VAR_NAME << " is: " << variable << std::endl;

在这个例子中, "variable"s 使用了C++11引入的 std::string 字面量操作符 s ,它返回一个 std::string 对象,而不是一个C风格的字符串字面量。 通过这种方式,C++11提供了更安全和更灵活的方法来连接字符串字面量和变量名。这样可以避免了宏的问题,并提供了更好的类型安全性和代码可读性。 

为啥平常工作中很少遇到这类问题?因为你可能使用的clion等智能IDE,自动给你解决了,当你有错误写法时保存文件时,自动的给你纠正了过来。不信请看:

#define LOG(fmt, ...) printf("[%s][%s][%d]:"fmt "\n", __FILE__, __FUNCTION__,\
                            __LINE__, ##__VA_ARGS__)

上述代码的fmt,后面多了个空格,然后才是..., 这种写法是正确的。如果你把...仅挨着fmt,呢?那样编译就报错了!在clion的IDE中会自动的给你纠正过来,在逗号后给你多加个空格。 

其他资源

流媒体服务器之————Linux(Ubuntu)环境运行EasyDarwin-技术文章-jiaocheng.bubufx.com

mirrors / easydarwin / easydarwin · GitCode

Releases · EasyDarwin/EasyDarwin · GitHub

C++雾中风景番外篇4:GCC升级二三事 - 简书

C++雾中风景番外篇4:GCC升级二三事 - 简书
https://www.cnblogs.com/xiaotong-sun/p/17461218.html

compile c# online 

GDB online Debugger | Compiler - Code, Compile, Run, Debug online C, C++

猜你喜欢

转载自blog.csdn.net/qq8864/article/details/133793294