C++类构造中的指针

发布于 2025-01-12 20:31:12 字数 3237 浏览 2 评论 0 原文

我这几天一直在努力解决这个问题。

在这个程序中,有一个从字符串键到结构体值的unordered_map。这个想法是可行的,但是当我将 unordered_map 放入一个类中,然后更轻松地访问它时,我收到一个错误:

Eliminare.exe!std::_Hash<std::_Umap_traits<std::string,Data,std::_Uhash_compare<std::string,std::hash<std::string>,std::equal_to<std::string>>,boost::interprocess::allocator<std::pair<std::string,Data>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>,0>>::_Find_last<std::string>(const std::string & _Keyval, const unsigned __int64 _Hashval) Row 1510    C++
    Eliminare.exe!std::unordered_map<std::string,Data,std::hash<std::string>,std::equal_to<std::string>,boost::interprocess::allocator<std::pair<std::string,Data>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>::at(const std::string & _Keyval) Riga 387    C++
Eliminare.exe!MapCreator::getValue(std::string Key) Row 37  C++
Eliminare.exe!main() Row 7  C++

这是主要内容:

#include "MapCreator.h"

int main() {
    MapCreator mappaClass;

    auto values = mappaClass.getValue("value");
}

虽然这是在 MapCreator.h 中:

#pragma once
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <unordered_map>
#include<iostream>
typedef struct Data
{
    int a;
    int b;
}dataClass;

namespace bost = boost::interprocess;
typedef std::string    KeyType;
typedef dataClass MappedType;
typedef std::pair<std::string, dataClass> ValueType;
typedef bost::allocator<ValueType, bost::managed_shared_memory::segment_manager> ShmemAllocator;
typedef std::unordered_map<KeyType, MappedType, std::hash<KeyType>, std::equal_to<KeyType>, ShmemAllocator> MySHMMap;

class MapCreator
{
public:
    MySHMMap* mappa = nullptr;
    MapCreator() {
        bost::shared_memory_object::remove(nameMemory);
        bost::managed_shared_memory segment(bost::create_only, nameMemory, sizeDeclared);
        ShmemAllocator alloc_inst(segment.get_segment_manager());

        mappa = segment.construct<MySHMMap>("MySHMMapName")(alloc_inst);
        dataClass value;
        value.a = 12;
        value.b = 1111;
        mappa->insert(std::pair<std::string, dataClass>("value", value));
        std::cout << mappa->at("value").a;

    }
    dataClass getValue(std::string Key) {
        return mappa->at(Key);
    }
private:
    const int sizeDeclared = 65536;
    const char nameMemory[20] = "SharedMemoryName";
};

cout ”在MapCreator构造函数中正确打印“12”,但是使用getValue函数,它打印上面提到的错误。

感谢您的帮助,如果您对代码有任何疑问,请提问。

信息:

  • 编译器:Visual C++ 和 Visual Studio 2022
  • 没有该类的代码运行良好,
  • boost 类的版本是:1.78.0

I'm trying to solve this problem from several days.

In this program there is an unordered_map which from string key to structure value. The idea works, but when I put the unordered_map in a class and then access it more easily I get an error:

Eliminare.exe!std::_Hash<std::_Umap_traits<std::string,Data,std::_Uhash_compare<std::string,std::hash<std::string>,std::equal_to<std::string>>,boost::interprocess::allocator<std::pair<std::string,Data>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>,0>>::_Find_last<std::string>(const std::string & _Keyval, const unsigned __int64 _Hashval) Row 1510    C++
    Eliminare.exe!std::unordered_map<std::string,Data,std::hash<std::string>,std::equal_to<std::string>,boost::interprocess::allocator<std::pair<std::string,Data>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>::at(const std::string & _Keyval) Riga 387    C++
Eliminare.exe!MapCreator::getValue(std::string Key) Row 37  C++
Eliminare.exe!main() Row 7  C++

This is the main:

#include "MapCreator.h"

int main() {
    MapCreator mappaClass;

    auto values = mappaClass.getValue("value");
}

While this is in MapCreator.h:

#pragma once
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <unordered_map>
#include<iostream>
typedef struct Data
{
    int a;
    int b;
}dataClass;

namespace bost = boost::interprocess;
typedef std::string    KeyType;
typedef dataClass MappedType;
typedef std::pair<std::string, dataClass> ValueType;
typedef bost::allocator<ValueType, bost::managed_shared_memory::segment_manager> ShmemAllocator;
typedef std::unordered_map<KeyType, MappedType, std::hash<KeyType>, std::equal_to<KeyType>, ShmemAllocator> MySHMMap;

class MapCreator
{
public:
    MySHMMap* mappa = nullptr;
    MapCreator() {
        bost::shared_memory_object::remove(nameMemory);
        bost::managed_shared_memory segment(bost::create_only, nameMemory, sizeDeclared);
        ShmemAllocator alloc_inst(segment.get_segment_manager());

        mappa = segment.construct<MySHMMap>("MySHMMapName")(alloc_inst);
        dataClass value;
        value.a = 12;
        value.b = 1111;
        mappa->insert(std::pair<std::string, dataClass>("value", value));
        std::cout << mappa->at("value").a;

    }
    dataClass getValue(std::string Key) {
        return mappa->at(Key);
    }
private:
    const int sizeDeclared = 65536;
    const char nameMemory[20] = "SharedMemoryName";
};

The "cout" in the MapCreator constructor correctly prints "12", but using the getValue function, it prints the error mentioned above.

Thanks for your help, if you have any questions about the code, just ask.

INFO:

  • Compiler: Visual C++ with Visual Studio 2022
  • the code without the class works well
  • the version of boost class is: 1.78.0

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

冷弦 2025-01-19 20:31:12

有多个问题。

第一个是 segment 是构造函数中的局部变量,这意味着 mappa 在构造函数返回后是一个悬空指针。

其次,更大的问题是您的映射正确使用共享内存分配器,但字符串却没有。字符串也是动态分配的,因此除非您使字符串也使用共享内存,否则您的解决方案将无法工作。

开始修复:

namespace bip = boost::interprocess;
namespace bc  = boost::container;
using Segment = bip::managed_shared_memory;
using Mgr     = Segment::segment_manager;

template <typename T> using Alloc = bip::allocator<T, Mgr>;
using MyString   = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
using KeyType    = MyString;
using MappedType = dataClass;
using ValueType  = std::pair<KeyType const, MappedType>;
using MySHMMap   = std::unordered_map<KeyType, MappedType, std::hash<MyString>,
                                    std::equal_to<MyString>, Alloc<ValueType>>;

请注意,在我的编译器上,还需要在 ValueType 中使用 KeyType const

在执行中存在额外的复杂性:segment code> 必须是一个字段,但它只能从初始化列表构造(或使用 NSMI)。这意味着我们必须让 remove 也出现在初始化列表中(在此之前)。这是我的初步尝试:

template <typename T> using Alloc = bip::allocator<T, Mgr>;
using MyString   = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
using KeyType    = MyString;
using MappedType = dataClass;
using ValueType  = std::pair<KeyType const, MappedType>;
using MySHMMap = boost::unordered_map<KeyType, MappedType, boost::hash<MyString>,
                         std::equal_to<MyString>, Alloc<ValueType>>;

class MapCreator {
  public:
    MapCreator()
        : mappa(segment.find_or_construct<MySHMMap>("MySHMMapName")(
              segment.get_segment_manager()))
    {
        mappa->emplace(MyString("value", segment.get_segment_manager()),
                       dataClass{12, 1111});
    }

    dataClass getValue(std::string key) const {
        return mappa->at(MyString(key.c_str(), segment.get_segment_manager()));
    }

  private:
    static constexpr int sizeDeclared = 65536;

    // note: declaration order defines initialization order!
    std::string_view nameMemory = "SharedMemoryName";

    struct pre_remover {
        pre_remover(const char* name)
        {
            bip::shared_memory_object::remove(name);
        }
    } pre_remove{nameMemory.data()};

    Segment segment{bip::create_only, nameMemory.data(), sizeDeclared};
    MySHMMap* mappa = nullptr;
};

注释

  • 也根本没有理由为该名称分配 20 个字符的固定大小缓冲区。
  • 成员声明的顺序就是它们初始化的顺序。这很重要,因为 mappa 需要位于 segment 之后,例如

我发现 std::hash 实际上并不是专门用于 MyString 实例,因为我知道我想改变这一点,无论如何我不会浪费时间来创建哈希函数对象。相反,我切换到 boost::unordered_map 以便使用 boost::hash<>

这已经可以工作Live On Coliru

打印:

12, 1111

改进

  1. 作用域分配器

    allocator/get_segment_manager() 周围的事情非常笨拙。使用作用域分配器可以缓解这种情况:

    模板 <类型名称 T>
    使用 Alloc = bc::scoped_allocator_adaptor>;
    使用 MyString = bc::basic_string, Alloc>;
    使用 KeyType = MyString;
    
    模板 <类型名称 K,类型名称 V,类型名称 H = boost::hash,
              类型名 Eq = std::equal_to>;
    使用 SHMap = boost::unordered_map>>;
    
    使用 MySHMMap = SHMap;
    

    现在你可以“简单地”写:

    mappa->emplace("value", Data{12, 1111});
    

    而不是之前的复杂版本。 在 Coliru 上直播

  2. 异构键查找

    在我们构造 MyString 的另一个地方(只是为了在 at() 调用之后将其丢弃...),我们可以使用 boost: :unordered_map兼容键查找

    Data getValue(std::string_view key) const {
        auto it = mappa->find(key, mappa->hash_function(), mappa->key_eq());
        返回它!= mappa->end() ?它->第二
                                  :抛出 std::range_error("getValue");
    }
    

    这需要哈希和相等性兼容:

    使用 MySHMMap = SHMap,
                           std::equal_to>;
    

    查看在 Coliru 上直播

  3. 使用 C++20 标准 unordered_map 代替(请参阅https://en.cppreference.com/w/cpp/container/unordered_map/find):

    struct Hash : std::hash; {
        使用 is_transparent = std::true_type;
    };
    结构 Eq : std::equal_to {
        使用 is_transparent = std::true_type;
    };
    使用 MySHMMap = SHMap;
    

    然后:

     数据 getValue(std::string_view key) const {
    #ifdef __cpp_lib_generic_unordered_lookup
            auto it = mappa->find(key);
    #别的
            auto it = mappa->find(
                MyString(key.data()、key.size()、segment.get_segment_manager()));
    
    #endif
            返回它!= mappa->end() ?它->第二
                                      : 抛出 std::range_error("getValue");
        }
    

    也可以查看在 Coliru 上直播< /p>

There are multiple issues.

The first one is that segment is a local variable in your constructor, meaning that mappa is a dangling pointer after the constructor returns.

Second, bigger, problem is that your map uses a shared memory allocator correctly, but the strings do NOT. String are also dynamically allocating, so your solution cannot work unless you make the string also use shared memory.

Starting the fix:

namespace bip = boost::interprocess;
namespace bc  = boost::container;
using Segment = bip::managed_shared_memory;
using Mgr     = Segment::segment_manager;

template <typename T> using Alloc = bip::allocator<T, Mgr>;
using MyString   = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
using KeyType    = MyString;
using MappedType = dataClass;
using ValueType  = std::pair<KeyType const, MappedType>;
using MySHMMap   = std::unordered_map<KeyType, MappedType, std::hash<MyString>,
                                    std::equal_to<MyString>, Alloc<ValueType>>;

Note that on my compiler it's also required to use KeyType const in the ValueType.

In the execution there's additional complexity: segment must be a field, but it can only be constructed from the initializer list (or using NSMI). That means that we have to make the remove also occur (before that) in the initializer list. Here's my initial attempt:

template <typename T> using Alloc = bip::allocator<T, Mgr>;
using MyString   = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
using KeyType    = MyString;
using MappedType = dataClass;
using ValueType  = std::pair<KeyType const, MappedType>;
using MySHMMap = boost::unordered_map<KeyType, MappedType, boost::hash<MyString>,
                         std::equal_to<MyString>, Alloc<ValueType>>;

class MapCreator {
  public:
    MapCreator()
        : mappa(segment.find_or_construct<MySHMMap>("MySHMMapName")(
              segment.get_segment_manager()))
    {
        mappa->emplace(MyString("value", segment.get_segment_manager()),
                       dataClass{12, 1111});
    }

    dataClass getValue(std::string key) const {
        return mappa->at(MyString(key.c_str(), segment.get_segment_manager()));
    }

  private:
    static constexpr int sizeDeclared = 65536;

    // note: declaration order defines initialization order!
    std::string_view nameMemory = "SharedMemoryName";

    struct pre_remover {
        pre_remover(const char* name)
        {
            bip::shared_memory_object::remove(name);
        }
    } pre_remove{nameMemory.data()};

    Segment segment{bip::create_only, nameMemory.data(), sizeDeclared};
    MySHMMap* mappa = nullptr;
};

Notes

  • there was also no reason at all to allocate a fixed size buffer of 20 chars for the name.
  • The order in which members are declared is the order in which they're initalized. That's important because mappa needs to come after segment, e.g.

I found out that std::hash isn't actually specialized for the MyString instance, and because I know down the line I'd want to change that anyways I'm not wasting time to create a Hash function object. Instead, I'm switching to boost::unordered_map just so boost::hash<> is used

This is already working Live On Coliru

Prints:

12, 1111

Improvements

  1. Scoped Allocators

    Things are pretty clunky around the allocator/get_segment_manager(). Using scoped-allocators you can alleviate that:

    template <typename T>
    using Alloc      = bc::scoped_allocator_adaptor<bip::allocator<T, Mgr>>;
    using MyString   = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
    using KeyType    = MyString;
    
    template <typename K, typename V, typename H = boost::hash<K>,
              typename Eq = std::equal_to<K>>
    using SHMap = boost::unordered_map<K, V, H, Eq, Alloc<std::pair<K const, V>>>;
    
    using MySHMMap = SHMap<MyString, Data>;
    

    Now you can "simply" write:

    mappa->emplace("value", Data{12, 1111});
    

    Instead of the complicated version before. Live On Coliru

  2. Heterogenous Key Lookup

    The other place where we are constructing MyString (just to throw it away after the at() call...), we can use boost::unordered_map's compatible-key lookup:

    Data getValue(std::string_view key) const {
        auto it = mappa->find(key, mappa->hash_function(), mappa->key_eq());
        return it != mappa->end() ? it->second
                                  : throw std::range_error("getValue");
    }
    

    This requires the hash and equality to be compatible:

    using MySHMMap = SHMap<MyString, Data, std::hash<std::string_view>,
                           std::equal_to<std::string_view>>;
    

    See it Live On Coliru

  3. Using C++20 standard unordered_map instead (see https://en.cppreference.com/w/cpp/container/unordered_map/find):

    struct Hash : std::hash<std::string_view> {
        using is_transparent = std::true_type;
    };
    struct Eq : std::equal_to<std::string_view> {
        using is_transparent = std::true_type;
    };
    using MySHMMap = SHMap<MyString, Data, Hash, Eq>;
    

    And then:

        Data getValue(std::string_view key) const {
    #ifdef __cpp_lib_generic_unordered_lookup
            auto it = mappa->find(key);
    #else
            auto it = mappa->find(
                MyString(key.data(), key.size(), segment.get_segment_manager()));
    
    #endif
            return it != mappa->end() ? it->second
                                      : throw std::range_error("getValue");
        }
    

    See it Live On Coliru as well

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文