Функтор Локи - проблема с памятью
Я использую 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, детектор ошибок памяти ==30296== Copyright (C) 2002-2010, и GNU GPL'd, Джулиан Сьюард и соавт. == 30296 == Использование Valgrind-3.6.1 и LibVEX; повторно введите -h для получения информации об авторских правах == 30296 == Команда:./main == 30296 == Тест: Hello world ==30296== Недопустимое чтение размера 4 == 30296 == в 0x40EB655: std::basic_string, std::allocator >::basic_string(std::string const&) (в /usr/lib/libstdc++.so.6.0.16) ==30296== от 0x8049C4F: Loki::Functor, Loki::SingleThreaded>:: оператор ()(std::string&) (Functor.h:779) ==30296== от 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== по 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== по 0x8048E7A: main (main.cpp:26) ==30296== Адрес 0x42f2640 составляет 8 байтов внутри блока размером 24 free'd ==30296== в 0x4026B2C: оператор delete(void*) (в /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== по 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (в /usr/lib/libstdc++.so.6.0.16) ==30296== по 0x41A0232: (ниже основного) (в /lib/libc-2.14.so) ==30296== ==30296== Недопустимое чтение размера 4 == 30296 == в 0x40EAD96: std::string::_Rep::_M_clone(s) td::allocator const&, unsigned int) (в /usr/lib/libstdc++.so.6.0.16) ==30296== от 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== от 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== от 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== от 0x8048E7A: main (main.cpp:26) ==30296== Адрес 0x42f263c составляет 4 байта внутри блока размера 24 free'd ==30296== в 0x4026B2C: оператор delete(void*) (в /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== от 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (в /usr/lib/libstdc++.so.6.0.16) ==30296== по 0x41A0232: (ниже основного) (в /lib/libc-2.14.so) ==30296== ==30296== Недопустимое чтение размера 4 == 30296 == в 0x40EADA5: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (в / usr / lib / libstdC++.so.6.0.16) ==30296== by 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== по 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== по 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== по 0x8048E7A: main (main.cpp:26) ==30296== Адрес 0x42f2638 составляет 0 байтов внутри блока размером 24 free'd ==30296== при 0x4026B2C: оператор delete(void*) (в /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== от 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (в /usr/lib/libstdc++.so.6.0.16) ==30296== по 0x41A0232: (ниже основного) (в /lib/libc-2.14.so) ==30296== ==30296== Неверное чтение размера 4 == 30296 == в 0x40EADB3: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (в /usr/lib/libstdc++.so.6.0.16) ==30296== от 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== от 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== от 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== от 0x804 8E7A: main (main.cpp: 26) == 30296 == Адрес 0x42f2638 составляет 0 байтов внутри блока размером 24 free'd ==30296== at 0x4026B2C: оператор delete(void*) (в / usr / lib / valgrind / vgpreload_memcheck-x86-linux.so) == 30296 == от 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (в /usr/lib/libstdc++.so.6.0.16) ==30296== по 0x41A0232: (ниже основного) (в /lib/libc-2.14.so) ==30296== ==30296== Недопустимое чтение размера 1 ==30296== в 0x40294BA: memcpy (в / usr / lib / valgrind / vgpreload_memcheck-x86-linux.so) == 30296 == от 0x40EADF7: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (в / usr / lib / libstdC++.so.6.0.16) ==30296== by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (в /usr/lib/libstdc++.so.6.0.16) ==30296== по 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== по 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== от 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== по 0x8048E7A: main (main.cpp:26) ==30296== Адрес 0x42f264e составляет 22 байта внутри блока размером 24 free'd ==30296== при 0x4026B2C: оператор delete(void*) (в /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== от 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (в /usr/lib/libstdc++.so.6.0.16) ==30296== по 0x41A0232: (ниже основного) (в /lib/libc-2.14.so) ==30296== ==30296== Неверное чтение размер 4 == 30296 == в 0x40294E8: memcpy (в /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== от 0x40EADF7: std:: string:: _ Rep:: _ M_clone (std:: распределитель const &, unsigned int) (в /usr/lib/libstdc++.so.6.0.16) ==30296== от 0x40EB68F: std:: basic_string, std:: allocator>:: basic_string (std:: string const &) (в /usr/lib/libstdc++.so.6.0.16) ==30296== от 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== по 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== by 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== от 0x8048E7A: main (main.cpp:26) ==30296== Адрес 0x42f2648 составляет 16 байтов внутри блока размер 24 free'd ==30296== в 0x4026B2C: оператор delete(void*) (в /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== от 0x40E9C7A: std:: string:: _Rep:: _ M_destroy (std:: allocator const &) (в /usr/lib/libstdc++.so.6.0.16) ==30296== по 0x41A0232: (ниже основного) (в /lib/libc-2.14.so) = = 30296 == == 30296 == Недопустимое чтение размера 4 == 30296 == в 0x40EADF8: std::string::_Rep::_M_clone(std::allocator const&, unsigned int) (в / usr / lib / libstdC++.so.6.0.16) ==30296== by 0x40EB68F: std::basic_string, std::allocator >::basic_string(std::string const&) (в /usr/lib/libstdc++.so.6.0.16) ==30296== по 0x8049C4F: Loki::Functor, Loki::SingleThreaded>::operator()(std::string&) (Functor.h:779) ==30296== по 0x8049B59: Loki::BinderFirst, Loki::SingleThreaded> >::operator()() (Functor.h:908) ==30296== от 0x80492D6: Loki::Functor::operator()() (Functor.h:776) ==30296== по 0x8048E7A: main (main.cpp:26) ==30296== Адрес 0x42f2638 составляет 0 байтов внутри блока размером 24 free'd ==30296== в 0x4026B2C: оператор delete(void*) (в /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==30296== от 0x40E9C7A: std::string::_Rep::_M_destroy(std::allocator const&) (в / usr / lib /libstdc++.so.6.0.16) == 30296 == по 0x41A0232: (ниже основного) (в /lib/libc-2.14.so)
Я подозреваю, что функтор берет только ссылку на переданный объект, который затем уничтожается (как временно созданный) и начинаются проблемы. Но что я тут делаю не так? Я предположил, что привязка должна использоваться для хранения части окружения (как Андрей описывает в своей книге), чтобы окружение могло быть уничтожено.
1 ответ
Проблема заключается в том, что объект функтора Локи не создает истинную копию строки, а хранит ссылку на строковый объект, который вы хотите связать с вашей функцией. Это связано с тем, что объект функтора loki хранит ссылочный тип, если тип связываемого аргумента не является указателем, указателем члена или арифметическим типом (т. Е. Типом, с которым вы можете выполнять арифметическую операцию). Таким образом, поскольку строка является временной, и сохраняется только ссылка на временную, после того как стек откатывается от вызова функции, доступ к временной строке теряется из-за внутренней ссылки в объекте подшивки, и вы не можете распечатать строка.
Одним из возможных решений может быть создание вашей функции так, чтобы она принимала умный тип указателя, чтобы вы могли динамически размещать объект, и время жизни объекта будет выходить за пределы текущей области, но избегать проблем, связанных с временем жизни объекта и утечками памяти, которые будет иметь место с обычным или голым указателем типа.
Изменить: я попробовал это... все еще не работает, так как он снова хранит ссылку на тип умного указателя, и это означает, что указатель освобождается, когда временный умный указатель выходит за рамки видимости. Так что да, вам придется либо изменить некоторые определения того, как объект функтора loki определяет, сохранять ли ссылку или значение, либо использовать другую версию аргументов привязки для функциональных объектов, такую как версия нового стандарта C++11 std::bind
а также std::function