我正在开发一个小应用程序,我正在为它构建一个简单的 CLI。我希望我的 CLI 有一些颜色,因为谁喜欢无聊的旧白色和黑色控制台?(如果你这样做,JK没有冒犯:))
然后,当我在构建它时,我似乎遇到了一个问题,不幸的是,我不明白:(。问题在于一些借来的代码,这些代码应该通过将所有必要的代码包装成整洁的小函数或定义来帮助我清理代码(你很快就会看到我在说什么)。我真的不知道定义如何在C++或至少更高级的定义中工作,但这是我目前用于将 CLI 颜色代码函数包装
到的代码:颜色.h
#ifndef _COLORS_
#define _COLORS_
/* FOREGROUND */
#define RST "x1B[0m" // RESET
#define KRED "x1B[31m" // RED
#define KGRN "x1B[32m" // GREEN
#define KYEL "x1B[33m" // YELLOW
#define KBLU "x1B[34m" // BLUE
#define KMAG "x1B[35m" // MAGENTA
#define KCYN "x1B[36m" // CYAN
#define KWHT "x1B[37m" // WHITE
#define FRED(x) KRED x RST
#define FGRN(x) KGRN x RST
#define FYEL(x) KYEL x RST
#define FBLU(x) KBLU x RST
#define FMAG(x) KMAG x RST
#define FCYN(x) KCYN x RST
#define FWHT(x) KWHT x RST
#define BOLD(x) "x1B[1m" x RST // BOLD
#define UNDL(x) "x1B[4m" x RST // UNDERLINE
#endif /* _COLORS_ */
所以问题是下面的工作:
std::cout << FBLU("Hello, World. I'm blue!") << std::endl;
这不...
std::string randomString = "Hello, World. I'm blue!";
std::cout << FBLU(randomString) << std::endl;
同样,我不太熟悉"定义函数"的工作原理,但我只是想知道是否有人可以向我展示一种接受原始文本输入和已定义函数的变量输入的新方法。另外,如果您能帮助我了解定义如何在C++中发挥作用,那也很棒。
宏的工作方式与将宏定义复制粘贴到使用它的位置相同。
所以这有效:
std::cout << FBLU("Hello, World. I'm blue!") << std::endl;
// same as
std::cout << "x1B[34m" "Hello, World. I'm blue!" "x1B[0m" << std::endl;
// same as
std::cout << "x1B[34mHello, World. I'm blue!x1B[0m" << std::endl;
(C++有一个规则,相邻的字符串文本连接在一起。我想添加此规则是为了让您完全可以在这里做您正在做的事情)
这不起作用:
std::string randomString = "Hello, World. I'm blue!";
std::cout << FBLU(randomString) << std::endl;
// same as
std::cout << "x1B[34m" randomString "x1B[0m" << std::endl;
// oops, syntax error
区别在于FBLU
是一个预处理器宏(而不是函数),仅当其参数是字符串文本时,它才能按预期工作。 预处理器执行 TEXT 替换以生成源代码 - 该源代码将传递到编译的后期阶段。
预处理器将转动
FBLU("Hello, World. I'm blue!")
到
KBLU "Hello, World. I'm blue!" RST
其中(通过替换宏KBLU
和RST
)成为
"x1B[34m" "Hello, World. I'm blue!" "x1B[0m"
这是一组字符串文本,它们被追加(再次由预处理器)成为单个字符串文本
"x1B[34mHello, World. I'm blue!x1B[0m"
净效应是
std::cout << FBLU("Hello, World. I'm blue!") << std::endl;
被编译器视为
std::cout << "x1B[34mHello, World. I'm blue!x1B[0m" << std::endl;
这是一个完全有效的代码语句。
这不适用于
std::string randomString = "Hello, World. I'm blue!";
std::cout << FBLU(randomString) << std::endl;
因为
FBLU(randomString)
经过预处理成为
KBLU randomString RST
其中(通过替换宏KBLU
和RST
)成为
"x1B[34m" randomString "x1B[0m"
现在,由于randomString
是一个标识符(在本例中为变量的名称),预处理器不再执行宏替换,并且
std::cout << FBLU(randomString) << std::endl;
被编译器视为
std::cout << "x1B[34m" randomString "x1B[0m" << std::endl;
这不是一个有效的陈述。
差异(取决于要FBLU()
的参数是字符串文本还是变量)是C++中积极不鼓励使用宏的众多原因之一。
可以使用各种替代方法,但基本准则是"不要使用宏"。
例如,更改头文件以将不带参数的宏转换为变量声明,例如
#include <string> // needed in the header since we're using std::string
/* FOREGROUND */
const std::string RST = "x1B[0m"; // RESET
const std::string KRED = "x1B[31m"; // RED
// etc
以及将带有参数的宏转换为内联函数
inline std::string FRED(const std::string &x)
{
return KRED + x + RST;
}
完成此操作后,您的两个示例都将按预期工作。
您正在将字符串与宏连接起来,宏不是函数。 不能使用变量调用宏。 例如,以下程序将打印"hello world">
#include <iostream>
using std::cout;
using std::endl;
int main() {
cout << "hello " "world" << endl;
}
见 https://wandbox.org/permlink/EbG5ZUgVfKLo9lEq
因此,当您"调用"宏时,经过预处理后,您的代码如下所示
std::cout << FBLU("Hello, World. I'm blue!") << std::endl;
std::cout << "x1B[34m" "Hello, World. I'm blue!" "x1B[0m" << std::endl;
实质上是在预处理时编译之前将宏的参数组合在一起。 因此,使用示例中的变量,您可以得到以下内容
std::cout << FBLU(randomString) << std::endl;
std::cout << "x1B[34m" randomString "x1B[0m" << std::endl;
这是格式不正确的C++,因为您无法将非字符串文字与这样的字符串文字连接起来。
请记住,宏只执行纯文本替换。
C++程序的编译涉及三个步骤:预处理、编译、链接。
所以,你看,预处理是第一位的。此时,预处理器不知道变量的内容,因此您的第二个代码片段展开为以下内容:
std::string randomString = "Hello, World. I'm blue!";
std::cout << "x1B[34m" randomString "x1B[0m" << std::endl;
这会产生语法错误。