Я пытаюсь создать карту диспетчера общей памяти в boost вместе с классом синхронизации для карты. Раньше я делал что-то примерно то же самое с векторами, и это работало нормально. Я получаю сообщение об ошибке, упомянутое в заголовке, при использовании карт. Я делаю все это неправильно? Код в этом репо
#include <iostream>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
namespace node_cluster_cache {
using namespace boost;
using namespace boost::interprocess;
using std::pair;
typedef uint64_t int64;
typedef uint32_t int32;
typedef std::array<int32, SIZE> msg_arr;
typedef std::array<char, ACCESS_SIZE> access_name;
template<class T>
class SynchronizedMap {
public:
typedef allocator<T, managed_shared_memory::segment_manager> allocator_type;
typedef lock_guard<interprocess_mutex> lock;
private:
map<access_name, T, allocator_type> _map;
mutable interprocess_mutex io_mutex;
mutable interprocess_condition wait_condition;
public:
SynchronizedMap(allocator_type alloc) : _map(alloc) {};
void insert(const access_name name, T msg_part) {
lock _lock(io_mutex);
_map.insert(pair<access_name, T>(name, msg_part));
}
int32 size() const {
lock _lock(io_mutex);
return _map.size();
}
bool empty() const {
lock _lock(io_mutex);
return _map.empty();
}
void clear() {
lock _lock(io_mutex);
_map.clear();
}
bool erase(access_name name) {
lock _lock(io_mutex);
return _map.erase(name);
}
bool erase(typename map<access_name, T>::iterator it) {
lock _lock(io_mutex);
return _map.erase(it);
}
};
};
struct Message {
int64 bit_no;
int64 pid;
int64 part_no;
int64 size;
msg_arr data;
Message() {}
Message(int64 bn, int64 p, int64 pn, int64 s, msg_arr& d) {
bit_no = bn;
pid = p;
part_no = pn;
size = s;
safe_copy(data, d);
}
Message(int64 bn, int64 p, int64 s, msg_arr& d) {
bit_no = bn;
pid = p;
part_no = 0;
size = s;
safe_copy(data, d);
}
};
template<size_t N>
static inline void safe_copy(std::array<int32, N>& dst, std::array<int32, N>& src) {
#undef min
std::copy_n(src.data(), std::min(src.size(), N), dst.data());
dst.back() = 0;
}
this->shmem = new managed_shared_memory(create_only, "node_cluster_cache", SHMEM_SIZE);
this->alloc_inst = new SynchronizedMap<Message>::allocator_type(this->shmem->get_segment_manager());
this->cache_map = this->shmem->construct<SynchronizedMap<Message> >("data_vector")(*(this->alloc_inst));
msg_arr test{ 123,456,789 };
access_name name = { 't', 'e', '\0' };
int64 a = 1;
int64 b = 2;
Message c(a, b, 4, test);
this->cache_map->insert(name, c);
Severity Code Description Project File Line Suppression State
Error C2664
'node_cluster_cache::SynchronizedMap<node_cluster_cache::Message>::SynchronizedMap
(const node_cluster_cache::SynchronizedMap<node_cluster_cache::Message> &)': cannot convert argument 1 from
'boost::interprocess::allocator<T,boost::interprocess::segment_manager<CharType,MemoryAlgorithm,IndexType>>' to 'const
node_cluster_cache::SynchronizedMap<node_cluster_cache::Message> &' node-cluster-cache
C:\boost_1_74_0\boost\interprocess\detail\named_proxy.hpp 85
Изменение allocator_type на пару значений и определение компаратора для std::array<char, ACCESS_SIZE> решило проблему.
typedef pair<const access_name, T> value_type;
typedef allocator<value_type, managed_shared_memory::segment_manager> allocator_type;
struct cmp_str {
bool operator()(access_name a, access_name b) const {
for (int i = 0; ; i++) {
if (a[i] != b[i]) {
return a[i] < b[i] ? -1 : 1;
}
if (a[i] == '\0') {
return 0;
}
}
}
};
map<access_name, T, cmp_str, allocator_type> _map;
Звучит так, будто все могло быть в 10 раз проще. Почему массивы int32, но все же «ведут себя» так, как будто они должны быть строками с нулевым завершением? Вы уверены, что не хотите просто wchar_t
, а затем wstring
с распределителем? Вы снова сделали все новым/удаленным, выделенным, что максимизирует потенциал ошибок. Может быть, вы могли бы описать, чего вы хотите достичь, и посмотреть, сможем ли мы помочь вам придумать более простые способы.
@sehe Я пытаюсь создать систему кэширования, которую все дочерние процессы сервера nodejs могут использовать в качестве простого унифицированного кеша в качестве собственного надстройки узла вместо запуска Redis или чего-то еще в этом роде. Я подумал, что преобразование всех моих данных в буферы int32 может упростить хранение. (обратите внимание, что некоторые кэшированные данные могут быть текстом, двоичными данными из изображений или видео и т. д. и т. д.)
Каков масштаб? Могут ли быть фиксированные ограничения (это значительно улучшит производительность) и всегда ли тип ключа (например, здесь access_name
) будет похож на POD? Все эти факторы позволяют значительно упростить структуры данных для разделяемой памяти и повысить производительность.
О, теперь я заметил, что объявление access_name
было похоронено в первом фрагменте кода. Итак, ПОД.
@sehe вместо использования структуры сообщения, показанной выше, теперь вместо этого я использую другой распределитель, чтобы поместить вектор в качестве значения для значения ключа карты. Vector, потому что я не могу быть уверен, насколько велики будут кэшированные данные. Будет ли использование N массивов постоянного размера с одним и тем же именем ключа обеспечивать более высокую производительность по сравнению с использованием вектора? код
Я бы так не думал, но если у вас, скажем, 90% значений <1024, вы можете оптимизировать для этого (см., например, boost::container::small_vector
)
Не могли бы вы изменить сообщение об ошибке на более читаемый формат?