4

I'm trying to insert a pointer object to a map through emplace() but it does not work.

I've created a simple representation of the problem below. I'm trying to insert to newFooList pointer object type Foo*.

I can't seem to find a way to create a type for FooMap* in std::map<int, FooMap*> m_fooMapList. Should it be done with the new on the second field of the map?

#include <iostream>
#include <utility>
#include <stdint.h>
#include <cstdlib>
#include <map>

class Foo
{
    private:
        int m_foobar;
    public:
        Foo(int value)
        {
            m_foobar = value;
        }
        void setfoobar(int value);
        int getfoobar();
};

class FooMap
{
    private:
        std::map<int, Foo*> m_newFoo;

    public:
        FooMap() = default;
};

class FooMapList
{
    private:
        std::map<int, FooMap*> m_fooMapList;
    public:
        FooMapList() = default;
        void insertFoo(Foo* newFooObj);
};

int Foo::getfoobar(void)
{
    return(m_foobar);
}

void FooMapList::insertFoo(Foo* newFooObj)
{
    if(m_fooMapList.empty())
    {
        std::cout << "m_fooMapList is empty" << std::endl ;
    }

    //m_fooMapList.emplace( newFooObj->getfoobar(), newFooObj  );
    // Need to find a way to insert newFooObj  to m_fooMapList
    m_fooMapList.second = new FooMap;
}

int main() {
    FooMapList newFooList;

    for (auto i=1; i<=5; i++)
    {
        Foo *newFoo = new Foo(i);
        newFoo->getfoobar();
        newFooList.insertFoo(newFoo);
    }

    return 0;
}

On g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28)

$  g++ -std=c++11 -Wall map_of_map.cpp 
map_of_map.cpp: In member function ‘void FooMapList::insertFoo(Foo*)’:
map_of_map.cpp:51:18: error: ‘class std::map<int, FooMap*>’ has no member named ‘second’
     m_fooMapList.second = new FooMap;
JeJo
  • 30,635
  • 6
  • 49
  • 88
Inian
  • 80,270
  • 14
  • 142
  • 161
  • You have a list L of maps M of elements E. And your code tells insert E in L without logic to find in which map to insert the element. – Rerito Feb 20 '19 at 08:50
  • whats the error from the commented out line? your `m_fooMapList.second = ...` line is nonsense, a map doesn't have a `second` member – kmdreko Feb 20 '19 at 08:51
  • @Rerito: So to clarify, the map needs to be created with `newFooObj->getfoobar()` as the key, that's why tried the emplace option which didn't work and threw a whole lot of STL errors pointing to `In file included from /usr/include/c++/4.8.2/map:60:0` – Inian Feb 20 '19 at 08:53
  • `getfoobar()` gets you the key for the element, but how would you get the correct map to use in your list? – Rerito Feb 20 '19 at 08:55
  • 2
    you have a map of maps of `Foo`s, do you want to use `getfoobar()` for the key of both maps? – kmdreko Feb 20 '19 at 08:56
  • 1
    @kmdreko: Yes that's right – Inian Feb 20 '19 at 08:58
  • Could you please explain **what problem** you are trying **to solve in plain english**. Your approach of a map of pointers to maps of poiinters to objects seems not to be a proper solution. What should happen with your maps, if the index (m_foobar) is changed for an Foo object? How will you avoid memory leaks or double deletion on cleanup or member deletion? - You stated that you do NOT want to have all objects (that you create with the 'new' operator) on the heap? - All you create with 'new' is on the heap... – CAF Feb 20 '19 at 13:32
  • @CAF : I’ve tried to explain the question as best as I could, not sure what you mean by plain English. Regarding all your conditions, I’ll try to improve on that. As I’ve indicated in one of the comments I’m just learning the language and I’ve made the best effort for now. If you can improve on my answer feel free to edit my answer, I’d really appreciate it. Thanks! – Inian Feb 20 '19 at 14:09

5 Answers5

6

m_fooMapList is defined as

    std::map<int, FooMap*> m_fooMapList;

So to insert into it, you need an int and a pointer to FooMap:

    m_fooMapList.emplace(newFooObj->getfoobar(), new FooMap);

Having said that, you should use C++ value semantics and rely less on raw pointers:

    std::map<int, FooMap> m_fooMapList; // no pointers

    m_fooMapList.emplace(newFooObj->getfoobar(), {}); // construct objects in-place

That is, instances of FooMap can reside directly in the map itself.

That way you get better performance and avoid memory leaks.

It's also worth looking into smart pointers (e.g. unique_ptr) if you really want to work with pointers.

rustyx
  • 80,671
  • 25
  • 200
  • 267
  • Thanks I like this option to create with pointers. But having created the new `FooMap` object. Can you elaborate how should I add new `Foo*` to that? – Inian Feb 20 '19 at 09:31
  • Also is it wrong to to use `make_pair` with `insert()` as `m_fooMapList.emplace(std::make_pair( newFooObj->getfoobar(), new FooMap ))`. It seems to compile all right – Inian Feb 20 '19 at 09:33
  • How do I make insertions into the `FooMap` now? do let me know on that. – Inian Feb 20 '19 at 09:42
  • `insert` copies the pair into the map, `emplace` creates it in-place. The end result is the same. Regarding your other question - it's another question already, please ask it separately. Please also explain what you want to achieve. It's unclear what you actually want. – rustyx Feb 20 '19 at 09:46
  • @Inian You *shouldn't* use `new`. There's no reason here for pointers in this code. Removing them makes everything so much easier. I would be tempted to replace `FooMap` with `using FooMap = std::map;` – Caleth Feb 20 '19 at 09:47
  • @rustyx: Sorry if I was vague in my question. I just want to know. How do I make an insert of `Foo*` into the newly created `FooMap`. I tried `m_fooMapList.emplace( newFooObj->getfoobar(), newFooObj );` which didn't work if you are referrign to that – Inian Feb 20 '19 at 09:50
  • 1
    You need two keys, one for the outer map, one for the inner map `m_fooMapList[key1][key2] = Foo(i);` – Caleth Feb 20 '19 at 09:59
  • @Caleth: Can you add an answer with that logic. I would really appreciate it. I'm still learning the language and I'm not proficient yet to how to do that – Inian Feb 20 '19 at 10:00
  • @Inian please explain your data structure. Why is it a map of maps? Why not a map of lists? – rustyx Feb 20 '19 at 10:08
  • @rustyx: Because on the real example of mine, I need to create thousands of objects every now and then where the index varies for the outer map and the inner map. At the basic level is the `Foo*` which needs to be added at the top-level from the `m_fooMapList`. Could you show me how do I do that? – Inian Feb 20 '19 at 11:00
3

I am not sure if you need a map structure where the values are pointers to another map. The FooMapList class could be simple

std::map<int, FooMap> m_fooMapList;

On the other hand, the entire play with the row pointer will bring you nothing but a pain on the neck.

In case the use of std::map<int, FooMap*> m_fooMapList; and std::map<int, Foo*> are neccesarry, I would go for smartpointers.

Following is an example code with replacing row pointers with std::unique_ptr and shows how to insert map of Foo s to map in-place. See live here

#include <iostream>
#include <utility>
#include <map>
#include <memory>

class Foo
{
private:
    int m_foobar;
public:
    Foo(int value): m_foobar(value) {}
    void setfoobar(int value) noexcept { m_foobar = value; }
    int getfoobar() const noexcept { return m_foobar; }
};

class FooMap
{
private:
    std::map<int, std::unique_ptr<Foo>> m_newFoo;
    //            ^^^^^^^^^^^^^^^^^^^^
public:
    FooMap() = default;
#if 0 // optional
    // copy disabled
    FooMap(const FooMap&) = delete;
    FooMap& operator=(const FooMap&) = delete;

    // move enabled
    FooMap(FooMap&&) = default;
    FooMap& operator=(FooMap&&) = default;
#endif
    // provide a helper function to insert new Foo to the map of Foo s
    void insertFoo(std::unique_ptr<Foo> newFooObj)
    //             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    {
        std::cout << "inserting to FooMap..." << std::endl;
        m_newFoo.emplace(newFooObj->getfoobar(), std::move(newFooObj)); // construct in place
    }
};

class FooMapList
{
private:
    std::map<int, std::unique_ptr<FooMap>> m_fooMapList;
    //            ^^^^^^^^^^^^^^^^^^^^^^^
public:
    FooMapList() = default;

    void insertFooMap(std::unique_ptr<Foo> newFooObj)
    //               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    {
        if (m_fooMapList.empty())
        {
            std::cout << "m_fooMapList is empty" << std::endl;
        }
        // create FooMap and insert Foo to it.
        FooMap fooMap;
        const auto key = newFooObj->getfoobar();
        fooMap.insertFoo(std::move(newFooObj));

        // finally insert the FooMap to m_fooMapList
        std::cout << "inserting to fooMapList..." << std::endl;
        m_fooMapList.emplace(key, std::make_unique<FooMap>(std::move(fooMap))); // construct in place
    }
};

int main() 
{
    FooMapList newFooList;

    for (auto i = 1; i <= 5; i++)
    {
        auto newFoo = std::make_unique<Foo>(i);
        std::cout << newFoo->getfoobar() << std::endl;
        newFooList.insertFooMap(std::move(newFoo));
    }

    return 0;
}

Output:

1
m_fooMapList is empty
inserting to FooMap...
inserting to fooMapList...
2
inserting to FooMap...
inserting to fooMapList...
3
inserting to FooMap...
inserting to fooMapList...
4
inserting to FooMap...
inserting to fooMapList...
5
inserting to FooMap...
inserting to fooMapList...
JeJo
  • 30,635
  • 6
  • 49
  • 88
  • This is great! but bearing the pain in the neck, I still need to use pointers, because on the real example of mine, I need to create thousands of objects every now and then which needs be done dynamically which I don't want to hold-up on the heap – Inian Feb 20 '19 at 09:54
  • 2
    Then don't forget to also care about [rule of three/five/zero](https://en.cppreference.com/w/cpp/language/rule_of_three). Happy coding;) – JeJo Feb 20 '19 at 09:56
  • Thanks a lot! Could you let me know on the other answer from `rustyx` which showed how to use the `new` to create the `FooMap`. I'm just missing this one step of how to subsequently add a `Foo*` to that. It will be great if you can suggest on that regard – Inian Feb 20 '19 at 09:58
  • 1
    @Inian The `Foo` objects in a map are dynamically allocated *by the map* – Caleth Feb 20 '19 at 10:00
  • 1
    ^^ in other words(in addition to @Caleth's comment), whatever, you have in `std::map` is on the heap. So `std::map m_fooMapList;` would be enough. – JeJo Feb 20 '19 at 10:02
  • @Inian The same way shown for `m_fooMapList` . => `m_newFoo.emplace(newFooObj->getfoobar(), new Foo(value));` – JeJo Feb 20 '19 at 10:06
  • @JeJo: Thanks for being patient with me. But my problem all along has been how do I access this _newly_ created `FooMap`? how do I access `m_newFoo` in my logic. Thanks! Could you update your answer with the same? Thanks a lot – Inian Feb 20 '19 at 10:17
  • @Inian *how do I access this newly created `FooMap`?*: I assume that you wanna add more `Foo`s to it; not just one `Foo`, is that correct? If so simplest way is to create a `FooMap` in main-> add `Foo`s and then insert that to the `FooMapList`: the function would be `void insertFooMap(std::unique_ptr newFooMapObj)` instead of just `FooObj`. – JeJo Feb 20 '19 at 10:27
  • @Inian Also think about *getter* functions where you can give a `key` to it and that returns corresponding `value`(FooMap) from the `m_fooMapList`(if exist). – JeJo Feb 20 '19 at 10:29
  • @JeJo: I still can't seem to make this work. Tried all combinations with creating pointer references. In my real logic I can't create `FooMap` from main(). What I need is exactly your logic but with pointers. Please share your approach for the same – Inian Feb 20 '19 at 10:58
  • @Inian Then simply remove [smart pointer part](https://wandbox.org/permlink/jPiL5ccs5jmQ2IDq) which I won't recommend though. Have care about the row pointers BTW. – JeJo Feb 20 '19 at 11:06
2

You can throw away your do-nothing map classes, stop using pointers, and just

#include <iostream>
#include <utility>
#include <stdint.h>
#include <cstdlib>
#include <map>

class Foo
{
private:
    int m_foobar;
public:
    Foo(int value) : m_foobar(value) { }
    void setfoobar(int value) { m_foobar = value; }
    int getfoobar() const { return m_foobar; }

    // or more simply
    // int foobar;
};

using FooMap = std::map<int, Foo>;

using FooMapMap = std::map<int, FooMap>;

int main() {
    FooMapMap foos;

    for (auto i=1; i<=5; i++)
    {
        foos[i][i] = Foo(i);
    }

    return 0;
}

Do note that the inner map is totally pointless at this stage, as they only ever have one entry

Caleth
  • 52,200
  • 2
  • 44
  • 75
1

Except if you have a really good reason to do so, avoid to obfuscate things a la Java like this and try to leverage the STL anyway you can. To that end you can use type aliases

using FooMap = std::map<int, Foo*>; // Maybe use a smart pointer instead here?
using FooMapList = std::map<int, FooMap>; // Maybe List is not an appropriate name for a map

Now, you have a Foo element you just created and want to insert it in your map list, to do so you need a way to select in which map in the list you want to insert it. I will assume you'll insert in the first map in the list:

auto FooMap::emplace(int key, Foo* value)
{
    return m_newFoo.emplace(key, value);
}

void FooMapList::insertFoo(Foo* newFooObj)
{
    // If the map for `getfoobar` does not exist yet, operator[] will create it
    auto& mapPtr = m_fooMapList[newFooObj->getfoobar()];
    if (nullptr == mapPtr)
        mapPtr = new FooMap();

    mapPtr->emplace(
        newFooObj->getfoobar(),
        newFooObj
    );
}

Note that I didn't handle memory cleanup. I suggest you try to use smart pointers when applicable (std::unique_ptr and std::shared_ptr)

Rerito
  • 5,886
  • 21
  • 47
  • 1
    That seemed right, but getting an error as `map_of_map.cpp:51:42: error: request for member ‘emplace’ in ‘((FooMapList*)this)->FooMapList::m_fooMapList.std::map<_Key, _Tp, _Compare, _Alloc>::operator[], std::allocator > >((* & newFooObj->Foo::getfoobar()))’, which is of pointer type ‘std::map::mapped_type {aka FooMap*}’ (maybe you meant to use ‘->’ ?) m_fooMapList[newFooObj->getfoobar()].emplace( newFooObj->getfoobar(), newFooObj );` – Inian Feb 20 '19 at 09:05
  • as much i'd love to join a java rant, I dont really get how this is relevant here or what you actually mean with "obfuscate things a la Java like this", like what? – 463035818_is_not_an_ai Feb 20 '19 at 09:19
  • @user463035818 By facading the maps like this, you cannot use the STL interface as it's completely encapsulated. This leads you to have to expose manually the bits you are interested in. That's a huge loss on genericity and potential usage with STL algorithms. Therefore I still think "encapsulation" for the sake of "encapsulation" is not advisable. – Rerito Feb 20 '19 at 09:23
  • @Inian Sorry forgot that the map was wrapped inside an object, just expose an `emplace` function in your `FooMap` object like I did. – Rerito Feb 20 '19 at 09:25
0

I have considered valid points from each of the answers to remove pointers and remove useless double level map representations. But the real world abstraction is a much complex problem which involves thousands of objects on the fly which needs to be created and destroyed dynamically. Using pointers seemed a valid approach, but JeJo' approach seemed much better.

I tried to re-use his attempt but with object pointers and the below seems to work. With the below insert functions

In the FooMap class the function would be

void FooMap::insertFoo(Foo* newFooObj)
{
    m_newFoo.emplace(newFooObj->getfoobar(), newFooObj);
}

const std::map<int, Foo*> FooMap::getList()
{
    return m_newFoo;
}

and in FooMapList it would be

void FooMapList::insertFooList(Foo* newFooObj)
{
    std::map <int, FooMap*>::iterator iter;
    FooMap *localFooMap = NULL;
    iter = m_fooMapList.find( newFooObj->getfoobar() );

    if( iter == m_fooMapList.end() )
    {
        localFooMap = new FooMap;
        localFooMap->insertFoo(newFooObj);
        m_fooMapList.emplace(newFooObj->getfoobar(), localFooMap );
    }
    else
    {    
        localFooMap = iter->second;
        localFooMap->insertFoo(newFooObj);
        m_fooMapList.emplace(newFooObj->getfoobar(), localFooMap );
    }
}

const std::map<int, FooMap*> FooMapList::getList()
{
    return m_fooMapList;
}

I would appreciate feedback for this approach too. I will be adding the calls to destructor to clean up the created objects

Inian
  • 80,270
  • 14
  • 142
  • 161
  • please see my comment on the initial question. – CAF Feb 20 '19 at 13:33
  • Your `getList()` function copies the member maps (both `m_fooMapList` and `m_newFoo` each time of call. Rethink your design: is that(above mentioned) you want or a `const ref` or just corresponding `Foo`objects in those maps, when passing some *key*s. That being said, if your code working, you need to post it in [codereview.stackexchange.com](https://codereview.stackexchange.com/) for further reviewing, not in SO. – JeJo Feb 20 '19 at 15:42
  • @JeJo: Thanks I'm using c++11 for now. Also I've added the question to Code Review Stack Exchange - https://codereview.stackexchange.com/q/213904/147059 – Inian Feb 20 '19 at 20:43