Naios / function2

Improved and configurable drop-in replacement to std::function that supports move only types, multiple overloads and more
http://naios.github.io/function2
Boost Software License 1.0
539 stars 47 forks source link

function_view cannot bind to const lambda #21

Closed nitronoid closed 1 year ago

nitronoid commented 5 years ago

@Naios


Expected Behavior

The const-ness of the callable should be propagated to the erased function pointer

Actual Behavior

In file included from main.cpp:1:0:
function2.hpp: In instantiation of ‘static void* fu2::abi_400::detail::type_erasure::address_taker<T, <template-parameter-1-2> >::take(O&&) [with O = const main()::<lambda()>&; T = main()::<lambda()>; <template-parameter-1-2> = void]’:
function2.hpp:1184:51:   required from ‘constexpr fu2::abi_400::detail::type_erasure::erasure<false, Config, fu2::abi_400::detail::property<IsThrowing, HasStrongExceptGuarantee, Args ...> >::erasure(T&&) [with T = const main()::<lambda()>&; Config = fu2::abi_400::detail::config<false, true, fu2::capacity_default>; bool IsThrowing = true; bool HasStrongExceptGuarantee = false; Args = {int()}]’
function2.hpp:1376:72:   required from ‘constexpr fu2::abi_400::detail::function<Config, fu2::abi_400::detail::property<IsThrowing, HasStrongExceptGuarantee, Args ...> >::function(T&&) [with T = const main()::<lambda()>&; fu2::abi_400::detail::function<Config, fu2::abi_400::detail::property<IsThrowing, HasStrongExceptGuarantee, Args ...> >::enable_if_not_convertible_to_this<T>* <anonymous> = 0; fu2::abi_400::detail::function<Config, fu2::abi_400::detail::property<IsThrowing, HasStrongExceptGuarantee, Args ...> >::enable_if_can_accept_all_t<T>* <anonymous> = 0; fu2::abi_400::detail::function<Config, fu2::abi_400::detail::property<IsThrowing, HasStrongExceptGuarantee, Args ...> >::assert_wrong_copy_assign_t<T>* <anonymous> = 0; fu2::abi_400::detail::function<Config, fu2::abi_400::detail::property<IsThrowing, HasStrongExceptGuarantee, Args ...> >::assert_no_strong_except_guarantee_t<T>* <anonymous> = 0; Config = fu2::abi_400::detail::config<false, true, fu2::capacity_default>; bool IsThrowing = true; bool HasStrongExceptGuarantee = false; Args = {int()}]’
main.cpp:9:42:   required from here
function2.hpp:249:26: error: invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive]
     return std::addressof(obj);

Steps to Reproduce

#include "function2.hpp"
int main()
{
  const auto callable = []{ 
      return 5;
  };  

  fu2::function_view<int()> view(callable);
}
g++ -std=c++14 main.cpp

Your Environment

>$ g++ --version
g++ (GCC) 7.3.1 20180303 (Red Hat 7.3.1-5)
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Naios commented 5 years ago

From my point of view this is the intended behaviour here because there is no way to access a method qualified as const from a non const context without ignoring the qualifier.

If you want to create a function_view to a const callable object use fu2::function_view<int() const> view(callable);

Implementing it in a way you are proposing would heavily violate the const guarantees provided by the library, because then the constness of an erased object is not propageted to the callable erasure anymore.

nitronoid commented 5 years ago

Hi, thanks for the fast reply. Firstly, the method you suggest fails with the same error:

#include "function2.hpp"

int main()
{
  const auto callable = []{ 
      return 5;
  };  

  fu2::function_view<int() const> view(callable);
}

Secondly I disagree with your final remark. If it is guaranteed that casting any pointer to void* and back will yield the same pointer, and you are not modifying the stored pointer, then I don't see where the violation occurs? This code appears valid to me:

#include "function2.hpp"
#include <iostream>

using func_t = int (*const)();

int main()
{
  const auto callable = [](){
      return 5;
  };

  func_t fptr = callable;
  void* ptr = (void*)(fptr);
  auto cptr = (func_t)(ptr);
  std::cout<<cptr()<<'\n';
}

I'm am however happy to be told otherwise, thanks

ladnir commented 5 years ago

See folly/function for a basic explanation. It's about halfway down and I quote the beginning:

Other than copyability, there is one more significant difference between std::function and folly::Function, and it concerns const-correctness. std::function does not enforce const-correctness: it allows you to store mutable callables (i.e. callables that may change their inner state when executed, such as a mutable lambda) and call them in a const context (i.e. when you only have access to a const reference to the std::function object). For example ...

nitronoid commented 5 years ago

That does make sense, so could you explain why my const example doesn't compile?

Naios commented 5 years ago

Seems like there is a bug in the implementation, I'll provide a fix for that soon.

wak-google commented 1 year ago

Any update on this? I was testing and noticed

  const auto callable = [] { 
      return 5;
  };  
  fu2::function_view<int() const> view(callable);

still doesn't work

Naios commented 1 year ago

@wak-google Sadly I did not look into this yet. Mainly I thought this issue has not a super high priority.

wak-google commented 1 year ago

Any update? :smiley:

Naios commented 1 year ago

Fixed in fa0b7082494c53fd3e393af758088aec54ce6304