I have a C++ program with many thousands of string literals in the code which need to be translated, for example:
statusBar->Print( "My Message" );
I wrapped the string literals with a function which looks up the value in a dictionary and returns the translated version:
statusBar->Print( Translated( "My Message" ) );
The problem is that after profiling I've discovered that doing this look up all over the code is a performance problem. What I'd like to do is change lines like that to:
static const char * translatedMessage5 = Translated( "My Message" );
statusBar->Print( translatedMessage5 );
But due to the many thousands of instances of this in the code, it's going to be error prone (and a bit of a maintenance nightmare). I was hoping that I could turn Translated into a macro which declared the static variable in-line. This obviously doesn't work. Anyone have a better idea?
I/O time needed to print your message should be several orders of magnitude more than any dictionary lookup time. If this is not the case, you are doing something wrong.
Tried and tested software is available that does what you need. I suggest you either study GNU Gettext, which is used by every other FOSS project out there, or just use it in your program instead of a homebrew solution.
EDIT: With C++0x it is possible to do what you want, but still consider using GNU Gettext as your real l10n engine. Here's some proof-of-concept little code:
#include <iostream>
const char* realTranslate(const char* txt)
{
std::cout << "*** translated " << txt << std::endl;
return txt; // use a real translation here such as gnu gettext
}
#define Translate(txt) \
(([]()->const char* \
{static const char* out = realTranslate(txt); return out;})())
int main ()
{
for (int i = 0; i < 10; ++i)
{
std::cout << Translate("This is a message") << std::endl;
std::cout << Translate("This is a message") << std::endl;
std::cout << Translate("This is another message") << std::endl;
}
}
I'm not sure what the real C++ standard is going to specify, but under gcc-4.6 the realTranslate() function is called 3 times.
Can you change to unique error codes and index them into vector? This simplifies the code and the lookup, and adding additional error messages becomes trivial. Also, ensures error messages added in this manner are more visible (externally to this application, for example -- could easily be published to a "User Guide" or similar).
#include <string>
#include <vector>
enum ErrorMessages
{
my_message,
my_other_message,
...
msg_high
};
std::vector<std::string> error_messages;
void init()
{
error_messages.resize(msg_high);
error_messages[my_msg] = "My Message";
error_messages[my_other_msg] = "My Other Message";
...
}
const char* const Translate(const ErrorMessage msg)
{
return error_messages[msg].c_str();
}
void f()
{
statusBar->Print(Translated(my_msg));
}
This might not help you here, but what you could do is declare a std::map that would hold a map of hash -> text pairs. The question here is if calculating hash code on a string will be same level of effort as translating it, and this I don't know.
char * Translate(char *source)
{
static std::map<int, char*> sources;
static std::map<int, char*> results;
int hashcode = CalculateHashCode(source);
std::map<int, char*>::const_iterator it = sources.find( source );
if ( it != sources.end() )
{
return results[ hashcode ];
}
... code to translate ...
results[ hashcode ] = translated;
}
Related
Suppose s is
a
b
c
const std::string s =
std::cout << R"( s )" << std::endl;
How to std::cout the content of the string in raw literal? I mean the cout return the value in this format: "a\nb\nc".
I need to transform a very large text into a std::string.
I cant use fileread as i need to define its value inside the src.
What you would need to do is to scan the string, and replace all occurrences of the characters you are interested in (such as carriage return, tab, etc) with printable escape sequence and than print this new text.
Here is somewhat crude proof of concept:
std::string escape(std::string_view src) {
std::string ret;
ret.reserve(src.size() * 2); // at worst, the string consists solely of escapable symbols
static constexpr std::array escapable = {std::make_pair('\t', 't'),
std::make_pair('\n', 'n')}; // add more chars as needed, note that the array is sorted
for (const char ch: src) {
std::pair search_pair{ch, ' '};
auto esc_char = std::equal_range(escapable.begin(), escapable.end(), search_pair, [](auto& a, auto& b) { return a.first < b.first; });
if (esc_char.first != escapable.end()) {
ret.push_back('\\');
ret.push_back(esc_char.first->second);
} else {
ret.push_back(ch);
}
}
return ret;
}
Now, you can use it:
const std::string str = "A\nbub\tfuf\n";
std::cout << escape(str) << "\n";
Above snippet prints A\nbub\tfuf\n
You could be interested by the JSON specification.
You could consider serializing your data in JSON format using open source C++ libraries like jsoncpp
You could also consider using some YAML format with the yaml-cpp library
You could be interested by the SWIG tool which generates C++ glue code.
You could consider using binary data formats like XDR.
You should specify (on paper, with a pencil) your data format in EBNF notation and use ANTLR or GNU bison to generate the parser (the printer is easier to code)
The RefPerSys project (an open source symbolic artificial intelligence system, GPLv3+ licensed) is persisting data in textual format. You may borrow some code are re-use it in your application, if you obey to that GPL license.
Look also into Qt or POCO frameworks, but notice that DWORD64 is not a standard C++ type. See this C++ reference and read a recent C++ standard (like n3337 or better).
Consider generating your C++ serializing code
With tools like GNU m4 or GPP (or your own one).
Pitrat's book Artificial Beings: the Conscience of a Conscious Machine (ISBN-13: 978-1848211018) should give you valuable insight and intuitions.
You can load this text file into a std::string like this:
Store the text in your file, e.g. mystring.txt, as a raw string literal in the format R"(raw_characters)":
R"(Run.M128A XmmRegisters[16];
BYTE Reserved4[96];", Run.CONTEXT64 := " DWORD64 P1Home;
DWORD64 P2Home;
...
)"
#include the file into a string:
namespace
{
const std::string mystring =
#include "mystring.txt"
;
}
Your IDE might flag this up as a syntax error, but it isn't. What you're doing is loading the contents of file directly into the string at compile time.
Finally print the string:
std::cout << mystring << std::endl;
Why not just save the escaped version of the string in the file?
Any way, here's a function to 'escape' characters:
#include <iostream>
#include <string>
#include <unordered_map>
std::string replace_all(const std::string &mystring)
{
const std::unordered_map<char, std::string> lookup =
{ {'\n', "\\n"}, {'\t', "\\t"}, {'"', "\\\""} };
std::string new_string;
new_string.reserve(mystring.length() * 2);
for (auto c : mystring)
{
auto it = lookup.find(c);
if (it != lookup.end())
new_string += it->second;
else
new_string += c;
}
return new_string;
}
int main() {
std::string mystring = R"(Run.M128A XmmRegisters[16];
BYTE Reserved4[96];", Run.CONTEXT64 := " DWORD64 P1Home;
DWORD64 P2Home;
DWORD64 P3Home;
DWORD64 P4Home;
DWORD64 P5Home;
DWORD64 P6Home;)";
auto new_string = replace_all(mystring);
std::cout << new_string << std::endl;
return 0;
}
Here's a demo.
Problem Description:
I have created a custom C++ function print() that is supposed to be pushed as a global onto a table to so the user can use the print() function to print to the debug console. This function works to some extent, however, when you try to print a string with a space in it (over one word) the function is not called at all... This has greatly confused me, as I don't know why. If I were to try and call something such as print("Hello!") the console will have "Hello!" printed to it, but if I were to try and print something such as print("Hello world!") the function will not be called at all, I know this because I have used a message box to alert when the function is called.
Additional Information:
So, the closest thing to this I could find was a question asking how to make a custom print function in C++ with the Lua C API then push it onto the global table. I can already do this, and my function works to some extent. My function isn't being pushed onto the Lua C API's global table, instead to a table that is created by me with lua_newtable(L, s);. However, I've tried it both ways and it makes no difference. This print function does not support tables nor function as of now, I'm just focused on finding out and fixing why the function can't print strings over one word. Just in case you were wondering, Lua v5.1.5 and Microsoft Visual Studio 2017 are used for this. Debug mode, x86.
Code (C++):
If anyone could help me fix this, that would be great!
#include <iostream>
#include <string>
#include <Windows.h>
#pragma comment(lib, "Lua.lib")
#include "lua.hpp"
#include "luaconf.h"
static int print(lua_State* LUASTATE)
{
MessageBoxA(NULL, "Custom print called.", "FUNCTION!", NULL);
int nargs = lua_gettop(LUASTATE);
std::string string = "";
for (int i = 1; i <= nargs; i++)
{
if (i > 1) string += " ";
switch (lua_type(LUASTATE, i))
{
case LUA_TSTRING:
string += (std::string)lua_tostring(LUASTATE, i);
case LUA_TNUMBER:
string += (int)lua_tonumber(LUASTATE, i);
case LUA_TBOOLEAN:
string += (bool)lua_toboolean(LUASTATE, i);
}
}
std::cout << string << "\n";
return 0;
}
int pushs(lua_State* LuaState)
{
luaL_openlibs(LuaState);
lua_newtable(LuaState);
lua_pushcfunction(LuaState, print);
lua_setglobal(LuaState, "print");
lua_settop(LuaState, 0);
return 0;
}
int main()
{
lua_State* ls = luaL_newstate();
lua_State* LS = lua_newthread(ls);
pushs(LS);
while (true)
{
std::cout << " ";
std::string inputo;
std::cin >> inputo;
luaL_dostring(LS, inputo.c_str());
lua_settop(LS, 0);
}
lua_close(LS);
return 0;
}
Main problem
std::cin >> inputo does not read a full line from the standard input. It just reads a single word. So when you type the following input line in your shell:
print("Hello world")
Your main loop breaks it into two separate strings:
print("Hello
world")
And these string are evaluated independently by the Lua interpreter. None of these strings are valid Lua statements, so the interpreter doesn't execute them. lua_dostring will return an error code, and let an error message on the Lua stack.
To work line by line on the standard input, you can use std::getline, which works well in a loop:
std::string line;
while (std::getline(std::cin, line)) {
// do something with line.
}
Side notes
What follows is not directly related to your bug, but look suspicious:
std::string += int (or bool) interprets the int as a single char, and append this single character to the string.
Your switch/case seems to be missing break statements.
lua_State* ls is never closed.
I'm going through the process of learning c++, so I'm making a few programs/tools to do certain easy operations on the computer. In this example, I'm creating a program that will locate browsers on the computer (it will be used to clear browser cookies etc.). There is probably more advanced ways to do this more effieciently, but I'm trying to keep this as simple as possible at the moment.
So far, I'm trying to find out if the directory "C:\Program Files (x86)\Google\Chrome" exist. I get the address to the program files directory by using getenv ("Program Files (x86)", but how do I add the rest of the address after?
I can't use the + operator for concatenation, since the variable is const char * (bool PathIsDirectory() requires const char * as parameter).
std::cout << "Searching for browsers..." << std::endl;
const char *chromePath;
chromePath = getenv ("ProgramFiles(x86)");
bool result = PathIsDirectory(chromePath);
if(result == true)
{
std::cout << "-- Google Chrome - FOUND" << std::endl;
}
else
{
std::cout << "-- Google Chrome - NOT FOUND" << std::endl;
}
You can store the result of getenv() in a std::string object (as mentioned in
the comments). And then you can add the rest of the path using the + operator like this:
#include <string>
//...
std::string chromePath = getenv ("ProgramFiles(x86)");
chromePath += "\\remaining\\path";
bool result = PathIsDirectory(chromePath.c_str());
Note that you'll have to escape the backslashes as shown above.
Assume I have a C++ string /dev/class/xyz1/device/vendor/config. As a part of my work, I am required to remove substrings "device" and "config" from the above string.
I know I can accomplish it by using "erase" call twice. But, I was wondering if this can be achieved in a single call. Any string class library call or boost call to achieve this?
Other than Regular Expressions, I'm not aware of any other method.
However think about why you want to do this. Just because it's a single call won't make it "alot" faster, as the code still needs to be executed one way or the other.
On the other hand, having a command for each word would increase code-readability, which always should be high-priority.
If you need this often and want to save lines, you could however easily write such a function yourself, and put it into a library of your custom utility functions. The function could take the input string and a std::vector for strings or any other form of string-collection to remove from the prior.
It's not entirely clear how specific the algorithm should be. But, for the case given, the following would have minimum copying and do the mutation "atomically" (as in: either both or no substrings removed):
namespace ba = boost::algorithm;
void mutate(std::string& the_string) {
if (ba::ends_with(the_string, "/config")) {
auto pos = the_string.find("/device/");
if (std::string::npos != pos) {
the_string.resize(the_string.size() - 7); // cut `/config`
the_string.erase(pos, 7); // cut `/device`
}
}
}
See it Live On Coliru
#include <boost/algorithm/string.hpp>
namespace ba = boost::algorithm;
void mutate(std::string& the_string) {
if (ba::ends_with(the_string, "/config")) {
auto pos = the_string.find("/device/");
if (std::string::npos != pos) {
the_string.resize(the_string.size() - 7); // cut `/config`
the_string.erase(pos, 7); // cut `/device`
}
}
}
#include <iostream>
int main() {
std::string s = "/dev/class/xyz1/device/vendor/config";
std::cout << "before: " << s << "\n";
mutate(s);
std::cout << "mutated: " << s << "\n";
}
Prints
before: /dev/class/xyz1/device/vendor/config
mutated: /dev/class/xyz1/vendor
I need to print the name of the called functions of a program using gcc plugins
for this I created a pass that will be called after ssa pass, I already initiated the plugin and I can loop on its statements, using a gimple_stmt_iterator :
int read_calls(){
unsigned i;
const_tree str, op;
basic_block bb;
gimple stmt;
tree fnt;
FOR_EACH_BB_FN(bb, cfun) {
gimple_stmt_iterator gsi;
for (gsi=gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi))
{
stmt = gsi_stmt(gsi);
if (is_gimple_call(stmt)){
const char* name = THE_FUNCTION_I_NEED(stmt);
cerr << " Function : " << name << " is called \n";
}
}
}
return 0;
}
How can I print the name of the called function using its gimple node ??
Can I also print other informations like the line number where it was called, the name of the function where it was called etc .. ?
I've been looking for the answer for hours, the answer is actually pretty easy :
get_name(tree node)... I've been trying many functions since the documentation is really poor... I found it here :
GCC Middle and Back End API Reference
As you can see, there is no comments about what the functions does, and it quit the best documentation I found about gcc, anyway get_name(..) is working fine, bit I haven't find how to print the source line yet
I know three ways:
1:
tree current_fn_decl = gimple_call_fndecl(stmt);
const char* name = function_name(DECL_STRUCT_FUNCTION(current_fn_decl);
2:
const char* name = IDENTIFIER_POINTER(DECL_NAME(current_fn_decl));
3:
tree current_fn_decl = gimple_call_fndecl(stmt);
const char* name = get_name(current_fn_decl);