Pregunta

I use Loki::Functor in my project for a simple event system. The event has its handler function taking some parameters. In this case, it is called PrintEventString. In order to put it in the queue, the event handlers must have same prototypes - in my case, void func(void). So CreateEvent takes the handler, creates functor from it and binds the parameter, resulting in void f (void) prototype. Everything goes fine (first example with string stored in local variable), until I destroy the data source before calling functor (second example, string created temporarily). Here is the code:

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

This compiles, executes, but the second test does't work and valgrind throws a bunch of errors:

==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)

I suspect the functor to take only a reference to passed object, which is then destroyed (as temporarily created) and the problems begin. But what am I doing wrong here? I supposed the binding is to be used for storing part of the environment (as Andrei describes in his book), so that the environment can be destroyed.

¿Fue útil?

Solución

The problem is that Loki's functor object is not making a true copy of the string, but rather is storing a reference to the string object that you are wanting to have bound to your function. This is because loki's functor object stores a reference type if the type of the argument being bound is not a pointer, member pointer, or arithmetic type (i.e., a type you can perform an arithmetic operation with). So since the string is a temporary, and only a reference to the temporary is stored, once the stack unwinds from the function call, access to the temporary string is lost from the internal reference in the binder object, and you are unable to print the string.

One possible solution could be to create your function so that it takes a smart pointer type so that you can allocate an object dynamically, and the lifetime of the object will extend beyond the current scope, but avoid the issues surrounding object lifetime and memory leaks that would take place with a normal or naked pointer type.

Edit: I tried that out ... still doesn't seem to work as it's again storing a reference to the smart pointer type, and that means the pointer gets deallocated when the temporary smart pointer goes out-of-scope. So yes, you're either going to have to change some definitions of how loki's functor object determines whether to store a reference or a value, or use another version of binding arguments to function objects like the new C++11 standard's version of std::bind and std::function

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top