问题
I need to allocate a user defined struct in shared memory using boost's managed_shared_memory but I am not sure how to go about this correctly. It appears that the key to getting this to work correctly is the use of custom allocators. I have successfully been able to make this work with a simple POD uint64_t type, however things become more involved when I try to put more complex types in shared memory. An example of a simple struct I would like to place in shared memory would be something along the following lines:
struct SharedMemStruct {
explicit SharedMemStruct() = default;
explicit SharedMemStruct(
const bool bField0,
const std::string& rField1,
const uint32_t& rField2)
: mField0(bField0)
, mField1(rField1)
, mField2(rField2)
{}
bool mField0;
std::string mField1;
uint32_t mField2;
};
With the POD type I successfully initialized the shared memory as follows (placing the 64 bit shared value in a named area called "FaultReport" that can be shared with another process.
// Remove shared memory on construction and destruction
struct shm_remove {
shm_remove() { shared_memory_object::remove("albfd_shared_mem"); }
~shm_remove() { shared_memory_object::remove("albfd_shared_mem"); }
} remover;
// create a new shared memory segment 2K size
managed_shared_memory managed_shm(create_only, "albfd_shared_mem", 2048);
// construct a FaultReport - default to no faults
auto pFaultWrapper = managed_shm.construct<UtlSharedIPCWrapper<uint64_t>>("FaultReport")(0);
In another process that requires access to the above FaultReport, it is a simple matter as follows:
// create a new shared memory segment 2K size
managed_shared_memory managed_shm(open_only, "albfd_shared_mem");
// shared memory must exist in
shared_memory_object shm(open_only, "albfd_shared_mem", read_write);
// lookup the FaultReport shared memory wrapper
auto pFaultReport = managed_shm.find<UtlSharedIPCWrapper<uint64_t>>("FaultReport").first;
// initialize the bit-set from the current shared memory fault report
std::bitset<64> bitSet(pFaultReport->getSharedData());
The UtlSharedIPCWrapper provides me safe access to its data type T
template<typename T>
struct UtlSharedIPCWrapper {
private:
using upgradable_mutex_type = boost::interprocess::interprocess_upgradable_mutex;
mutable upgradable_mutex_type mMutex;
/*volatile*/ T mSharedData;
public:
// explicit struct constructor used to initialize directly from existing memory
explicit UtlSharedIPCWrapper(const T& rInitialValue)
: mSharedData(rInitialValue)
{}
T getSharedData() const {
boost::interprocess::sharable_lock<upgradable_mutex_type> lock(mMutex);
return mSharedData;
}
void setSharedData(const T& rSharedData) {
boost::interprocess::scoped_lock<upgradable_mutex_type> lock(mMutex);
// update the shared data copy mapped - scoped locked used if exception thrown
// a bit like the lock guard we normally use
this->mSharedData = rSharedData;
}
};
EDIT:
So after doing a bit of experimentation - in particular after realizing that my SharedIPCStruct
contains multiple variable string fields (and therefore effectively requires pointers or some nature), I rewrote the struct in the header file and have shown it below. The key here seems to be having some sort of special stl replacement string that uses the boost's shared memory model. To this end, I defined the following for the string type that needed to be stored in my structure:
using char_allocator = boost::interprocess::managed_shared_memory::allocator<char>::type;
using shm_string = boost::interprocess::basic_string<char, std::char_traits<char>, char_allocator>;
One of these char_allocators is passed as the last argument into my custom struct to assist with converting normal std::string
s to these new shm_string
s. I don't really understand what is going under the covers here but it seemed to work - stepping through boost code is pretty much template nonsense at this point.
#pragma once
// SYSTEM INCLUDES
#include <string>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
using char_allocator = boost::interprocess::managed_shared_memory::allocator<char>::type;
using shm_string = boost::interprocess::basic_string<char, std::char_traits<char>, char_allocator>;
// FORWARD DECLARATIONS
struct SharedIPCStruct {
explicit SharedIPCStruct (
const std::string& rString1,
const std::string& rString2,
const uint32_t& rHashValue,
const std::string& rString3,
const char_allocator& ca)
: mField1(rString1.cbegin(), rString1.cend(), ca)
, mField2(rString2.cbegin(), rString2.cend(), ca)
, mField3(rHashValue)
, mField4(rString3.cbegin(), rString3.cend(), ca)
{}
// note these are not std::strings
shm_string mField1;
shm_string mField2;
uint32_t mField3;
shm_string mField4;
};
Now with all this in place, allocating the shared memory structure was as simple as this:
char_allocator ca(managed_shm.get_allocator<char>());
// Create an object of SharedIPCStruct initialized to {"", "", 0, "", ca}
auto *pSHMStruct= managed_shm.construct<SharedIPCStruct>
("SharedIPCStruct") // name of the object
("", "", 0, "", ca); // ctor args
This works well and in another process I can perform the counterpart to search for the existing SharedIPCStruct with the following
// lookup the SharedIPCStruct
auto *pSHMStruct = managed_shm.find<SharedIPCStruct>("SharedIPCStruct").first;
The problem still remains however that I cannot construct a UtlSharedIPCWrapper<SharedIPCStruct>
in shared memory. I wanted to do this to provide safe access to the SharedIPCStruct
.
This compiles and runs due to the fact that the template argument type for the UtlSharedIPCWrapper
is a POD type:
auto pFaultWrapper = managed_shm.construct<UtlSharedIPCWrapper<uint64_t>>("FaultReport")(0);
However, I replace
// Create an object of SharedIPCStruct initialized to {"", "", 0, "", ca}
auto *pSHMStruct= managed_shm.construct<SharedIPCStruct>
("SharedIPCStruct") // name of the object
("", "", 0, "", ca); // ctor args
with the templatized wrapper like I did above for the POD type for that wrapping a custom data structure
// Create an object of SharedIPCStruct initialized to {"", "", 0, "", ca}
auto *pSHMStruct= managed_shm.construct<UtlSharedIPCWrapper<SharedIPCStruct>>
("SharedIPCStruct") // name of the object
("", "", 0, "", ca); // ctor args
I get a ton of unintelligible errors. Can someone explain what is going on here and how I should write this correctly in order to use my wrapper. Also what is general rule (if any) that one has to apply to use custom data structures in shared memory? I figured out that std::strings could not be used but what about floats, etc and other types.
main.cpp
1>c:\main\extlibs\boost_1_60_0\boost\interprocess\detail\named_proxy.hpp(83): error C2661: 'UtlSharedIPCWrapper<SharedIPCStruct>::UtlSharedIPCWrapper': no overloaded function takes 5 arguments
1> c:\main\extlibs\boost_1_60_0\boost\interprocess\detail\named_proxy.hpp(71): note: see reference to function template instantiation 'void boost::interprocess::ipcdetail::CtorArgN<T,false,const char (&)[1],const char (&)[1],int,const char (&)[1],char_allocator &>::construct<0,1,2,3,4>(void *,boost::interprocess::ipcdetail::false_,const boost::container::container_detail::index_tuple<0,1,2,3,4> &)' being compiled
1> with
1> [
1> T=UtlSharedIPCWrapper<SharedIPCStruct>
1> ]
1> c:\main\extlibs\boost_1_60_0\boost\interprocess\detail\named_proxy.hpp(71): note: see reference to function template instantiation 'void boost::interprocess::ipcdetail::CtorArgN<T,false,const char (&)[1],const char (&)[1],int,const char (&)[1],char_allocator &>::construct<0,1,2,3,4>(void *,boost::interprocess::ipcdetail::false_,const boost::container::container_detail::index_tuple<0,1,2,3,4> &)' being compiled
1> with
1> [
1> T=UtlSharedIPCWrapper<SharedIPCStruct>
1> ]
1> c:\main\extlibs\boost_1_60_0\boost\interprocess\detail\named_proxy.hpp(68): note: while compiling class template member function 'void boost::interprocess::ipcdetail::CtorArgN<T,false,const char (&)[1],const char (&)[1],int,const char (&)[1],char_allocator &>::construct_n(void *,size_t,size_t &)'
1> with
1> [
1> T=UtlSharedIPCWrapper<SharedIPCStruct>
1> ]
1> c:\main\extlibs\boost_1_60_0\boost\interprocess\detail\named_proxy.hpp(128): note: see reference to class template instantiation 'boost::interprocess::ipcdetail::CtorArgN<T,false,const char (&)[1],const char (&)[1],int,const char (&)[1],char_allocator &>' being compiled
1> with
1> [
1> T=UtlSharedIPCWrapper<SharedIPCStruct>
1> ]
1> c:\main\dlmu\albfd-0339.1\src\albfd\main.cpp(224): note: see reference to function template instantiation 'T *boost::interprocess::ipcdetail::named_proxy<boost::interprocess::segment_manager<CharType,MemoryAlgorithm,IndexType>,T,false>::operator ()<const char(&)[1],const char(&)[1],int,const char(&)[1],char_allocator&>(const char (&)[1],const char (&)[1],int &&,const char (&)[1],char_allocator &) const' being compiled
1> with
1> [
1> T=UtlSharedIPCWrapper<SharedIPCStruct>,
1> CharType=char,
1> MemoryAlgorithm=boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,ptrdiff_t,uintptr_t,0>,0>,
1> IndexType=boost::interprocess::iset_index
1> ]
1> c:\main\dlmu\albfd-0339.1\src\albfd\main.cpp(224): note: see reference to function template instantiation 'T *boost::interprocess::ipcdetail::named_proxy<boost::interprocess::segment_manager<CharType,MemoryAlgorithm,IndexType>,T,false>::operator ()<const char(&)[1],const char(&)[1],int,const char(&)[1],char_allocator&>(const char (&)[1],const char (&)[1],int &&,const char (&)[1],char_allocator &) const' being compiled
1> with
1> [
1> T=UtlSharedIPCWrapper<SharedIPCStruct>,
1> CharType=char,
1> MemoryAlgorithm=boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,ptrdiff_t,uintptr_t,0>,0>,
1> IndexType=boost::interprocess::iset_index
1> ]
来源:https://stackoverflow.com/questions/35204104/sharing-a-custom-struct-using-boostinterprocess