Macros in system calls - c++

In c++, is it possible and safe to use macros in system calls? Take the following code for example:
#define WINX 54
#define WINY 30
int main()
{
system("mode con lines=WINY cols=WINX");
...
Would that work and be safe to use in code? Or would I have to manually construct a string?

A macro will not expand inside a string literal. Instead, you can use another macro to expand a macro into a string literal, and use string literal concatenation to create the desired string:
#define STR2(x) STR(x)
#define STR(x) #x
const char *cmd = "mode con lines=" STR2(WINY) " cols=" STR2(WINX);
system(cmd);
STR2 expands the provided argument (e.g. WINY) into what it is defined to be and then passes it to STR. STR just uses the stringifying macro operator, and its result is a string literal. Adjacent string literals are concatenated into a single string by the compiler before the code is tokenized and compiled into object code.
If the macros are something more complex than simple numbers, then you need to manually construct a string. In C++, the easiest way is to use ostringstream (from <sstream>):
std::ostringstream oss;
oss << "mode con lines=" << WINY << " cols=" << WINX;
system(oss.str().c_str());

Macros certainly don't expand in strings. So, this
system("mode con lines=WINY cols=WINX");
won't expand into
system("mode con lines=30 cols=54");

If you don't need the actual decimal value of WINX and WINY, you can concatenate static strings and save resources during execution time:
#define WINX "54"
#define WINY "30"
int main()
{
system("mode con lines=" WINY " cols=" WINX);

You'll have to manually construct a string.
For example:
char command[100];
int result;
sprintf(command, "mode con lines=%d cols=%d", WINY, WINX);
result = system(command);
Don't forget the required include directives:
#include <stdio.h>
#include <stdlib.h>
Consult your system's documentation for the meaning of the value returned by system.
Make sure the command array is big enough to hold the full command -- or, probably better, use std::string instead. jxh's answer shows how to do this.

Related

How to call macro that uses token pasting?

I am trying to print ffmpeg version in a C++ program. I see that in the /libavutil/version.h there is AV_VERSION which should tell the version number in the format x.x.x.
As a test I used some random numbers as function parameters like this: std::string version = AV_VERSION(3,4,2);. The same error I get if I use LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR and LIBAVUTIL_VERSION_MICRO from the file. That was actually my first try to print the version number.
The error I get is invalid suffix '.2' on floating constant or invalid suffix '.101' on floating constant if I try to print std::cout << AV_VERSION(LIBAVUTIL_VERSION_MAJOR,LIBAVUTIL_VERSION_MINOR,LIBAVUTIL_VERSION_MICRO) << std::endl;
I do understand that the preprocessor is thinking that the token is a float, hence the error. How do you actually use this type of macro funtion?
That macro is in the file I mentioned above, so it must be a way to call that macro function without giving an error, thinking that is a mature library, and I guess other libraries use something similar for printing version number.
Here is how AV_VERSION is defined in the header file and how I call it:
#define AV_VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
#define AV_VERSION_DOT(a, b, c) a ##.## b ##.## c
#define AV_VERSION(a, b, c) AV_VERSION_DOT(a, b, c)
#define AV_VERSION_MAJOR(a) ((a) >> 16)
#define AV_VERSION_MINOR(a) (((a) & 0x00FF00) >> 8)
#define AV_VERSION_MICRO(a) ((a) & 0xFF)
#define LIBAVUTIL_VERSION_MAJOR 57
#define LIBAVUTIL_VERSION_MINOR 9
#define LIBAVUTIL_VERSION_MICRO 101
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
LIBAVUTIL_VERSION_MINOR, \
LIBAVUTIL_VERSION_MICRO)
#define LIBAVUTIL_VERSION AV_VERSION(LIBAVUTIL_VERSION_MAJOR, \
LIBAVUTIL_VERSION_MINOR, \
LIBAVUTIL_VERSION_MICRO)
int main()
{
std::string version = AV_VERSION(3,4,2);
std::cout << AV_VERSION(LIBAVUTIL_VERSION_MAJOR,LIBAVUTIL_VERSION_MINOR,LIBAVUTIL_VERSION_MICRO) << std::endl;
return 0;
}
I coud've skip this error but as I'm trying to learn C++ I am pretty sure that I will find more of this type of macros so no point to avoid learning them now as I'm facing them.
Thanks in advance!
You need to use a stringize expansion. Because of how the preprocessor works, this involves two macros:
#define STR(x) #x
#define XSTR(x) STR(x)
The macro STR will take whatever parameter you give it and make that a string literal.
The macro XSTR will first expand its parameter x and the result will be the parameter to STR.
To illustrate:
STR(LIBAVUTIL_VERSION) will give "LIBAVUTIL_VERSION"
XSTR(LIBAVUTIL_VERSION) will give "57.9.101"
Demo according to your code:
int main()
{
std::string version1 = XSTR(LIBAVUTIL_VERSION);
std::string version2 = XSTR(AV_VERSION(3,4,2));
std::cout << version1 << "\n";
std::cout << version2 << "\n";
return 0;
}
Output:
57.9.101
3.4.2

serial stream and defined variables

Hopefully an easy question from the newb...
If I have a defined variable:
#define LOCK_OUT 0xA00A
and I use it in a serial stream like this:
iprintf("%s Sent to the system. \r\n", LOCK_OUT);
on my serial terminal, will I see the text representation of LOCK_OUT or the numeric representation? For instance:
"LOCK_OUT Sent to the system."
in particular, I have a loop that checks an array of defined macros, and I want to use the selected macro (once found) in the output serial stream. so this would be the loop:
int UDPDATA;
for (int i = 0, i < UDP_Size; i++)
{
if (MACRO_ARRAY[i] == UDPDATA iprintf("%s Sent to system \r\n", MACRO_ARRAY[i]);
}
and I want the macro name sent to the serial stream, not the value it represents. I hope I'm explaining this correctly...
vs
"0xA00A Sent to the system."
I'm looking to do the first, not sure if it's possible... Thanks!
Because, macros are substitutions, you would have to do something like this:
#include <stdio.h>
#define STRINGIFY(x) #x
#define LOCK_OUT 0xA00A
int main() {
// %#04x prints 4 hexadecimal places
printf("%s %#04x Sent to the system. \r\n", STRINGIFY(LOCK_OUT), LOCK_OUT);
return 0;
}
Nope, what #define macro does is pure substitution without semantic parsing.
If you define LOCK_OUT as 0xA00A, the compiler will replace LOCK_OUT with 0xA00A first, and then interprete it as a integer value.

Macro long string concernation

In my application I want to add the version ID as a macro and use it in multiple parts of the application. As explained in this question I can easily generate a string with this:
#define APP_VER "1.0"
#define APP_CAPTION "Stackoverflow example app v." ## APP_VER
My problem is now, that in some parts, I need to have the caption as an unicode string.
I tried the following:
MessageBoxW(0,_T(APP_CAPTION),L"Minimal Counterexample",0);
But it gives the error "can't concernate wide 'Stackoverflow example app v.' with narrow '1.0'"
I also tried
#define WIDE_CAPTION L ## APP_CAPTION
But that just gives "LAPP_CAPTION" is not defined.
I know that I can convert the string at runtime to unicode, but that is rather messy. Can someone provide a Macro-level solution for my problem?
You just want:
#define APP_CAPTION "Stackoverflow example app v." APP_VER
Since APP_VER is already a string.
String concatenation happens for free, for example:
const char *str = "hello " "world"
Complete compilable example:
#include <iostream>
#define APP_VER "1.0"
#define APP_CAPTION "Stackoverflow example app v." APP_VER
int main() {
std::cout << APP_CAPTION << "\n";
return 0;
}

formatting a string which contains quotation marks

I am having problem formatting a string which contains quotationmarks.
For example, I got this std::string: server/register?json={"id"="monkey"}
This string needs to have the four quotation marks replaced by \", because it will be used as a c_str() for another function.
How does one do this the best way on this string?
{"id"="monkey"}
EDIT: I need a solution which uses STL libraries only, preferably only with String.h. I have confirmed I need to replace " with \".
EDIT2: Nvm, found the bug in the framework
it is perfectly legal to have the '"' char in a C-string. So the short answer is that you need to do nothing. Escaping the quotes is only required when typing in the source code
std::string str("server/register?json={\"id\"=\"monkey\"}")
my_c_function(str.c_str());// Nothing to do here
However, in general if you want to replace a substring by an other, use boost string algorithms.
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
int main(int, char**)
{
std::string str = "Hello world";
boost::algorithm::replace_all(str, "o", "a"); //modifies str
std::string str2 = boost::algorithm::replace_all_copy(str, "ll", "xy"); //doesn't modify str
std::cout << str << " - " << str2 << std::endl;
}
// Displays : Hella warld - Hexya warld
If you std::string contains server/register?json={"id"="monkey"}, there's no need to replace anything, as it will already be correctly formatted.
The only place you would need this is if you hard-coded the string and assigned it manually. But then, you can just replace the quotes manually.

How to Replace only Part of the Variable using #define

#define C_TX_ TX_
#define C_RX_ RX_
enum Test
{
C_TX_MAC = 0x0100, // Pre-Processor should replace C_TX_ to TX_
C_RX_MAC = 0x0101 // But Not Working.
};
int main(int argc, char *argv[])
{
cout << TX_MAC; // HOW TO PRINT ?
cout << RX_MAC; // HOW TO PRINT ?
return true;
}
The pre-processor only operates on strings that are entire tokens. There would be chaos otherwise.
Try:
#define C_TX_MAC TX_MAC
#define C_RX_MAC RX_MAC
You cannot split a token with the pre-processor. You need to
#define C_RX_MAC RX_MAC
#define C_TX_MAC TX_MAC
(Of course there's ugly solutions such as adding a pre-pre-processing step:
sed s/C_ADDR_// x.cpp | g++ -x c++ -
But sed doesn't know about the context. It will replace strings e.g. cout << "And C_ADDR_RX = " with cout << "And RX = ".)
As stated in the other answers the pre-processor uses the whitespace to work out where the token is defined, and cannot replace it 'part way through". Perhaps you could try a "Find/Replace In Files" to rename the variables in your source code directly. Visual Studio's Find and Replace function can be used to replace any occurences in any folders/subfolders, or if you don't run with the Microsoft there's some other programs like NotePad++ that offer the same functionality. Both also support Regular Expressions for better targeted find/replace queries
The preprocessor replaces tokens, and C_TX_MAC is a full token.
However, you can achieve this fairly easily with some macro concatenation:
#include <iostream>
#define foo(x) C_ ## x
enum Test
{
C_TX_MAC = 0x0100, // Pre-Processor should replace C_TX_ to TX_
C_RX_MAC = 0x0101 // But Not Working.
};
int main()
{
std::cout << foo(TX_MAC) << ' ' << foo(RX_MAC) << '\n';
}
(live demo)
Easy. No need for sed, and no need for find-and-replace in your text editor.