Функтор Локи - проблема с памятью - PullRequest
3 голосов
/ 04 сентября 2011

Я использую Loki :: Functor в своем проекте для простой системы событий. Событие имеет функцию-обработчик, принимающую некоторые параметры. В этом случае он называется PrintEventString. Чтобы поместить его в очередь, обработчики событий должны иметь одинаковые прототипы - в моем случае, void func(void). Таким образом, CreateEvent берет обработчик, создает из него функтор и связывает параметр, в результате чего получается void f (void) прототип. Все идет хорошо (первый пример со строкой, хранящейся в локальной переменной), пока я не уничтожу источник данных перед вызовом функтора (второй пример, строка создана временно). Вот код:

#include <climits>
#include <string>
#include <iostream>
#include "Loki/Functor.h"

void PrintEventString(std::string str)
{
    std::cout << "Test: " << str << std::endl;
}

Loki::Functor<void> CreateEvent (std::string str)
{
    Loki::Functor<void, TYPELIST_1(std::string)> handler(PrintEventString);
    Loki::Functor<void> event (Loki::BindFirst(handler, str));
    return event;
}

int main (void)
{
    std::string hello("hello");

    Loki::Functor<void> eventTestLocal(CreateEvent(hello));
    eventTestLocal();

    Loki::Functor<void> eventTestTemp(CreateEvent("Hello world"));
    eventTestTemp();


    return 0;
}

Это компилирует, выполняет, но второй тест не работает, и valgrind выдает кучу ошибок:

==30296== Memcheck, a memory error detector
==30296== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==30296== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==30296== Command: ./main
==30296== 
Test: Hello world
==30296== Invalid read of size 4
==30296==    at 0x40EB655: std::basic_string, std::allocator >::basic_string(std::string const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779)
==30296==    by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908)
==30296==    by 0x80492D6: Loki::Functor::operator()() (Functor.h:776)
==30296==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f2640 is 8 bytes inside a block of size 24 free'd
==30296==    at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 4
==30296==    at 0x40EAD96: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779)
==30296==    by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908)
==30296==    by 0x80492D6: Loki::Functor::operator()() (Functor.h:776)
==30296==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f263c is 4 bytes inside a block of size 24 free'd
==30296==    at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 4
==30296==    at 0x40EADA5: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779)
==30296==    by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908)
==30296==    by 0x80492D6: Loki::Functor::operator()() (Functor.h:776)
==30296==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f2638 is 0 bytes inside a block of size 24 free'd
==30296==    at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 4
==30296==    at 0x40EADB3: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779)
==30296==    by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908)
==30296==    by 0x80492D6: Loki::Functor::operator()() (Functor.h:776)
==30296==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f2638 is 0 bytes inside a block of size 24 free'd
==30296==    at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 1
==30296==    at 0x40294BA: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40EADF7: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779)
==30296==    by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908)
==30296==    by 0x80492D6: Loki::Functor::operator()() (Functor.h:776)
==30296==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f264e is 22 bytes inside a block of size 24 free'd
==30296==    at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 4
==30296==    at 0x40294E8: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40EADF7: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779)
==30296==    by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908)
==30296==    by 0x80492D6: Loki::Functor::operator()() (Functor.h:776)
==30296==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f2648 is 16 bytes inside a block of size 24 free'd
==30296==    at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)
==30296== 
==30296== Invalid read of size 4
==30296==    at 0x40EADF8: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779)
==30296==    by 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908)
==30296==    by 0x80492D6: Loki::Functor::operator()() (Functor.h:776)
==30296==    by 0x8048E7A: main (main.cpp:26)
==30296==  Address 0x42f2638 is 0 bytes inside a block of size 24 free'd
==30296==    at 0x4026B2C: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==30296==    by 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (in /usr/lib/libstdc++.so.6.0.16)
==30296==    by 0x41A0232: (below main) (in /lib/libc-2.14.so)

Я подозреваю, что функтор берет только ссылку на переданный объект, который затем уничтожается (как временно созданный) и начинаются проблемы. Но что я здесь делаю не так? Я предположил, что привязка должна использоваться для хранения части окружения (как Андрей описывает в своей книге), чтобы окружение могло быть уничтожено.

1 Ответ

1 голос
/ 04 сентября 2011

Проблема состоит в том, что объект функтора Локи не делает истинную копию строки, а хранит ссылку на строковый объект, который вы хотите связать с вашей функцией.Это связано с тем, что объект функтора loki хранит ссылочный тип, если тип связываемого аргумента не является указателем, указателем члена или арифметическим типом (т. Е. Типом, с которым вы можете выполнять арифметическую операцию).Таким образом, поскольку строка является временной, и сохраняется только ссылка на эту временную область, после того как стек откатывается от вызова функции, доступ к временной строке теряется из-за внутренней ссылки в объекте подшивки, и вы не можете распечататьstring.

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

Редактировать : я попробовал это ... все еще не работает, поскольку он снова сохраняет ссылку на тип интеллектуального указателя, и это означает, что указатель освобождается, когда временныйумный указатель выходит за рамки видимости.Так что да, вам нужно будет либо изменить некоторые определения того, как объект функтора loki определяет, сохранять ли ссылку или значение, либо использовать другую версию аргументов привязки для функциональных объектов, такую ​​как версия нового стандарта C ++ 11 std::bind и std::function

...