Этот вопрос относится к предыдущий вопрос Я спросил год назад.
Я использую WordNet 3.0, который был написан давным-давно на C (около 1996 года, я думаю).
Чтобы попытаться избежать управления памятью, я спросил, как я могу обернуть std::shared_ptr
вокруг старого struct Synset
используется WordNet.
справочная таблица показывает функции WordNet-3.0 и проверку фактический заголовок показывает, что SynsetPtr
,
какие функции traceptrs_ds
а также findtheinfo_ds
возвращаются, есть указатели на ss
или же Synset
,
В моем предыдущий вопрос Я решил удалить указатель,
а затем оберните его вокруг shared_ptr
вот так:
std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
(findtheinfo_ds(searchstr, pos, ptr_type, sense_num),
free_syns);
std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
(traceptrs_ds(synptr, ptr_type, pos, depth),
free_synset);
У меня есть две петли, одна из которых повторяется senses
следуя nextss
и тот, который пересекает список деревьев, вызывая traceptrs_ds
,
Это две петли (упрощенно):
std::shared_ptr<Synset> synsets;
synsets = std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
(findtheinfo_ds(const_cast<char*>(key.c_str()), lexical, HYPERPTR, ALLSENSES),free_syns);
if (synsets)
{
std::shared_ptr<Synset> sense_ptr = synsets;
while(sense_ptr)
{
graph g = graph();
iterate(sense_ptr, nullptr, g, 0, lexical);
sense_ptr = std::shared_ptr<std::remove_pointer<SynsetPtr>::type>(sense_ptr->nextss);
}
}
iterate(
std::shared_ptr<Synset> ptr,
std::shared_ptr<layer> last,
graph & rhs,
int i,
int lexical
)
{
while (ptr)
{
i++;
ptr = std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
(traceptrs_ds(ptr.get(), HYPERPTR, lexical, 1), free_synset);
}
}
Из того, что я понимаю (не стесняйтесь поправлять меня) многие std::shared_ptr
оборачиваются вокруг той же памяти,
и когда вызывается их деструктор, они пытаются освободить ту же самую память.
я знаю это findtheinfo_ds
возвращает связанный список, и что каждый вызов traceptrs_ds
появляется для повторения этого списка.
Я представляю, что наилучшим подходом будет завернуть только один std::shared_ptr
который собирается назвать Synset
деструктор free_synset
вместо того, чтобы делать это много раз.
Предполагая, что нет никакого решения, чтобы предотвратить это,
как могут разрушители многих std::shared_ptr
ту карту в ту же область, только бесплатно один раз?
Я думаю, что это плохой / уродливый способ, но может быть проще?
valgrind
выход:
==3066== Invalid read of size 8
==3066== at 0x52C3B4C: free_synset (in /usr/lib/libwordnet-3.0.so)
==3066== by 0x4E6E2D9: std::_Sp_counted_deleter<ss*, void (*)(ss*), std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:463)
==3066== by 0x403C5F: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)
==3066== by 0x403AD8: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:666)
==3066== by 0x4E61DB7: std::__shared_ptr<ss, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:914)
==3066== by 0x4E61DD1: std::shared_ptr<ss>::~shared_ptr() (shared_ptr.h:93)
==3066== by 0x4E6161A: smnet::hyper_handler::operator()(std::string, int) (hyper_handler.cpp:22)
==3066== by 0x4036D6: main (hypernym.cpp:16)
==3066== Address 0x10 is not stack'd, malloc'd or (recently) free'd
==3066== Process terminating with default action of signal 11 (SIGSEGV)
==3066== Access not within mapped region at address 0x10
==3066== at 0x52C3B4C: free_synset (in /usr/lib/libwordnet-3.0.so)
==3066== by 0x4E6E2D9: std::_Sp_counted_deleter<ss*, void (*)(ss*), std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:463)
==3066== by 0x403C5F: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)
==3066== by 0x403AD8: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:666)
==3066== by 0x4E61DB7: std::__shared_ptr<ss, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:914)
==3066== by 0x4E61DD1: std::shared_ptr<ss>::~shared_ptr() (shared_ptr.h:93)
==3066== by 0x4E6161A: smnet::hyper_handler::operator()(std::string, int) (hyper_handler.cpp:22)
==3066== by 0x4036D6: main (hypernym.cpp:16)
==3066== LEAK SUMMARY:
==3066== definitely lost: 0 bytes in 0 blocks
==3066== indirectly lost: 0 bytes in 0 blocks
==3066== possibly lost: 251 bytes in 8 blocks
==3066== still reachable: 19,964 bytes in 90 blocks
==3066== suppressed: 0 bytes in 0 blocks
==3066== Reachable blocks (those to which a pointer was found) are not shown.
==3066== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==3066==
==3066== For counts of detected and suppressed errors, rerun with: -v
==3066== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
fish: “valgrind --leak-check=full ./hy…” terminated by signal SIGSEGV (Address boundary error)
Что ж, после долгих поисков я нашел решение с использованием структурных функторов и лямбд. Конечно, это не красиво (на самом деле это довольно уродливо), но, похоже, работает:
struct sense_del
{
void operator()(Synset *ptr)
{
if (ptr)
free_syns(ptr);
}
};
struct trace_del
{
void operator()(Synset *ptr)
{
if (ptr)
free_synset(ptr);
}
};
std::shared_ptr<Synset>
shared_findtheinfo_ds(std::string key, int gram_type, int ptr_type, int sense_num)
{
wn_init_once();
std::shared_ptr<Synset> sp(findtheinfo_ds(const_cast<char*>(key.c_str()), gram_type, ptr_type, sense_num),
[](Synset * ptr){sense_del()(ptr);});
return sp;
}
std::shared_ptr<Synset>
shared_traceptrs_ds(SynsetPtr synptr, int ptr_type, int gram_type, int depth)
{
assert(synptr);
std::shared_ptr<Synset> sp(traceptrs_ds(synptr, ptr_type, gram_type, depth),
[](Synset *ptr){trace_del()(ptr);});
return sp;
}
К вашему сведению, для тех, кто работает над Wordnet, правильный способ итерации для Hypernyms заключается в следующем:
if (auto ss_ptr = findtheinfo_ds(const_cast<char*>(key.c_str()), lexical, HYPERPTR, ALLSENSES))
{
while(ss_ptr)
{
if (SynsetPtr trc_ptr = traceptrs_ds(ss_ptr, HYPERPTR, lexical, 1))
{
do_smth_with_it(trc_ptr);
free_syns(trc_ptr);
}
// get next sense
ss_ptr = ss_ptr->nextss;
}
free_syns(ss_ptr);
}
Других решений пока нет …