Help me making a small change in a C++ program
- Inicie sesión o regístrese para enviar comentarios
Read here:
http://stackoverflow.com/questions/33220415/modifying-virtualbox-gui-to-use-kchmviewer-if-available
There are two mistakes in the provided link that I'm currently aware of:
1. I cannot use #define in my statement, however, I'm somewhat confused about which (int, const, float, QString, QList, etc.) combination of variable/data type should I choose, and why.
2. Instead of setting USE_KCHMVIEWER to "0", I actually have to undefine it (how?).
I would be grateful if someone could help fix the code, and pointed to the file/location where it should be placed.
Thanks in advance.
Peter
What you're trying to do isn't possible. #define is a preprocessor command, executed at compile time. Any instances of USE_KCHMVIEWER get replaced in the code, before it is compiled, with the value of USE_KCHMVIEWER. So there isn't a way to change such constants at run-time, and "undefining" them doesn't make any sense.
What you need to do is modify the code to remove USE_KCHMVIEWER from the relevant decision(s). Probably, it should be removed entirely and replaced with a variable. I'm not familiar with VirtualBox, so I don't know how difficult this is. But the type you would want to use is bool.
So, you're telling me that C++, a powerful programming language that's powering operating systems and state-of-the-art games, isn't powerful enough to define a simple conditional statement? Even a shell script (dash) can do this:
#!/bin/sh which kchmviewer > /dev/null if [ $? = 0 ] then # "which kchmviewer" exit code 0 USE_KCHMVIEWER=1 else # "which kchmviewer" exit code 1 USE_KCHMVIEWER= fi if [ "$USE_KCHMVIEWER" = '' ] then echo kchmviewer IS NOT installed. else echo kchmviewer IS installed. fi
USE_KCHMVIEWER is not used in the code, however, I would like to replace VBOX_OSE with it in the mentioned cases.
What do you mean by that USE_KCHMVIEWER isn't a variable? I understand that VBOX_OSE (some of whose occurrences I want to replace with USE_KCHMVIEWER) is a compile-time option. Now, I just want to create a code that could define this at run-time instead of compile-time, and then put it somewhere into the codebase. The expected result should be that whenever I access the documentation inside the GUI, either the CHM or PDF file will be opened based on what document viewer is available at the moment (kind of a OSE/non-OSE hybrid*).
So you're basically saying that variables (or whatever USE_KCHMVIEWER is) used in preprocessor statements (the lines with a hashtag) cannot be changed/redefined once the program is compiled because they are replaced by their value (i.e. they are hardcoded). What would be the preferred way to work around this (i.e. what should I use instead of # ifdef USE_KCHMVIEWER, and # if !defined USE_KCHMVIEWER)?
* https://forums.virtualbox.org/viewtopic.php?f=10&t=73979#p342127
He's telling you that this is of course possible if USE_KCHMVIEWER is a variable, but at the moment, it's not.
The compiler won't no about USE_KCHMVIEWER at all, because before it starts to compile, the so called "preprocessor" already replaced every single chain of characters that are "USE_KCHMVIEWER" with whatever you have defined in #define.
if you defined it to be "3", then the compiler will just find "3" in the source code.
int USE_KCHMVIEWER
becomes (after preprocessing and BEFORE compiling)
int 3
that's what the compiler gets.
I understood that part, I just don't understand C++ yet. The main problem is that I'm unable to test the changed code before the compilation, only after. Together this means that it's very difficult for me to work on the code.
> So, you're telling me that C++, a powerful programming language that's powering operating systems and state-of-the-art games, isn't powerful enough to define a simple conditional statement?
No, I'm telling you that C++ is a compiled language, and #define (and other statements starting with a hash) is not even C++ code, but a C++ preprocessor directive which is executed at compile-time. It's simply not possible to use code executed at compile-time to do something at run-time.
> What do you mean by that USE_KCHMVIEWER isn't a variable?
It's a preprocessor-defined constant. This is not the same as a variable, and as I said, it's not technically possible to change it after the program has been compiled.
For example, if you have this:
#define FOO ((5 + 3) / 2)
Then the preprocessor goes through all the relevant C++ code before compiling and replaces all instances of "FOO" with "((5 + 3) / 2)". So for example, this:
int x = FOO;
becomes this:
int x = ((5 + 3) / 2);
So a compiled binary never sees that "FOO" constant. It only sees "((5 + 3) / 2)".
> What would be the preferred way to work around this (i.e. what should I use instead of # ifdef USE_KCHMVIEWER, and # if !defined USE_KCHMVIEWER)?
As I said, use variables. (Or, depending on the specifics, a constant defined with the const keyword). It needs to be all based on C++ code, not preprocessor directives, because preprocessor directives are all handled at compile-time.
Thank you. I'm going to use this information to make some tests and will report back once I get something useful.
I replaced #define with bool as suggested (note: why this doesn't work?)
if (system("which kchmviewer > /dev/null") == 0) { // "which kchmviewer" exit code 0 bool useKchmviewer = true; } else { // "which kchmviewer" exit code 1 bool useKchmviewer = false; }
but the compilation was terminated with the following message (screenshot):
warning: unused variable ‘useKchmviewer’ [-Wunused-variable]
Based on what I read on the Internet (I need to declare a variable first, then I can change the value later), I tried a different approach (note: while this seems to work, I would also like to know how to do this, if possible, using an if…else statement as pointed out above):
bool useKchmviewer = false; if (system("which kchmviewer > /dev/null") == 0) { // "which kchmviewer" exit code 0 useKchmviewer = true; }
This worked, however once I tried to use this with
if (!useKchmviewer) { const QString name = "UserManual"; const QString suffix = "pdf"; } else { const QString name = "VirtualBox"; const QString suffix = "chm"; }
the compiler was unhappy again (screenshot). Based on the answer from Stack Overflow, I altered this code too (note: why is this necessary?):
QString name = "UserManual"; QString suffix = "pdf"; if (useKchmviewer) { name = "VirtualBox"; suffix = "chm"; }
After this, the compilation finished successfully and the changes worked as expected. However, I still need to adapt other parts of the code so this is only a partial success.
P.S.: I understand how frustrating can be to help a person that doesn't have any knowledge in the topic he/she's expecting to get help in, but please bear with me.
warning: unused variable ‘useKchmviewer’ [-Wunused-variable]
That is not an error. Only a warning. The code was compiling successfully. The warning is telling you that the Boolean you defined is not used: you write in the variable "useKchmviewer" but never read from it.
I only mentioned the relevant message there (see the attached screenshot above). The problem is with the code that's following right after, that sets the variables for the filename of the document file, that will be opened by the frontend. If those variables aren't set, the compilation terminates with an error.
Finally, I'm in the stage where my changes are working properly. There's one important thing left to do before they will be usable. I would like to know where should I place
bool useKchmviewer = false; if (system("which kchmviewer > /dev/null") == 0) { // "which kchmviewer" exit code 0 useKchmviewer = true; }
in the program codebase so I can check for variable useKchmviewer from wherever I want, specifically VirtualBox-5.0.6/src/VBox/Frontends/VirtualBox/src/globals/UIMessageCenter.cpp and VBoxGlobal.cpp. How can I do this?
Virtualbox is non-free since 4.2. :-/
However there are alternatives.
VirtualBox is under GPLv2 besides the Extension Pack, which is under PUEL. The main problem with it is that some parts require non-free compilers, tools, and operating systems to be built. I'm not using these parts so this doesn't affect me.
Just a simplification:
bool useKchmviewer = system("which kchmviewer > /dev/null") == 0;
Thanks, this'll sure come handy. I'm gonna test it right away.
On the subject, do you also know how to redirect the output of "which kchmviewer" to /dev/null if called through QProcess::execute() instead of system()? I recieved a suggestion on Stack Overflow to use it instead, but this doesn't seem to work:
if (QProcess::execute("which kchmviewer > /dev/null") == 0) …
Thanks in advance.
Works like a charm. Thanks!
- Inicie sesión o regístrese para enviar comentarios