2

I have this ifdef typedef in my class:

#ifdef HASHMAP
    typedef std::unordered_map<unsigned int, L1Entry> L1; //C++ 11 only
#else
    typedef std::map<unsigned int, L1Entry> L1;
#endif

I need to control what container is used when I create a new object of the class. What would be the best approach to do this?

Luis
  • 678
  • 8
  • 14
  • So you want to do this at compile time or at runtime? – szx Aug 08 '13 at 07:07
  • @szx I don't mind, would prefer runtime though. – Luis Aug 08 '13 at 07:08
  • 1
    if you want control at runtime: use the template solution as below and create 2 typedefs - MyClassHash and MyClassTree (or whatever you like) that alias the appropriate MyClass including template parameter (e.g. `typedef MyClass> MyClassHash` – Tobias Langner Aug 08 '13 at 07:25

2 Answers2

6

Make the container a template parameter of the class:

template<typename MapType>
class MyClass
{
public:
    // ...

private:
    MapType myMap;
};

And you would instantiate like so:

MyClass< std::map<unsigned int, L1Entry> > obj;
MyClass< std::unordered_map<unsigned int, L1Entry> > obj2;

There's a container in the standard library that does exactly this, take a look at std::queue by default it is implemented with an std::deque but you can specify another container, so long as this container provides certain operations.

Here's another version where you only have to specify std::map or std::unordered_map:

#include <map>
#include <unordered_map>

typedef size_t L1Entry;

template<template <typename...> class Container>
class MyClass
{
    typedef Container<int, L1Entry> MapType;
public:
    // ...

private:
    MapType myMap;
};

int main()
{
    MyClass<std::map> obj;
    MyClass<std::unordered_map> obj2;
}

OK! Here's a final version, to show you how you can split the code in .h/.cpp (everything goes in .h except for the section I marked):

#ifndef MYMAP_H
#define MYMAP_H

#include <map>
#include <unordered_map>
#include <iostream>

typedef size_t L1Entry;

template<template <typename...> class Container>
class MyClass
{
    typedef Container<int, L1Entry> MapType;
public:
    void printMap();

private:
    MapType myMap;
};

// START OF CPP CHUNK (replace with #include <whatever.h>)
template<template <typename...Args> class Container>
void MyClass< Container >::printMap()
{
    // ... do the actual printing
    std::cout << "You'd print stuff here." << std::endl;
}
// END OF CPP CHUNK

#endif // MYMAP_H

And this would be the main.cpp:

#include "mymap.h"

int main()
{
    MyClass<std::map> obj;
    MyClass<std::unordered_map> obj2;

    obj.printMap();
    obj2.printMap();

    return 0;
}
Borgleader
  • 15,826
  • 5
  • 46
  • 62
  • Pedantic note: `queue`, `stack` and `priority_queue` are technically *container adaptors*, not containers. – juanchopanza Aug 08 '13 at 06:54
  • Is there a way to avoid the part std::map and just have (MyClass< std::map >)? – Luis Aug 08 '13 at 06:58
  • @Luis template template parameters, but it is messy. – juanchopanza Aug 08 '13 at 07:02
  • Can anyone provide an example? I would prefer it that way because the types in the map are never going to change. – Luis Aug 08 '13 at 07:06
  • @Luis Updated answer with example – Borgleader Aug 08 '13 at 07:21
  • @Borgleader Can I implement it with the variadic template version having separated .h and .cpp files? – Luis Aug 08 '13 at 07:25
  • @Luis you cannot split templates in .h .cpp files. [See here](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Borgleader Aug 08 '13 at 07:26
  • @Borgleader The class is big, so having everything in the header will be really messy... – Luis Aug 08 '13 at 07:31
  • @Borgleader Could I do it having this in the bottom of the .cpp: template class MyClass; template class MyClass; ? – Luis Aug 08 '13 at 07:37
  • @Luis What you can do, is split the declaration and implementation, but, include the .cpp at the bottom of the .h. As long as the compiler can see the declaration *and* implementation, it will compile. This is explained in the accepted answer of the question I linked earlier. – Borgleader Aug 08 '13 at 07:38
  • @Borgleader I ended up using the template with the separated implementation, I would love to edit your answer that is correct with the solution, so it is easier for other people to solve this problem. – Luis Aug 08 '13 at 08:21
  • @Luis I added the example with separate implementation. – Borgleader Aug 08 '13 at 08:22
  • @Borgleader I would suggest doing another example. The one you just posted is fine, but in my case would't work because the class is part of a dllexport and definition and implementation cannot be in the same files. The solution is using in the .cpp template class MyClass; template class MyClass; and not including anything in the .h. It is the last implementation option in your answer about templtes and .h/.cpp files. – Luis Aug 08 '13 at 08:27
0

Do you mean this ?

  #ifdef __GXX_EXPERIMENTAL_CXX0X__ //Checks for C++11
      typedef std::unordered_map<unsigned int, L1Entry> L1; //C++ 11 only
  #else
      typedef std::map<unsigned int, L1Entry> L1;
  #endif
P0W
  • 46,614
  • 9
  • 72
  • 119