-2

How to append string to a vector contained inside map? Structure is map(float,vector(string)) where the map is in shared memory.my question is if key==desired key then append string to the vector of strings?

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
p.k
  • 37
  • 3
  • Please take some time to re-read [the help pages](http://stackoverflow.com/help), especially ["What topics can I ask about here?"](http://stackoverflow.com/help/on-topic) and ["What types of questions should I avoid asking?"](http://stackoverflow.com/help/dont-ask). Also please [re-take the SO tour](http://stackoverflow.com/tour) and [re-read about how to ask good questions](http://stackoverflow.com/help/how-to-ask). Lastly don't forget how to create a [Minimal, Complete, and Verifiable Example](http://stackoverflow.com/help/mcve). – Some programmer dude Oct 10 '18 at 11:26
  • It should not matter that the map is in shared memory, at least not for the access itself. But maybe you need a semaphore or a mutex to prevent multiple, parallel access. – Rene Oct 10 '18 at 12:17
  • 1
    Using floats for equality comparison is almost always a bad idea. https://stackoverflow.com/questions/6684573/floating-point-keys-in-stdmap – sehe Oct 10 '18 at 15:20
  • @Rene why should it not matter? I agree that given the absence of code, we can only speak conceptually. And conceptually it doesn't matter. But technically, it does matter in a lot of details. – sehe Oct 10 '18 at 16:22
  • People tend to forget that while allocators are a nice abstraction, shared memory allocators are necessarily stateful, and stateful allocators require lots of library support and hand-holder by the programmer. – sehe Oct 10 '18 at 16:24
  • @sehe That's why I wrote "at least not for the access itself". E.g. the `map<>` has an `insert()` method, and it's the same for the `std::map` or for the `boost::interprocess::map<>` (if that's the correct namespace, I'm not sure). But the internal implementations of course differ. – Rene Oct 11 '18 at 05:32
  • @Rene The point is that usage is different because you have to deal with the allocator and converting the values – sehe Oct 11 '18 at 05:44

2 Answers2

1

Do you mean something like this:

#include <map>
#include <vector>
#include <string>
#include <iostream>

int main()
{
        std::map<float, std::vector<std::string>> m;

        m[.5f].emplace_back("First");
        m[.5f].emplace_back("Second");
        m[.0f].emplace_back("Hello");
        m[.0f].emplace_back("World");

        for(const auto& [key, value] : m)
        {
                std::cout << "Key: " << key << '\n';
                for(const auto& str : value)
                        std::cout << '\t' << str << '\n';
        }
        std::cout.flush();
        return 0;
}
Max
  • 638
  • 1
  • 4
  • 19
  • I have initialized and allocated map object using managed shared memory in one process. I need to add some more elements into it in another process.need to expand the map or vector. – p.k Oct 10 '18 at 14:55
  • @Max this is highly simplistic. None of this translates to shared memory containers, without advanced mechanisms (scoped allocators) and even then it skirts the edges of the construction argument forwarding mechanisms. I'll post an answer just to show. – sehe Oct 10 '18 at 16:09
  • [Posted](https://stackoverflow.com/a/52744714/85371). If you're up for a challenge, you could try to write just the `doesn't exist` branch in a natural fashion (say `_map.emplace(f, {s});` as you'd usually be able to write). Of course, my solution cheats on that by using the default-constructing `operator[]`. – sehe Oct 10 '18 at 16:21
1

Doing this in shared memory is pretty hard, actually.

If you get all the allocators right, and add the locking, you'd usually get very clunky code that is hard to read due to all the allocator passing around.

You can, however, employ Boost's scoped allocator adaptor which will do a lot (lot) of magic that makes life better.

I think the following code sample just about nails the sweet spot.

Warning: This builds on years of experience trying to beat this into submission. If you fall just outside of the boundary of "magic" (mostly the in-place construction support due to uses_allocator<> and scoped_allocator_adaptor) you will find it breaks up and you'll be writing a lot of manual constructor/conversion calls to make it work.

Live On Coliru

#define DEMO
#include <iostream>
#include <iomanip>
#include <mutex>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/managed_mapped_file.hpp> // For Coliru (doesn't support shared memory)
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/container/scoped_allocator.hpp>

namespace bip = boost::interprocess;
namespace bc = boost::container;

namespace Shared {
    using Segment = bip::managed_mapped_file; // Coliru doesn't support bip::managed_shared_memory

    template <typename T> using Alloc = bc::scoped_allocator_adaptor<bip::allocator<T, Segment::segment_manager> >;
    template <typename V>
        using Vector = bip::vector<V, Alloc<V> >;
    template <typename K, typename V, typename Cmp = std::less<K> >
        using Map = bip::map<K, V, Cmp, Alloc<std::pair<K const, V> > >;

    using String = bip::basic_string<char, std::char_traits<char>, Alloc<char> >;

    using Mutex = bip::interprocess_mutex;
}

namespace Lib {
    using namespace Shared;
    struct Data {
        using Map = Shared::Map<float, Shared::Vector<Shared::String> >;

        mutable Mutex _mx;
        Map _map;

        template <typename Alloc> Data(Alloc alloc = {}) : _map(alloc) {}

        bool append(float f, std::string s) {
            std::lock_guard<Mutex> lk(_mx); // lock

            auto it = _map.find(f);
            bool const exists = it != _map.end();

#ifndef DEMO
            if (exists) {
                it->second.emplace_back(s);
            }
#else
            // you didn't specify this, but lets insert new keys here, if
            // only for the demo
            _map[f].emplace_back(s);
#endif
            return exists;
        }

        size_t size() const {
            std::lock_guard<Mutex> lk(_mx); // lock
            return _map.size();
        }

        friend std::ostream& operator<<(std::ostream& os, Data const& data) {
            std::lock_guard<Mutex> lk(data._mx); // lock

            for (auto& [f,v] : data._map) {
                os << f << " ->";
                for (auto& ss : v) {
                    os << " " << std::quoted(std::string(ss));
                }
                os << "\n";
            }

            return os;
        }
    };
}

struct Program {
    Shared::Segment msm { bip::open_or_create, "data.bin", 10*1024 };
    Lib::Data& _data = *msm.find_or_construct<Lib::Data>("data")(msm.get_segment_manager());

    void report() const {
        std::cout << "Map contains " << _data.size() << " entries\n" << _data;
    }
};

struct Client : Program {
    void run(float f) {
        _data.append(f, "one");
        _data.append(f, "two");
    }
};

int main() {
    {
        Program server;
        server.report();

        Client().run(.5f);
        Client().run(.6f);
    }

    // report again
    Program().report();
}

First run would print:

Map contains 0 entries
Map contains 2 entries
0.5 -> "one" "two"
0.6 -> "one" "two"

A second run:

Map contains 2 entries
0.5 -> "one" "two"
0.6 -> "one" "two"
Map contains 2 entries
0.5 -> "one" "two" "one" "two"
0.6 -> "one" "two" "one" "two"
sehe
  • 374,641
  • 47
  • 450
  • 633