Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating and populating a map in a header file in C++

Tags:

c++

maps

It doesn't seem like C++ really has a way to do this, but I'm hoping I'm wrong.

What I'd like to do is create a map (in this case to map one set of strings to another, not that the datatypes should matter really) inside a header file, so that multiple source files can then access that map directly.

Right now I just have a function defined in my header file and implemented in the source file that essentially does this exact function, but I'd prefer to use a map since I would then be able to see at one glance what the keys/values are.

The problem I'm running into with this is that populating the map requires executable code, which I can't exactly just place into a header file. Is there any good way to achieve what I'm trying to do?

like image 999
Nickolai Avatar asked May 06 '14 20:05

Nickolai


3 Answers

There's a couple of ways to do this.

You can declare the variable in a header file, but you need to define it in a cpp file...

// FOO.H STARTS HERE
#include <map>
#include <string>

extern std::map<std::string, std::string> my_map;
// FOO.H ENDS 

// FOO.CPP STARTS HERE
#include <iostream>

#include "foo.h"

std::map<std::string, std::string> my_map = {
  { "Cat", "Feline" },
  { "Dog", "Canine" },
  { "Fish", "Fish" }
};

int main (void) {
  for (auto i = my_map.begin(); i != my_map.end(); ++i) {
    std::cout << "A " << i->first << " is a " << i->second << std::endl;
  }

  return 0;
}

// FOO.CPP ENDS HERE

Alternatively, you can just keep the map in a header as long as it's const. This has the downside that it obviously only works if you don't need to change the map.

// FOO.H STARTS HERE
#include <map>
#include <string>

const std::map<std::string, std::string> my_map = {
  { "Cat", "Feline" },
  { "Dog", "Canine" },
  { "Fish", "Fish" }
};

// FOO.H ENDS HERE

// FOO.CPP STARTS HERE
#include <iostream>

#include "foo.h"

int main (void) {
  for (auto i = my_map.begin(); i != my_map.end(); ++i) {
    std::cout << "A " << i->first << " is a " << i->second << std::endl;
  }

  return 0;
}

// FOO.CPP ENDS HERE

Both of these examples assume C++11, for the use of the nice and pretty map = { } initializer.

like image 150
QuestionC Avatar answered Sep 22 '22 20:09

QuestionC


I took this as a challenge -- is there a header-only solution using C++03 syntax?

The answer is "yes":

// GlobalMap.hpp
#include <map>

typedef std::map<std::string, std::string> GMap;

inline
GMap & globalMap()
{
    static GMap theMap;
    static bool firstTime = true;
    if(firstTime)
    {
        firstTime = false;
        theMap["Cat"] = "Feline";
        theMap["Dog"] = "Canine";
        theMap["Guppy"] = "Fish";
    }
    return theMap;
}

To test this:

#include <iostream>
#include "GlobalMap.hpp"
int main()
{
    for(
        GMap::const_iterator it = globalMap().begin();
        it != globalMap().end();
        ++it)
    {
        std::cout << '[' << it->first << "]=" << it->second << std::endl;
    }
   return 0;
}

Produces this output:

[Cat]=Feline
[Dog]=Canine
[Guppy]=Fish

Note I'm not recommending this solution. Just showing it as an interesting possibility.

like image 41
Dale Wilson Avatar answered Sep 21 '22 20:09

Dale Wilson


You can use an initializer list in C++ 11: http://en.cppreference.com/w/cpp/language/list_initialization

But this doesn't solve the problem. You don't want to initialize it in a header file, you will most likely get linker errors (multiple symbol definition).

You can: 1. Use the extern keyword and initialize it in a cpp file as global variable. 2. You can make it a static variable inside a class (but still initialize it in a cpp file).

like image 39
Bedford Avatar answered Sep 25 '22 20:09

Bedford