g_autoptr ()
Это архивная статья
Вот уже некоторое время GCC поддерживает хороший способ для
автоматической очистки переменных, когда они перестают использоваться
-- __attribute__((cleanup)).
Некоторое время назад, разработчик GLib, Ryan
Lortie,добавил специальные
макросы,
чтобы упростить работу программистам по очистке памяти. Они работают
только с GCC и Clang, так что если вы хотите собирать программу
используя MSVC ([STRIKEOUT:кто-то ещё использует это?]) - вы не должны
их использовать.
Новое API лучше всего продемонстрированно на следующем примере:
{ g_autoptr(GObject) object; g_autoptr(gchar) tmp; g_auto(GQueue) queue; g_queue_init (&queue); object = g_object_new (...); tmp = g_strdup_printf (...); // no free required }
Чтобы добавить поддержку собственных типов - нужно использовать
G_DEFINE_AUTOPTR_CLEANUP_FUNC и подобные макросы. Это будет
происходить автоматически, если вы используете недавно-добавленные в
API G_DECLARE_DERIVABLE_TYPE или G_DECLARE_FINAL_TYPE
макросы.
Для тех, кто пишет на C, GLib может показаться очень удобным
дополнением, там есть удобная реализация потоков, списков, классов
(да-да, вы не ослышались), асинхронность, таймеры, парсеры конфигов и
ещё много-много всего. Вы только посмотрите на
документацию!
Давайте рассмотрим более детальный пример:
// gcc `pkg-config --libs --cflags glib-2.0` -g g_autoptr.c #include G_DEFINE_AUTOPTR_CLEANUP_FUNC (gchar, g_free); void print_elem (gpointer data, gpointer user_data) { g_print ("GList: %s\n", (gchar *) data); } gint main (gint argc, gchar *argv[]) { g_autoptr(gchar) tmp = g_strdup ("Hello, I'm gchar!"); g_autoptr(GList) lst = NULL; lst = g_list_append (lst, tmp); g_print ("gchar: %s\n", tmp); g_list_foreach (lst, (GFunc) print_elem, NULL); return 0; }
Давайте скомпилируем и запустим наш пример под valgrind и посмотрим на утечки в памяти..
$ valgrind --tool=memcheck --leak-check=full ./g_autoptr ==9620== HEAP SUMMARY: ==9620== in use at exit: 2,094 bytes in 6 blocks ==9620== total heap usage: 22 allocs, 16 frees, 68,943 bytes allocated ==9620== ==9620== LEAK SUMMARY: ==9620== definitely lost: 0 bytes in 0 blocks ==9620== indirectly lost: 0 bytes in 0 blocks ==9620== possibly lost: 0 bytes in 0 blocks ==9620== still reachable: 2,094 bytes in 6 blocks ==9620== suppressed: 0 bytes in 0 blocks
А теперь уберём g_autoptr и ещё раз посмотрим.
$ valgrind --tool=memcheck --leak-check=full ./no_g_autoptr ==9705== HEAP SUMMARY: ==9705== in use at exit: 2,136 bytes in 8 blocks ==9705== total heap usage: 22 allocs, 14 frees, 68,943 bytes allocated ==9705== ==9705== 42 (24 direct, 18 indirect) bytes in 1 blocks are definitely lost in loss record 7 of 8 ==9705== at 0x4C2AC10: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==9705== by 0x4E82769: g_malloc (in /usr/lib64/libglib-2.0.so.0.4501.0) ==9705== by 0x4E99DC2: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.4501.0) ==9705== by 0x4E78EE3: g_list_append (in /usr/lib64/libglib-2.0.so.0.4501.0) ==9705== by 0x400848: main (no_g_autoptr.c:16) ==9705== ==9705== LEAK SUMMARY: ==9705== definitely lost: 24 bytes in 1 blocks ==9705== indirectly lost: 18 bytes in 1 blocks ==9705== possibly lost: 0 bytes in 0 blocks ==9705== still reachable: 2,094 bytes in 6 blocks ==9705== suppressed: 0 bytes in 0 blocks
Сейчас, некоторые наши коллеги, используют такие конструкции:
#define GS_DEFINE_CLEANUP_FUNCTION0(Type, name, func) \ static inline void name (void *v) \ { \ if (*(Type*)v) \ func (*(Type*)v); \ } #define _cleanup_error_free_ __attribute__ ((cleanup(gs_local_free_error))) GS_DEFINE_CLEANUP_FUNCTION0(GError*, gs_local_free_error, g_error_free) _cleanup_error_free_ GError *error = NULL;
Некрасиво, правда? К тому же ещё и код дублируется из проекта в проект.