Reputation: 883
I am developing an MFC application in which I have menus defined in .rc file. I have an requirement for removing of few menu items at run time which are defined in xml file.
The menu ids are stored as string in xml as like below
<exclusionmenu>ID_FILE_NEW</exclusionmenu>
<exclusionmenu>ID_FILE_OPEN</exclusionmenu>
From xml the menu ids are retrieved as string,
RemoveMenu function expects UINT (menu id),
How to convert the menu id string defined in xml to uint menu id
Note: This is not direct cstring to uint conversion, ID_FILE_NEW is macro and it has int value.
Upvotes: 0
Views: 1815
Reputation: 51395
The symbolic names for resource identifiers are defined in a header file, Resource.h by default. In source code and resource scripts, the symbolic names are substituted for their respective numeric values by the preprocessor. When compilation begins, the symbolic information is already gone.
To implement a scheme that uses symbolic names for configuration, you have to extract and preserve the mapping between symbolic names and resource identifiers for later use at runtime, or apply the mapping to your configuration files prior to deployment. The following is a list of potential options:
Use an associative container and populate it at application startup: An appropriate container would be std::map<std::string, unsigned int>
. Populating this container is conveniently performed using C++11's list initialization feature:
static std::map<std::string, unsigned int> IdMap = {
{"ID_FILE_NEW", ID_FILE_NEW},
{"ID_FILE_OPEN", ID_FILE_OPEN},
// ...
}
At runtime you can use this container to retrieve the resource identifier given its symbolic constant:
unsigned int GetId(const std::string& name) {
if (IdMap.find(name) == IdMap.end())
throw std::runtime_error("Unknown resource identifier.");
return IdMap[name];
}
The downside to this approach is that you have to keep IdMap
and the resources in sync. Whenever a resource is added, modified, or removed, the container contents must be updated to account for the changes made.
Parse Resource.h and store the mapping: The header file containing the symbolic identifier names has a fairly simple structure. Code lines that define a symbolic constant usually have the following layout:
\s* '#' \s* 'define' \s+ <name> \s+ <value> <comment>?
A parser to extract the mappings is not as difficult to implement as it may appear, and should be run at an appropriate time in the build process. Once the mapping has been extracted, it can be stored in a file of arbitrary format, for example an INI file. This file can either be deployed alongside the application, or compiled into the binary image as a resource. At application startup the contents are read back, and used to construct a mapping as described in the previous paragraph. In contrast to the previous solution, parsing the Resource.h file does not require manually updating the code when resources change.
Parse Resource.h and transform the configuration XML file: Like the previous solution this option also requires parsing of the Resource.h file. Using this information, the configuration XML file can then be transformed, substituting the symbolic names for their numeric counterparts prior to deployment. This, too, requires additional work. Once this is done, though, the process can be automated, and the results verified to maintain consistency. At runtime you can simply read the XML and have the numeric identifiers readily available.
Upvotes: 2
Reputation: 5132
The only way your scenario would work is when you distribute Resoutce.h with your application and you have logic to parse Resource.h at startup into a table containing ID_* names and their values.
Upvotes: 1
Reputation: 19622
You can't, the string form is 'lost' at compile time, it's a preprocessor token. You can store the string variations of the menu items: somewhere in your code, have std::map and fill it with values: menu_ids["ID_FILE_NEW"] = ID_FILE_NEW; Then you call RemoveMenu(menu_ids[string_from_xml]);
Upvotes: 1