4

I have a C++ program that parses and writes to XML files. Since the tags used in XML files are repetitive, I've declared a list of common strings as tags in CPP file itself. Should I create a separate header file for the strings alone or is it fine leaving them in the implementation file itself? Which is the best practice here?

Here is what my CPP file looks like:

#include<iostream>
#include<string>

const std::string POS_ID = "position-id-map";
const std::string HEIGHT = "height";
const std::string WIDTH = "width";
const std::string RATIO = "ratio";
.
.
.
.
//20 more strings

int main(int argc, char ** argv) {
    //do XML reading and other stuff
    return 0;
}

What benefit will declaring it in a separate header file give me over declaring it directly in the implementation file?

tnkousik
  • 103
  • 1
  • 8
  • 2
    If those go in the header as is, you'll probably run into ODR problems. – chris Jun 06 '14 at 22:17
  • 4
    Best practice is: just say *no* to XML. – Jerry Coffin Jun 06 '14 at 22:19
  • 1
    Depends on how you are going to use them or abuse them... – 101010 Jun 06 '14 at 22:20
  • @chris I can declare them as static strings? – tnkousik Jun 06 '14 at 22:26
  • @JerryCoffin I wish, but if someone put a gun to your head and asked you to parse an XML file, wouldn't you? – tnkousik Jun 06 '14 at 22:27
  • 1
    @tnkousik: Hmmm...what kind of gun? Is it one that's certain to kill me instantly? – Jerry Coffin Jun 06 '14 at 22:30
  • 1
    @tnkousik don't define `static`s in header files you break the one definition rule. If you are going to put them in a header declare them `extern` and define them in a single cpp file. – 101010 Jun 06 '14 at 22:33
  • @tnkousik I would say header file. Because you might want to you use them in other source files too. – santahopar Jun 06 '14 at 22:35
  • 2
    If you really do not use them in other parts of the project, e.g. because parse the XML to a decent data model, you can put these variables in a C++ file. In this case, might also want to put them in an unnamed namespace. – Csq Jun 06 '14 at 22:46
  • @JerryCoffin Haha no, one that kills you when you go out of scope. – tnkousik Jun 06 '14 at 22:52
  • @40two Right, got it. – tnkousik Jun 06 '14 at 22:52
  • 1
    If you only use them in the one cpp file, declare them static const in that file or const and wrap them in namespace {} – cppguy Jun 06 '14 at 22:58
  • @nos From the perspective of organizing code, which would be better? Say I want to do this for ten different XML files, each with its unique set of tags. Wouldn't putting them all in a single header file be easier to manage the codebase? – tnkousik Jun 06 '14 at 22:59

2 Answers2

3

Solution No.1

  • Declare them as extern in a header and define them in a '.cpp' file.
  • Don't define them as static in a header file as you would break the one definition rule.

Solution No.2

  • Declare them as static constant member variables of a helper class. Put the class definition in a header file (e.g., XML_constants.hpp). Define them in a .cpp file (e.g., XML_constants.cpp):

  // XML_constants.hpp
  struct XML {
    static const std::string POS_ID;
    static const std::string HEIGHT;
    static const std::string WIDTH;
    static const std::string RATIO;
  };

  // XML_constants.cpp
  const std::string XML::POS_ID = "position-id-map";
  const std::string XML::HEIGHT = "height";
  const std::string XML::WIDTH  = "width";
  const std::string XML::RATIO  = "ratio";

Solution No.3

  • If the usage of these constants is limited in your main.cpp then your current configuration looks fine.
101010
  • 41,839
  • 11
  • 94
  • 168
  • 3
    Declaring them as static in the header doesn't break the one definition rule. You'll just have an instance of these strings for each place you include the header (redundant instances) – cppguy Jun 06 '14 at 22:57
  • @cppguy You declare them in the scope of `class XML` in the `XML_constants.hpp` and define them in `XML_constants.cpp`. So this doesn't break the one definition rule as you define them in a single translation unit (i.e., `XML_constants.cpp`) – 101010 Jun 06 '14 at 22:59
  • @cppguy exactly - they are separate entities and dont break odr. I.e, don' t expect to get the same address for the strings in different translation units. Thats the whole point of static storage on global variables... – Pete Jun 06 '14 at 23:43
3

Well, since you are asking a question about header files, you program probably consists (or will eventually consist) of more than one implementation files, several (or all) of which include your header file.

If so, defining heavy const objects in header files is not a good idea. const objects in C++ have internal linkage by default, which will prevent any "multiple definitions" errors, but at the same time will create an independent copy of each such heavy object in each translation unit that includes that header file. Doing something like that without a good reason is a rather wasteful thing to do.

A better idea would be to provide non-defining declarations in the header file

// Declarations
extern const std::string POS_ID;
extern const std::string HEIGHT;
extern const std::string WIDTH;
extern const std::string RATIO;

and place the definitions in one and only one implementation file

// Definitions
extern const std::string POS_ID = "position-id-map";
extern const std::string HEIGHT = "height";
extern const std::string WIDTH = "width";
extern const std::string RATIO = "ratio";

Note that the keyword extern has to be specified explicitly in this approach, to override the "static by default" property of const. However, if the header declarations are visible at the point of definition, then extern can be omitted from the definitions.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • That is neat. Thanks. – tnkousik Jun 07 '14 at 05:47
  • *A better idea would be to provide non-defining declarations in the header file..."* - But then some programs suffer [Static Initialization Order Fiasco](https://stackoverflow.com/q/1005685/608639). Or at least C++ libraries do. – jww Dec 02 '18 at 17:42