5

In my current code project, there is an awful lot of constant strings I use to print out error messages.

Hardcoding such strings is generally considered to be bad practice. Now I'm searching for a "clean way" to include these strings in my program. Therefore I've written a "constants.h" and "constants.cpp" which looks something like

#ifndef CONSTANTS_H_
#define CONSTANTS_H_

extern std::string error_message_1;

#endif

and the constants.cpp:

#include "constants.h"

std::string error_message_1 = "this is just a sample, not my actual naming convention. move on the the question ;-)"

So you might see the problem I have with this: due to a lot of error messages, the variable name itself is fairly long. The error message is even longer, so it crashes the standard 80 characters in one line of code.

Obviously I want my code nice and clean. I don't like going far beyond the 80 charactes per line of code. I don't like somthing like the following either:

std::string error_message_1 = "this is just a sample about how to"
                              " have one string across many lines"
                              " in the source code. this is not "
                              "very pretty too..";
std::string msg = "and suddenly there's one line which is good..";
std::string error_message_with_longer_name = "and it's even "
                                             "uglier with not "
                                             "equally long "
                                             "variable names";

Storing the strings in an external file which one would read in at the start of the program does not seem to be optimal too since the compiler cannot warn about missing strings. Basically this would be a mess to maintain.

So what do you do about it? Is there such a nice thing as "Resources", which I know of Android (or is it Java in general?) development?

Note: Please do NOT tell me that my naming convention is bad. The above is just an example. It's not what it looks like ;-)

7
  • 1
    Why aren't those objects marked const?
    – Klaim
    Commented May 20, 2012 at 11:09
  • 1
    because it seems to be not relevant to this question. Or does this change anything about code style?
    – stefan
    Commented May 20, 2012 at 11:11
  • 1
    Yes, those are not constants until you mark them const. No optimizations can happen here. That's not related to the question though, it's just that you talk about constant, but they are not (in a C++ way).
    – Klaim
    Commented May 20, 2012 at 11:19
  • 1
    I think you might also have a problem if you access those string constants before the corresponding module has been initialized. I was using a similar technique and when I was reading the strings from another .cpp file they were all empty because the code using the constants was executed before the object module in which they were defined was initialized. At least, this is my explanation for that.
    – Giorgio
    Commented May 20, 2012 at 11:27
  • well then it seems that I will switch to const char[], but that still leaves an unbearable mess of code. I just want to have my beloved compiler warnings and errors in combination with nice string storage..
    – stefan
    Commented May 20, 2012 at 11:39

4 Answers 4

7

Hardcoding such strings is generally considered to be bad practice.

Not sure I agree with that.

String literals are easy to read rather than the name of a string. If you want the same text for multiple locations then encapsulate the text into a function and call the single function to display the message.

If you want to centralize the text into a single file. Then what you are looking for is: strings as resources not manually moving all your string literals to string variables.

Checkout the following packages (normally used in localization but will work here).

* gettext

Basically what this package does is allow you to leave the 'English' version of the message in the code. But it will use the 'English' text as a key into a resource table to get the corrct version of the string for a particular local.

So code that looked like this:

printf ("and suddenly there's one line which is good..");

// Now looks like this:

printf (gettext ("and suddenly there's one line which is good.."));

You do not need to go through the hassel of extracting all your constant strings into another package.

See here for full details. http://www.gnu.org/software/gettext/

Personally I use the above in combination with boost format libraries for localization.

std::cout << boost::formt("The time is %1 in %2.") % time % country;

becomes:

std::cout << boost::formt(gettext("The time is %1 in %2.")) % time % country;

English Resource File:      "The time is %1 in %2."
Azerbaijani Resource File:  "Saat %2 saat %1 deyil."
1
  • gettext is great, everyone should use it. Note that if you fail to provide a translated string, it will fall back to using the original English one, so everything will still work.
    – gbjbaanb
    Commented Mar 12, 2015 at 15:43
5

Hardcoding such strings is generally considered to be bad practice

Do you realize that having a file that contains literals is still hardcoding? It seems you're confusing hardcoding with magic numbers. Strings, in general, aren't magical and so you don't need to give them a name. Moving them to a constant is only necessary to avoid duplication. If you're only using a string once (as in the case of most error messages) then it's pointless to move them into another file, unless for some reason you're doing it as compile-time configure file.

If it's crucial for the user to be able to change the error messages then move them to a configuration file (not a .hpp).

If you need it for translation then use something like gettext which allows you to provide translation files and still keep your source clean.

Also, don't use std::string for string constant expressions - use const char[] instead.

13
  • Yes, I do realize it's not really avoiding "hardcoding", but I don't see a cleaner way to still get compiler warnings if a string is missing. Some messages are present in multiple cpp-files, so storing them elswhere seems to be a good idea. Why are you suggesting the use of const char[]? Why should this be better?
    – stefan
    Commented May 20, 2012 at 11:25
  • 2
    Maybe because const char is initialized at compile time while std::string is initialized when the containing module is loaded. Then you might have an error if modules are not loaded in the order you expect. At least this is what I suspect can explain some bugs I had lately. If someone can comment on this theory I would be glad to learn more.
    – Giorgio
    Commented May 20, 2012 at 11:31
  • 3
    @stefan const char is more efficient. std::string has quite a bit of overhead and requires dynamic initialization. It's not a huge issue, but I still recommend using const char[] if possible.
    – Pubby
    Commented May 20, 2012 at 11:33
  • @Pubby: I recently had the problem that I accessed some const std::string variables from a different .cpp file and they were "" (probably not initialized yet). So I would say that using const char[] is definitely better because it avoids this kind of problems too.
    – Giorgio
    Commented May 20, 2012 at 11:37
  • 2
    -1 for the last paragraph. The std::string class does NOT add a bunch of extra overhead when used in this manner, has a well defined and determined end that is a pre-calculated aspect of the instance's state (unlike C strings), retains consistency by not using one API for some strings and another for others,...etc...I could go on and on. This is really bad advice in an otherwise decent answer. Commented May 20, 2012 at 16:51
0

Hardcoding strings is fine as long as the values never need to change. I have seen them stored as static consts and referred to be an associated enum many times.

You could wrap it in a class if you wanted to make the interface easier.

However, there are not really things built into the language to do this - this would be something you did using the language.

-2

What I am doing is to keep most of the strings in one resources.h file where i am formatting it as follows

#include <string>
#include <vector>

struct Class1_Res
{
   const std::wstring string1 = std::wstring(L"string 1");
      ----------------
}

struct Class2_Res
{
   const std::wstring string1 = std::wstring(L"string 1");
      ----------------
}

struct StatusGeneric_Res
{
const std::wstring MESSAGE_SUCCESS                  = std::wstring(L"Success");
const std::wstring MESSAGE_FAILURE                  = std::wstring(L"Failure");
const std::wstring MESSAGE_BAD_PARAM                = std::wstring(L"Bad Parameter");
const std::wstring MESSAGE_BAD_INPUT                = std::wstring(L"Bad Input");
const std::wstring MESSAGE_BAD_FILE                 = std::wstring(L"Bad File");
const std::wstring MESSAGE_MISSING_FILE             = std::wstring(L"Missing File");
const std::wstring MESSAGE_CONTAINER_NOITEM         = std::wstring(L"No item present");
const std::wstring MESSAGE_CONTAINER_ITEM_DUP       = std::wstring(L"Duplicate item");
const std::wstring MESSAGE_CONTAINER_EMPTY          = std::wstring(L"Empty Container");
const std::wstring MESSAGE_MEMORY_ALLOC_FAIL        = std::wstring(L"Memory Allocation Failure");
const std::wstring MESSAGE_UNIMPLEMENTED            = std::wstring(L"Not implemented");
const std::wstring MESSAGE_UNKNOWN_FAILURE          = std::wstring(L"Unknown Failure");
};

I can then include these struct in Class1 or Class2 or direct call them like

Class1_Res().string1

Saving in wstring helps me to get c style string as and when I need it. There is no messy allocation stuff to do since I use wstring at most of the places. This will allow me to replace these strings by language specific Hash-define if needed.

StatusGeneric_Res is there to bring together generic errors at one place without keeping it in all files. I can club this with function name to localize the reference.

If Class1_Res becomes too big, I can divide it into two parts by taking out error strings

struct Class1_Err
{
    // .............
}

Such division help me to keep all strings at one place and sometimes utilize them at multiple places when say Class1 is called somewhere and I need to print error related to it. Overall cost of keeping data in const wstring or const char[] should not be too different knowing how STL obsesses about efficiency.

8
  • 3
    Are you aiming for maximum inefficiency? This makes a copy of nearly every string in the entire program each time one of them gets used!
    – Ben Voigt
    Commented Mar 12, 2015 at 18:31
  • @BenVoigt Use of hard coded strings is not a CPU killer operation. We use these mostly to display something or write in the logs which is generally infrequent operations. If the string is too big and getting used frequently, there would be case to do it otherwise. Its a tradeoff due to convenience of working with wstring over hashdefed c style strings if you use wstring extensively in your program.
    – Atul Kumar
    Commented Mar 13, 2015 at 15:15
  • The original question showed an approach that provides string objects, with a lot less copying.
    – Ben Voigt
    Commented Mar 13, 2015 at 15:30
  • @BenVoigt I need to do some more experimentation but keeping strings non-const may be trouble if we include these classes through inheritance somewhere. VS gave me trouble when I tried original problem approach. I will check it more.
    – Atul Kumar
    Commented Mar 13, 2015 at 15:59
  • 1
    const is fine. The problem with the code in this answer is that the strings are non-static data members, so new copies are made for each class instance. There's no reason to have multiple copies of the same hardcoded string.
    – Ben Voigt
    Commented Mar 13, 2015 at 16:20

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.