Click here to Skip to main content
15,881,882 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
Hello forum,

the good news is that this works:
C
// Header
typedef enum
{
    LanguageId_English  = 0,
    LanguageId_German,
    // Insert additional languages here
    LanguageId_Invalid
}LanguageIds;

// Source
typedef union Translation Translation;
union Translation
{
    struct
    {
        const char* English;
        const char* German;
        // Upon inserting more languages, update LanguageIds as well.
        //   LanguageId_Invalid will grow automatically.
        int id;
    }Structured;
    const char* Heap[LanguageId_Invalid];             // (1) Annoyance
};

// [Edit]
typedef enum
{
    // More values here
    NodeId_Languages,
    NodeId_EN,
    NodeId_DE
    // And still more values here
}NodeIds;
// [/Edit]

const Translation Translations[] = {
    // there are more entries here
    { "Languages", "Sprachen", NodeId_Languages },
    { "German", "Deutsch", NodeId_DE },
    { "English", "Englisch", NodeId_EN },
    /* Note the comma at the end of the previous line.
     * There are even more entries to this.
     * Even those whose last field doesn't originate
     * from the NodeIds enumeration.
     */
};

const char* GetTranslation( int id )
/* Even though every id in this example stems from the
 * NodeIds enumeration,
 * the same is not guaranteed for the entire program when finished.
 */
{
    int i;
    for(i = 0; i < sizeof(Translations)/sizeof(Translations[0]); i++)
    {
        Translation oneLine = Translations[i];
        if(oneLine.Structured.id == id)
        // Got the correct line.
        {
            if(_currentLanguageId >= LanguageId_Invalid) // Which is greater
                                                         // than all other language ids.
            {
                return("ErrUnknownLanguage");
            }
            return(oneLine.Heap[_currentLanguageId]);
        }
    }

    return("ErrNotTranslated");
}
[Edit]
This calls above code:
C
const char* output = GetTranslation(NodeId_DE);
I didn't mention but you already figured out that this shall translate an application's UI to different languages (the identifiers "LanguageIds" and "GetTranslation" were applicable hints). The function returns a const char* that can be printed. I'd like to keep it that way.
[/Edit]

I'd like to get rid of the dependency at (1). But my efforts to get (2) to work again did not succeed.

C
// (1)
const char** Heap;

// (2)
return( *(oneLine.Heap + _currentLanguageId));
What's the correct way of dereferencing (1) to get the correct const char* to be printf'd?
Posted
Updated 3-Jul-15 2:53am
v5
Comments
[no name] 2-Jul-15 9:14am    
How do we know it works? You haven't told us what it is supposed to do. If you are creating a dictionary of words in different languages I can't see it working.
lukeer 3-Jul-15 5:35am    
I updated my question. Feel free to point me at missing information.
CPallini 2-Jul-15 9:20am    
It is not clear what you want to do.
lukeer 3-Jul-15 5:35am    
I updated my question. Feel free to point me at missing information.
Richard MacCutchan 3-Jul-15 5:56am    
the good news is that this works:
I can't quite see how, from that rather convoluted code. What is the reason for the for loop in the GetTranslation function?

1 solution

Your programming sample is a very bad example of obfuscated function prone to error and confusion. I suggest you to receed from your intent and consider a more clear alternative, or it will be a nightmare in future revisions.
You are abusing of the sequence of translation pointers at beginning of your structure making appear the last as an array of translations.
Even worst is that there was a perfectly legal mode to achive what you are trying to do. Consider:
C++
typedef struct
{
	const char* Translations[LanguageId_Invalid];
	// Upon inserting more languages, update LanguageIds as well.
	//   LanguageId_Invalid will grow automatically.
	int id;
}Translation;

No more union, a simple structure with an array of translations as first field.

Now for the sake of clarification of the C concepts let analyze your software.
In the union you are declaring:
C++
const char** Heap;

That is a variable holding the address of a memory location where there is an array of pointers to char. Read it carefully!
When you try to access data with:
C++
return( *(oneLine.Heap + _currentLanguageId));

the compiler get the address pointed by Heap as the address of the array of char pointers, but it is the address of first string, get the first 4 bytes in that location and treat them as the address of the string. Then program crashes...
In plane words you're telling that Heap is the address of the array, but it is the array itself.
You must tell to the compiler that at Heap there is the array of pointers, your declaration must be:
C++
const char* Heap[];


More, using loops on repetitive tasks as translation is not the best way, it can slowdown your app. Could have been a better approach a direct access.
It's also inefficient to copy structures like in:
C++
Translation oneLine = Translations[i];

Its faster and more effective to use the addressed structure:
C++
const char* GetTranslation( int id )
{
    int i;
    for(i = 0; i < sizeof(Translations)/sizeof(Translations[0]); i++)
    {
        if(Translations[i].Structured.id == id)
        // Got the correct line.
        {
            if(_currentLanguageId >= LanguageId_Invalid) // Which is greater
                                                         // than all other language ids.
            {
                return("ErrUnknownLanguage");
            }
            return *(Translations[i].Heap + _currentLanguageId);
        }
    }
 
    return("ErrNotTranslated");
}


Finally this is my full version:
C++
// Header
typedef enum
{
    LanguageId_English  = 0,
    LanguageId_German,
    // Insert additional languages here
    LanguageId_Invalid
}LanguageIds;
 
// Source
typedef struct
{
	const char* Translations[LanguageId_Invalid];
	// Upon inserting more languages, update LanguageIds as well.
	//   LanguageId_Invalid will grow automatically.
	int id;
}Translation;

// [Edit]
typedef enum
{
    NodeId_Languages,
    NodeId_EN,
    NodeId_DE
}NodeIds;
// [/Edit]

const Translation Translations[] = {
    { {"Languages", "Sprachen"}, NodeId_Languages },
    { {"German", "Deutsch"}, NodeId_DE },
    { {"English", "Englisch"}, NodeId_EN },
};

const char* GetTranslation( int id )
{
    int i;
    for(i = 0; i < sizeof(Translations)/sizeof(Translations[0]); i++)
    {
        if(Translations[i].id == id)
        // Got the correct line.
        {
            if(_currentLanguageId >= LanguageId_Invalid) // Which is greater
                                                         // than all other language ids.
            {
                return("ErrUnknownLanguage");
            }
            return Translations[i].Translations[_currentLanguageId];
        }
    }
 
    return("ErrNotTranslated");
}

int main(int argc, char *argv[])
{
	printf("%s\n", GetTranslation(NodeId_DE));

	return 0;
}
 
Share this answer
 
v9
Comments
Richard MacCutchan 3-Jul-15 13:18pm    
A very good answer. However, I think having the two enums so closely related is also a bad idea.
Frankie-C 4-Jul-15 9:18am    
Thanks. You're right, there is a lot to say, but when I understood that he was using one of the obscure features of C I decided to comment that part, to make clear to novices that side and to explain that generally there is always another more correct way to do it ;)
At the end if you think about the whole issue it appears as an effort to complicate a simple matter. He just need a multidimensional array of pointers to char, a dimension for each language, and a single enum for languages, having also the benefit of direct access.
Richard MacCutchan 4-Jul-15 9:21am    
Absolutely true, and my comment was really directed more at the OP than you. As I said, your answer is not only correct, but clearly described.
lukeer 6-Jul-15 3:07am    
Thank you for your clear solution. Looks like accepting the need for LanguageId_Invalid is perferrable to the workarounds I thought of.

As for your comment on<pre lang="c#">return( *(oneLine.Heap + _currentLanguageId));</pre>Yes, something is wrong with this statement. My hope was that someone would come up with the correct syntax for that. Because<pre lang="c#">const char* Heap[]; is impossible without specifying Heaps size as well, which in turn calls for LanguageId_Invalid.
So, thanks again.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900