I am using boost's managed_shared_memory, below is the A program which is responsible for creating and constructing an unordered_set.
#include "pch.h"
#include <iostream>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/unordered_set.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include<string>
using namespace boost::interprocess;
const char enrtNdbShareSet[] = "enrtTableSet";
struct EnrtRecord
{
std::string ern_id = "";
std::string ern_frequency = "";
std::string ern_name = "";
bool operator==(const EnrtRecord& other) const {
return ern_id == other.ern_id && ern_frequency == other.ern_frequency;
}
bool IsKeyNotEmpty() const {
return ern_id != "" && ern_frequency != "";
}
};
// hash func
struct EnrtRecordHash
{
size_t operator()(const EnrtRecord& s) const {
size_t h1 = boost::hash<std::string>()(s.ern_id);
size_t h2 = boost::hash<std::string>()(s.ern_frequency);
return h1 ^ (h2 << 1);
}
};
using EnrtShmemAllocator = boost::interprocess::allocator<EnrtRecord, boost::interprocess::managed_shared_memory::segment_manager>;
using EnrtUnorderedSet = boost::unordered_set<EnrtRecord, EnrtRecordHash, std::equal_to<EnrtRecord>, EnrtShmemAllocator>;
inline std::ostream& operator<<(std::ostream& os, const EnrtRecord& p) {
os << "enrt_ern_id: " << p.ern_id << ", ern_name: " << p.ern_frequency;
return os; // 返回ostream对象以支持链式操作
}
int main()
{
struct shm_remove
{
shm_remove() { shared_memory_object::remove("MySharedMemory"); }
~shm_remove() { shared_memory_object::remove("MySharedMemory"); }
} remover;
managed_shared_memory segment(create_only, "MySharedMemory", 65536);
auto mSet = segment.construct<EnrtUnorderedSet>("XX")(3, EnrtRecordHash(), std::equal_to<EnrtRecord>(), segment.get_allocator<EnrtUnorderedSet>());
EnrtRecord a;
a.ern_name = "HANGZHOULMM RW25";
a.ern_id = "AA";
a.ern_frequency = "444";
mSet->insert(a);
while (true) {}
}
the belowing B program is used to read the content in share memory:
#include "pch.h"
#include <iostream>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/unordered_set.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include<string>
using namespace boost::interprocess;
const char enrtNdbShareSet[] = "enrtTableSet";
struct EnrtRecord
{
std::string ern_id = "";
std::string ern_frequency = "";
//std::string ern_mag_var = "";
std::string ern_name = "";
bool operator==(const EnrtRecord& other) const {
return ern_id == other.ern_id && ern_frequency == other.ern_frequency;
}
bool IsKeyNotEmpty() const {
return ern_id != "" && ern_frequency != "";
}
};
// hash func
struct EnrtRecordHash
{
size_t operator()(const EnrtRecord& s) const {
size_t h1 = boost::hash<std::string>()(s.ern_id);
size_t h2 = boost::hash<std::string>()(s.ern_frequency);
return h1 ^ (h2 << 1);
}
};
using EnrtShmemAllocator = boost::interprocess::allocator<EnrtRecord, boost::interprocess::managed_shared_memory::segment_manager>;
using EnrtUnorderedSet = boost::unordered_set<EnrtRecord, EnrtRecordHash, std::equal_to<EnrtRecord>, EnrtShmemAllocator>;
inline std::ostream& operator<<(std::ostream& os, const EnrtRecord& p) {
os << "enrt_ern_id: " << p.ern_id << ", ern_name: " << p.ern_frequency;
return os; // 返回ostream对象以支持链式操作
}
int main()
{
managed_shared_memory segment(open_read_only, "MySharedMemory");
EnrtUnorderedSet *myGlsset = segment.find<EnrtUnorderedSet>("XX").first;
for (auto& ele : (*myGlsset)) {
std::cout << ele << std::endl;
std::cout<<ele.ern_name << std::endl;
}
}
but B throw "Exception thrown at 0x75181267 (ucrtbase.dll) in tryINsertSPACE.exe: 0xC0000005: Access violation reading location 0x00BFFEB8." at std::cout<<ele.ern_name << std::endl;
.
However when I change A program with a.ern_name = "HANGZHOULMMRW25"
,it works fine.Lately I tried a.ern_name = "HANGZHOULMMRW250"
,it throws exception again, it seems related to the length of string instead of the space problem.
Strings are dynamic containers, just like the set. The small size might mask this fact due to Small String Optimization.
You need to make it use the shared memory allocator to be able to use it. Alternatively, if you can fix the capacity, consider Boost StaticString:
using String = boost::static_string<128>;
See this modernized version of your program:
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/static_string.hpp>
#include <boost/unordered_set.hpp>
#include <iostream>
namespace bip = boost::interprocess;
using String = boost::static_string<128>;
namespace Enr {
using boost::hash_value; // enabling ADL
constexpr auto tableName = "enrtTableSet";
struct Record {
String id, frequency, /*mag_var, */ name;
bool operator==(Record const& other) const = default;
bool empty() const { return id.empty() && frequency.empty(); }
friend std::ostream& operator<<(std::ostream& os, Record const& rec) {
return os << "id: " << rec.id //
<< " frequency: " << rec.frequency //
<< " name: " << rec.name;
}
friend size_t hash_value(Record const& rec) { return hash_value(std::tie(rec.id, rec.frequency)); }
};
} // namespace Enr
namespace Shared {
constexpr auto Name = "MySharedMemory";
using Segment = bip::managed_shared_memory;
using Manager = Segment::segment_manager;
template <typename T> using Alloc = bip::allocator<T, Manager>;
template <typename T, typename H = boost::hash<T>, typename P = std::equal_to<T>>
using Set = boost::unordered_set<T, H, P, Alloc<T>>;
void remove() { Segment::device_type::remove(Name); }
}; // namespace Shared
using Set = Shared::Set<Enr::Record>;
void programA() {
Shared::remove();
Shared::Segment shm(bip::create_only, Shared::Name, 65536);
auto& mSet = *shm.construct<Set>(Enr::tableName)(shm.get_segment_manager());
mSet.insert(Enr::Record{"AA", "444", "HANGZHOULMM RW25"});
mSet.insert(Enr::Record{"BB", "555", "HANGZHOULMMRW250 more more more more"});
}
void programB() {
Shared::Segment segment(bip::open_read_only, Shared::Name);
Set& myGlsset = *segment.find<Set>(Enr::tableName).first;
for (auto& ele : myGlsset)
std::cout << ele << std::endl;
}
int main(int argc, char**) {
if (argc <= 1)
programA();
else
programB();
}
Testing with
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lrt
./a.out
./a.out B
Prints
id: AA frequency: 444 name: HANGZHOULMM RW25
id: BB frequency: 555 name: HANGZHOULMMRW250 more more more more
They become less convenient because you keep passing allocators around:
namespace Shared {
constexpr auto Name = "MySharedMemory";
using Segment = bip::managed_shared_memory;
using Manager = Segment::segment_manager;
template <typename T> using Alloc = bip::allocator<T, Manager>;
template <typename T, typename H = boost::hash<T>, typename P = std::equal_to<T>>
using Set = boost::unordered_set<T, H, P, Alloc<T>>;
using String = boost::container::basic_string<char, std::char_traits<char>, Alloc<char>>;
void remove() { Segment::device_type::remove(Name); }
}; // namespace Shared
namespace Enr {
using boost::hash_value; // enabling ADL
constexpr auto tableName = "enrtTableSet";
struct Record {
using allocator_type = Shared::Alloc<char>; // std::uses_allocator protocol
Record(allocator_type alloc, char const* i, char const* f, char const* n)
: id(i, alloc)
, frequency(f, alloc)
, name(n, alloc) {}
Shared::String id, frequency, /*mag_var, */ name;
bool operator==(Record const& other) const = default;
bool empty() const { return id.empty() && frequency.empty(); }
friend std::ostream& operator<<(std::ostream& os, Record const& rec) {
return os << "id: " << rec.id //
<< " frequency: " << rec.frequency //
<< " name: " << rec.name;
}
friend size_t hash_value(Record const& rec) { return hash_value(std::tie(rec.id, rec.frequency)); }
};
} // namespace Enr
using Set = Shared::Set<Enr::Record>;
void programA() {
Shared::remove();
Shared::Segment shm(bip::create_only, Shared::Name, 65536);
auto mgr = shm.get_segment_manager();
auto& mSet = *shm.construct<Set>(Enr::tableName)(mgr);
mSet.insert(Enr::Record{mgr, "AA", "444", "HANGZHOULMM RW25"});
mSet.insert(Enr::Record{mgr, "BB", "555", "HANGZHOULMMRW250 more more more more"});
}
With the same output as before.
Guessing that frequency might actually need to be numeric and using scoped-allocators to alleviate the need to pass around allocators manually:
using Id = boost::static_string<16>;
using Frequency = double;
using Name = Shared::String;
Now the advantage of the uses_allocator protocol comes to light:
mSet.emplace("AA", 444, "HANGZHOULMM RW25");
mSet.emplace("BB", 555, "HANGZHOULMMRW250 more more more more");
See it Live On Coliru
#include <boost/container/scoped_allocator.hpp>
#include <boost/container/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/static_string.hpp>
#include <boost/unordered_set.hpp>
#include <iostream>
namespace bip = boost::interprocess;
namespace Shared {
namespace bc = boost::container;
constexpr auto ShmName = "MySharedMemory";
using Segment = bip::managed_shared_memory;
using Manager = Segment::segment_manager;
template <typename T> using Alloc = bc::scoped_allocator_adaptor<bip::allocator<T, Manager>>;
template <typename T, typename H = boost::hash<T>, typename P = std::equal_to<T>>
using Set = boost::unordered_set<T, H, P, Alloc<T>>;
using String = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
void remove() { Segment::device_type::remove(ShmName); }
}; // namespace Shared
namespace Enr {
using Id = boost::static_string<16>;
using Frequency = double;
using Name = Shared::String;
constexpr auto tableName = "enrtTableSet";
using boost::hash_value; // enabling ADL
struct Record {
using allocator_type = Name::allocator_type; // std::uses_allocator protocol
Record(char const* i = "", Frequency f = {}, char const* n = "", allocator_type alloc = {})
: id(i)
, frequency(f)
, name(n, alloc) {}
Id id;
Frequency frequency;
Name name;
bool operator==(Record const& other) const = default;
bool empty() const { return id.empty() && frequency == Frequency{}; }
friend std::ostream& operator<<(std::ostream& os, Record const& rec) {
return os << "id: " << rec.id //
<< " frequency: " << rec.frequency //
<< " name: " << rec.name;
}
friend size_t hash_value(Record const& rec) { return hash_value(std::tie(rec.id, rec.frequency)); }
};
} // namespace Enr
using Set = Shared::Set<Enr::Record>;
void programA() {
Shared::remove();
Shared::Segment shm(bip::create_only, Shared::ShmName, 65536);
auto& mSet = *shm.construct<Set>(Enr::tableName)(shm.get_segment_manager());
mSet.emplace("AA", 444, "HANGZHOULMM RW25");
mSet.emplace("BB", 555, "HANGZHOULMMRW250 more more more more");
}
void programB() {
Shared::Segment segment(bip::open_read_only, Shared::ShmName);
Set& myGlsset = *segment.find<Set>(Enr::tableName).first;
for (auto& ele : myGlsset)
std::cout << ele << std::endl;
}
int main(int argc, char**) {
if (argc <= 1)
programA();
else
programB();
}
Again with similar output:
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lrt
./a.out
./a.out B
id: AA frequency: 444 name: HANGZHOULMM RW25
id: BB frequency: 555 name: HANGZHOULMMRW250 more more more more