Обтекание старых структур C с помощью интеллектуальных указателей в C++11 и автоматическое их освобождение

Я использую Word-Net, старую библиотеку C, разработанную Принстонским университетом еще в девяностых. Библиотека написана на C и показывает только заголовки, но не ее фактическую реализацию.

Единственная структура, которую я использую:

SynsetPtr

И две функции, которые я вызываю:

findtheinfo_ds

traceptrs_ds

Обе эти функции возвращают SynsetPtr.

Однако, когда SynsetPtr представляет список смыслов, я должен освободить его, используя

free_syns

Принимая во внимание, что когда SynsetPtr используется для обхода связанного списка (иерархического дерева), я должен освободить его, используя

free_synset

В документации не совсем понятно, когда звонить, и почему.

Это быстро становится кошмаром для меня. Я потратил три дня, работая медленно, через утечки, двойные освобождения, распределение памяти и тому подобное.

Так что мне было интересно, есть ли способ для меня, чтобы обернуть вокруг этих функций или фактической структуры, и заставить C++ управлять памятью? В идеале я хотел бы, чтобы они были свободны, когда на них больше нет ссылок, как в случае с std::shared_ptr.

Возможно ли это, зная, что Synset_Ptr не имеет деструктора, но должна вызываться функция dealloc?

В качестве альтернативы, могу ли я обернуть вокруг две функции, которые создают (распределяют) эти структуры, каким-то образом ведут учет объектов и уничтожают их, когда не остается ссылок на них?

Я был бы очень признателен за любую помощь!

РЕДАКТИРОВАТЬ:

Это точное объявление SynsetPtr в wn.h

/* Structure for data file synset */
typedef struct ss {
    long hereiam;       /* current file position */
    int sstype;         /* type of ADJ synset */
    int fnum;           /* file number that synset comes from */
    char *pos;          /* part of speech */
    int wcount;         /* number of words in synset */
    char **words;       /* words in synset */
    int *lexid;         /* unique id in lexicographer file */
    int *wnsns;         /* sense number in wordnet */
    int whichword;      /* which word in synset we're looking for */
    int ptrcount;       /* number of pointers */
    int *ptrtyp;        /* pointer types */
    long *ptroff;       /* pointer offsets */
    int *ppos;          /* pointer part of speech */
    int *pto;           /* pointer 'to' fields */
    int *pfrm;          /* pointer 'from' fields */
    int fcount;         /* number of verb frames */
    int *frmid;         /* frame numbers */
    int *frmto;         /* frame 'to' fields */
    char *defn;         /* synset gloss (definition) */
    unsigned int key;       /* unique synset key */

    /* these fields are used if a data structure is returned
       instead of a text buffer */

    struct ss *nextss;      /* ptr to next synset containing searchword */
    struct ss *nextform;    /* ptr to list of synsets for alternate
                   spelling of wordform */
    int searchtype;     /* type of search performed */
    struct ss *ptrlist;     /* ptr to synset list result of search */
    char *headword;     /* if pos is "s", this is cluster head word */
    short headsense;        /* sense number of headword */
} Synset;

typedef Synset *SynsetPtr;


/* Primary search algorithm for use with programs (returns data structure) */
extern SynsetPtr findtheinfo_ds(char *, int, int, int); 

/* Recursive search algorithm to trace a pointer tree and return results
   in linked list of data structures. */
SynsetPtr traceptrs_ds(SynsetPtr, int, int, int);

/* Free a synset linked list allocated by findtheinfo_ds() */
extern void free_syns(SynsetPtr);   

/* Free a synset */
extern void free_synset(SynsetPtr); 

И это по сути все, что я знаю.

РЕДАКТИРОВАТЬ 2:

Несмотря на то, что я использовал два ответа ниже, к сожалению, функции все еще пропускают байты.

Это только кажется, что происходит с:

traceptrs_ds ( ptr, SIMPTR, ADJ, 0 )

В документации очень мало информации о прилагательных синонимах (-synsa) или других типах (-synsn, -synsv).

Однако мне удалось перебрать большинство из них, просто следуя ptr-> ptrlist && ptr-> nextss;

traceptr_ds повторяет ВСЕ из них, но я не могу найти способ избежать утечки, даже при использовании минимизированной тестовой проги.

Спасибо, кто помог, очень ценится.

2 ответа

Решение

Я знаю, как решить эту проблему для уникального владельца, используя unique_ptrотличная особенность, где его управляемый тип становится Deleter::pointer, вместо T*, если существует первый тип.

Если у вас нет определения Synsetили любой другой тип SynsetPtr указывает на проблему с использованием shared_ptr является то, что он не имеет того же средства переключения управляемого типа, и если вы создаете shared_ptr<SynsetPtr>конструктор будет ожидать SynsetPtr*, но ваши функции C API не возвращают этот тип. И я не знаю наверняка, используя shared_ptr<remove_pointer<SynsetPtr>::type> скомпилируется, если у вас нет определения типа, который разыменовывает SynsetPtr доходность.

Это может сработать, но я не уверен.

std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
make_findtheinfo_ds(char *searchstr, int pos, int ptr_type, int sense_num)
{
    return 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>
make_traceptrs_ds(SynsetPtr synptr, int ptr_type, int pos, int depth)
{
    return std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
               (traceptrs_ds(synptr, ptr_type, pos, depth),
                free_synset);
}

Идя по уникальному маршруту владения, я бы сделал несколько заводских функций, которые возвращают unique_ptrс управлением SynsetPtrs.

Нам нужны 2 отдельных удалителя для разных видов SynsetPtrs

struct sense_list_del
{
    using pointer = SynsetPtr;
    void operator()(SynsetPtr p)
    {
        free_syns(p);
    }
};

struct linked_list_del
{
    using pointer = SynsetPtr;
    void operator()(SynsetPtr p)
    {
        free_synset(p);
    }
};

std::unique_ptr<SynsetPtr, sense_list_del>
make_findtheinfo_ds(char *searchstr, int pos, int ptr_type, int sense_num)
{
    return std::unique_ptr<SynsetPtr, sense_list_del>
               (findtheinfo_ds(searchstr, pos, ptr_type, sense_num));
}

std::unique_ptr<SynsetPtr, linked_list_del>
make_traceptrs_ds(SynsetPtr synptr, int ptr_type, int pos, int depth)
{
    return std::unique_ptr<SynsetPtr, linked_list_del>
               (traceptrs_ds(synptr, ptr_type, pos, depth));
}

Ты можешь использовать std::shared_ptr для этой цели, так как вы можете предоставить средство удаления для освобождения указателя.

std::shared_ptr<Synset> findTheInfo(...) {
    std::shared_ptr<Synset> sp(findtheinfo(...), free_syns);
    return sp;
}
std::shared_ptr<Synset> tracePtrs(...) {
    std::shared_ptr<Synset> sp(traceptrs(...), free_synset);
    return sp;
}

Теперь, если они действительно представляют разные вещи, вы можете потратить немного больше усилий и предоставить два типа, которые обертывают каждое использование и предоставляют соответствующий интерфейс. Имеет ли смысл рассматривать оба типа как один и тот же, когда трансверс список и дерево могут быть совершенно разными?

Другие вопросы по тегам