6

I'm suffering a segfault in a plugin when I call a std::function in it passed from the main executable, via converting it's address to/from void*. I can reproduce the problem in a few self-contained lines:

#include <iostream>
#include <functional>

int main()
{
    using func_t = std::function<const std::string& ()>;

    auto hn_getter = func_t{[]() {
        return "Hello";
    }};

    auto ptr = reinterpret_cast<void*>(&hn_getter);

    auto getter = reinterpret_cast<func_t*>(ptr);
    std::cout << (*getter)() << std::endl;   // Bang!

    return EXIT_SUCCESS;
}

Even though I'm casting to the original type, it's still segfaulting. Can anyone see where I'm going wrong?

cmannett85
  • 21,725
  • 8
  • 76
  • 119
  • Although casting to `void*` would better be done with `static_cast` (for the sake of getting rid of `reinterpret_cast`), the code seems fine to me and I can't reproduce it: http://coliru.stacked-crooked.com/a/6c316f8e9dc1ded2 – SergeyA Mar 15 '18 at 15:47
  • 1
    @SergeyA But it does crash with g++: http://coliru.stacked-crooked.com/a/30f84f279c2118af – NathanOliver Mar 15 '18 at 15:49
  • @user2079303 in the given example, cast is used (and it is required to cast from void* to actual type) – SergeyA Mar 15 '18 at 15:51
  • 1
    @user2079303: Look at his code; he's clearly using an "Almost Always Auto" programming style. – Nicol Bolas Mar 15 '18 at 15:51
  • 3
    Never return a reference to a local variable (well, unless it is static). – NathanOliver Mar 15 '18 at 15:52
  • @NicolBolas ah. I assumed that they used auto to avoid repeating the type from the cast; not that they used a cast in order to be able to use auto. What a confusing style (in my opinion). – eerorika Mar 15 '18 at 15:54
  • Questions with [mcve] are so seldom, so existence of that itself deserves +1 – Slava Mar 15 '18 at 15:57
  • As an alternative, you could also just `auto hn_getter = []() { return "Hello"; };` and then `auto getter = static_cast(ptr);`. – jdehesa Mar 15 '18 at 16:00

1 Answers1

17

The cause of your problem has nothing to do with cast, it's because of the function return a const string &. You need:

using func_t = std::function<const std::string ()>;

And as comments suggest, const here is useless, just:

using func_t = std::function<std::string ()>;
llllllllll
  • 16,169
  • 4
  • 31
  • 54
  • Eyes of an eagle! – SergeyA Mar 15 '18 at 15:53
  • Or just `std::function` – NathanOliver Mar 15 '18 at 15:53
  • Good catch, though `const` there is not needed. – Slava Mar 15 '18 at 15:53
  • This does indeed work, though I don't know why. The lambda returns a const char* literal, which will be implicitly converted to an rvalue `std:string`. Even though its a temporary, because the return type is `const&` it should stay alive long enough to be printed. – cmannett85 Mar 15 '18 at 16:20
  • 3
    @cmannett85 No. You are returning the reference of a local temporary. It will be destroyed just after lambda returns. – llllllllll Mar 15 '18 at 16:23
  • 2
    @cmannett85 The lifetime of a temporary is *only* extended if it is directly bound to a named local reference during its initialization. E.g. `const std::string &str = "Hello"s;`, but not e.g. on a `return`, and neither `const std::string &str = rand() > 10000 ? "Hello"s : "Bye"s;` (because neither temporary is *directly* bound to the reference). – Arne Vogel Mar 15 '18 at 17:43