Reputation: 3934
I am trying to write a function that behaves differently depending on the OS.
I got this code somewhere in my function:
#ifdef OS_WINDOWS
CONSOLE_SCREEN_BUFFER_INFO csbi;
int cols;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
#else
int cols;
#ifdef TIOCGSIZE
struct ttysize ts;
ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
cols = ts.ts_cols;
#elif defined(TIOCGWINSZ)
struct winsize ts;
ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
cols = ts.ws_col;
#endif // TIOCGSIZE
#endif // OS_WINDOWS
So, I want to get the number of columns, but that needs to be done in two different ways for Windows and Linux... I then want to continue working with the cols
variable. But I get variable 'cols' is uninitialized when used here
How can I "extract" the cols variable from the macro block?
Upvotes: 0
Views: 82
Reputation: 3934
Okay, thanks everyone. I changed a few more things and now it's working for me:
unsigned get_terminal_cols()
{
unsigned c = -1;
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
c = csbi.srWindow.Right - csbi.srWindow.Left - 3;
#else
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
c = w.ws_col -3;
#endif // _WIN32
return c;
}
Upvotes: 0
Reputation: 1759
When dealing with OS specific behavior, you'll notice that code is complicated and difficult to understand or maintain when such conditional code is included "inline" with the other, non conditional code.
One way to make this cleaner and clearer is to put conditional code outside the function, and choose among alternatives in a clearly separated function.
For example, let's say the concept is to get columns. Know that is different for each OS case, start by declaring a function to get columns:
int GetColumns();
Now, DEFINE the function depending on the operating system:
#ifdef OS_WINDOWS
int GetColumns()
{
int cols(0);
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
return cols;
}
#else
#ifdef TIOCGSIZE
int GetColumns()
{
int cols;
struct ttysize ts;
ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
cols = ts.ts_cols;
return cols;
}
#elif defined( TIOCGWINSZ )
int GetColumns()
{
int cols;
struct winsize ts;
ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
cols = ts.ws_col;
return cols;
}
#endif // elif
#endif // else to OS_WINDOWS
I have not reviewed all of your code within these blocks, but the point here is to separate these concepts to make the code clearer, cleaner and easier to use.
In your main function you now only need:
int cols = GetColumns();
What this does is acknowledge that you need a way to get columns, and that's it. It clearly separates the complications of getting the columns from the code that calls for it.
Now, the side effects of the various conditional code no longer corrupts the function you're writing. If there are complications, they are now isolated with the body of the various versions of GetColumns. The function "wraps" the issues of local variables, differences in function calls, inside the function.
This also makes it usable from elsewhere in the program without repeating conditional code.
Upvotes: 1
Reputation: 28573
What the compiler sees on Windows
CONSOLE_SCREEN_BUFFER_INFO csbi;
int cols; //This is the only place where `cols` is delcared!
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
What the compiler sees not on Windows
struct ttysize ts;
ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
cols = ts.ts_cols; //cols is not declared
or
struct winsize ts;
ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
cols = ts.ws_col; //cols is not declared
Note that there is no declaration of cols
in the non-Windows code.
A simple solution would be to move the decalration of cols
before the macro block.
int cols = 0; //or -1 or some other error value
#ifdef OS_WINDOWS
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
//...
As a note on preprocessor macros:
The preprocessor runs before the code is compiled, dealing with #include
, #define
(replaces text), #ifdef
(conditionally includes blocks of code), and so on. Editors and IDEs may show non-use blocks grayed out, but you can also get the preprocessor output seperately to look at (compiler flags depend on your compiler), and this may help you find why this is having errors.
Upvotes: 2