I was working on a small application that I'm building a simple CLI for. I wanted my CLI to have some colors because who likes boring old white and black consoles?? (jk no offense if you do:))
Then as I was building onto it I seem to have run into a problem that I, unfortunately, don't understand :(. The problem lies in some borrowed code that is supposed to help me clean the code by wrapping all the necessary code into neat little functions or definitions (you'll see what I'm talking about soon). I don't really know how definitions work in C++ or at least the more advanced ones but here is the code I currently have for wrapping the CLI color code functions into:
Colors.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_ */
So the problem is that this below works:
std::cout << FBLU("Hello, World. I'm blue!") << std::endl;
and this doesn't...
std::string randomString = "Hello, World. I'm blue!";
std::cout << FBLU(randomString) << std::endl;
Again, I'm not too familiar with how "defined functions" work but I was just wondering if anybody could possibly show me a new method that accepts both the raw text input and a variable input to the defined functions. Also if you could help me understand how definitions work more in C++ that would be awesome as well.
Macros work the same way as if you copy-pasted the macro definition to the place where it's used.
So this works:
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++ has a rule whereby adjacent string literals get joined together. I imagine this rule was added so that you can do exactly what you're doing here)
And this doesn't work:
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
You are concatenating strings with macros, macros are not functions. You can't invoke macros with variables. For example the following program will print "hello world"
#include <iostream>
using std::cout;
using std::endl;
int main() {
cout << "hello " "world" << endl;
}
See https://wandbox.org/permlink/EbG5ZUgVfKLo9lEq
So when you "invoke" the macro, after preprocessing your code looks like this
std::cout << FBLU("Hello, World. I'm blue!") << std::endl;
std::cout << "\x1B[34m" "Hello, World. I'm blue!" "\x1B[0m" << std::endl;
You are essentially combining the arguments to the macro together before compilation at preprocessing time. So with variables in your example you get the following
std::cout << FBLU(randomString) << std::endl;
std::cout << "\x1B[34m" randomString "\x1B[0m" << std::endl;
Which is ill formed C++, since you can't concatenate non string literals with string literals like that.
Remember that macros do nothing but plain text replacement.
The difference is because FBLU
is a preprocessor macro (and NOT a function) that works as you expect ONLY if its parameter is a string literal. The preprocessor does TEXT substitution to produce source code - which is passed to a later phase of compilation.
The preprocessor will turn
FBLU("Hello, World. I'm blue!")
into
KBLU "Hello, World. I'm blue!" RST
which (by substituting the macros KBLU
and RST
) becomes
"\x1B[34m" "Hello, World. I'm blue!" "\x1B[0m"
which is a set of string literals, which get appended (again by the preprocessor) to become a single string literal
"\x1B[34mHello, World. I'm blue!\x1B[0m"
The net effect is that
std::cout << FBLU("Hello, World. I'm blue!") << std::endl;
is seen by the compiler as
std::cout << "\x1B[34mHello, World. I'm blue!\x1B[0m" << std::endl;
which is a perfectly valid code statement.
This doesn't work for
std::string randomString = "Hello, World. I'm blue!";
std::cout << FBLU(randomString) << std::endl;
since
FBLU(randomString)
is preprocessed to become
KBLU randomString RST
which (by substituting the macros KBLU
and RST
) becomes
"\x1B[34m" randomString "\x1B[0m"
Now, since randomString
is an identifier (name of a variable, in this case), the preprocessor does no further macro substitution, and
std::cout << FBLU(randomString) << std::endl;
is seen by the compiler as
std::cout << "\x1B[34m" randomString "\x1B[0m" << std::endl;
which is not a valid statement.
The difference (depending on whether the argument to FBLU()
is a string literal or a variable) is one of many reasons that usage of macros is actively discouraged in C++.
There are various alternatives that can be used instead, but the essential guideline is "Don't use macros".
For example, change the header files to convert the macros with no arguments into variable declarations, such as
#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
and the macros with arguments into inline functions
inline std::string FRED(const std::string &x)
{
return KRED + x + RST;
}
After doing this, both of your examples will work as expected.
The compilation of a C++ program involves three steps: preprocessing, compilation, linking.
So, you see, preprocessing goes first. At that time content of variable is not known to preprocessor, so your second code snippet unrolls to the following:
std::string randomString = "Hello, World. I'm blue!";
std::cout << "\x1B[34m" randomString "\x1B[0m" << std::endl;
which produces syntax error.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.