2014-02-13 2 views
2

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

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

SynsetPtr 

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

findtheinfo_ds 

traceptrs_ds 

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

Howevever, когда SynsetPtr представляет список чувств, я должен освободить его с помощью

free_syns 

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

free_synset 

Documentation не совсем понятно, когда звонить, который, и почему.

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

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

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

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

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

EDIT:

Это точная декларация 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); 

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

EDIT 2:

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

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

traceptrs_ds (ptr, SIMPTR, ADJ, 0) 

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

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

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

Благодаря тому, кто помогал, очень ценю.

+1

Вы знаете, когда вы создаете объекты, типа это (дерево или список), и может ли изменение типа? – user3286380

+0

Вероятно, нет причин, по которым вы не могли бы обернуть функцию create в конструкторе C++ и вызвать соответствующий бесплатный метод в деструкторе. Сделать класс упаковки неизменным будет очень помогать. Затем используйте один из стандартных методов подсчета ссылок для доступа к вашему классу инкапсуляции. –

+0

Я могу заключить, что создано. Первая функция создает связанный список синхронизаций, вторая функция обходит одну из них, возвращая указатель синхронизации в списке. Весь дизайн действительно запутан, и я попытался связаться с авторами безрезультатно. –

ответ

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); 
} 

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

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

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)); 
} 
+0

Большое спасибо, это очень интересный подход. Я буду проверять все завтра (его почти 6:00 здесь). –

+0

Привет! Я использовал shared_ptr, поскольку он кажется более подходящим, потому что я продолжаю повторное использование тех же указателей. Тем не менее, я столкнулся с рассолом, которого я не совсем понимаю: auto next = std :: make_shared (ptr-> nextss); Производит ошибки. Независимо от того, как я пытаюсь обойти это. Распределитель жалуется на параметр. Что я делаю не так? –

+0

@Alex Вы не можете использовать 'make_shared'. Ваш вызов пытается построить 'Synset', вызывая' Synset :: Synset (ptr-> nextss) ', который не существует. Вы можете использовать решение, которое я разместил для 'shared_ptr' выше, или сообщение, отправленное DavidRodriguez. Они оба должны работать. – Praetorian

3

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

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; 
} 

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

+0

Вы уверены, что это скомпилируется? Конструктор 'shared_ptr' будет ожидать получать' SynsetPtr * ', но эти функции C возвращают' SynsetPtr' – Praetorian

+0

@Praetorian: подробности в этом вопросе нет, поэтому трудно понять, что действительно возвращается функцией , Часто библиотека, требующая вызова 'free_XXX', будет содержать указатель в функции * allocator *. Я полагаю, что 'Ptr' в' SynsetPtr' может указывать typedef типа указателя (я также исправил код там), но опять же, без фактических подписи это все угадывающая работа. * Я уверен, что это скомпилируется? * Ни в коем случае. –

+0

Да, слишком мало информации для продолжения, но я принимал то же самое, что 'SynsetPtr' является typedef для типа указателя, но любой тип отсрочки, который выдается, непрозрачен. Что делать, если вы должны использовать 'shared_ptr :: type>' (если тип 'Synset' не существует?); будет ли это работать с непрозрачным типом в этом случае? – Praetorian

Смежные вопросы