4

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?

6
  • 1
    How is Translated implemented? Are you using an efficient hash table implementation? Commented Jul 12, 2011 at 19:20
  • @Brian - I don't say its a solution, but how did you've implemented your dictionary? Does it use most appropriate data structure for look-up (e.g. boost::unorded_map) Commented Jul 12, 2011 at 19:22
  • Because Translated() obviously takes a string, even an efficient hash has to hash the entire string which is very costly. Commented Jul 12, 2011 at 19:24
  • Yes, that was the first thing I tried. Because the table is fixed in size, I allocate it as a single large array with the key string, value string, and "next" index. Then there's a hash array which points to the first index for each hash key. Most of the time is spent actually calculating the hash key. Commented Jul 12, 2011 at 19:28
  • 1
    Does your application support changing the language at run-time, or is it fixed at startup? If the former, caching the translated string on first invocation of the function is a Really Bad Idea (tm)! Commented Jul 12, 2011 at 19:35

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

+1, gettext is the tool to go, if the OP wants i18n for his program. Even if he wants to write his own solution he should study the gettext manual to see that in some cases (plural forms) it is not enough to do a simple dictionary look up.
The C++0x lambda version is awesome. I hadn't realized that's possible. FWIW the real problem is not one of localization, but I used that as my example as it's easier to grok.
3

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));
}

5 Comments

enum values don't have a namespace, you might want to fix the last line.
@Andre: They're valid in MSVC and C++0x.
This is most likely what I'm going to have to do. I was trying to avoid doing this though because of having to convert many thousands of lines of already existing code (not just a lot of work, but error prone as well). Additionally it's going to be hard to determine and remove unused messages.
Look at it as a benefit -- you have the ability to rationalize all of your existing error messages, and put them in an easily exportable location. Also, if "Translation" is ever something that needs to be done (like language translation support), it becomes trivial to support other languages by simply changing the error messages and leaving the rest of the code in place as-is.
@DeadMG: I assume C++03 unless stated otherwise.
0

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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.