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
545 stars 46 forks source link

function2 seems to fail when modeling functions returning references #35

Closed jurko-gospodnetic closed 4 years ago

jurko-gospodnetic commented 4 years ago

@Naios

Perhaps I'm misunderstanding something, but I have a use-case where fu2::function & fu2::unique_function usage fails to compile while the same code compiles and runs fine using std::function.


Commit Hash

noticed using function2.hpp sources from commit 7cd95374b0f1c941892bfc40f0ebb6564d33fdb9

Expected Behavior

fu2::function is a drop in replacement for std::function :-)

Actual Behavior

fu2::function and fu2::unique_function usage fails to compile when modeling functions returning references

Steps to Reproduce

run the following code - it should fail to compile, and then try commenting out the bottom part related to fu2::function & fu2::unique_function objects modeling a reference returning function, which should make the program work as expected:

#include <function2.hpp>

#include <iostream>
#include <functional>

class Blinky
{
public:
    Blinky(const char* data) : data_(data) {}
    Blinky(Blinky&&) = default;
    Blinky& operator=(Blinky&&) = default;

    const char* data() const { return data_; }

private:
    Blinky(const Blinky&) = delete;
    void operator=(const Blinky&) = delete;

private:
    const char* data_;
};
Blinky blinky("42");

typedef Blinky& AAAR;
AAAR xxxgr() { return blinky; }
const char* get_data(AAAR aaa) { return aaa.data(); }

typedef Blinky* AAAP;
AAAP xxxgp() { return &blinky; }
const char* get_data(AAAP aaa) { return aaa->data(); }

int main()
{
    // std::function works with all return types
    {
        std::function<AAAR ()> xxx((xxxgr));
        std::cout << "std::function with reference type: " << get_data(xxx()) << '\n';
    }
    {
        std::function<AAAP ()> xxx((xxxgp));
        std::cout << "std::function with pointer type: " << get_data(xxx()) << '\n';
    }

    // function2 variants work with pointer return types
    {
        fu2::function<AAAP ()> xxx((xxxgp));
        std::cout << "fu2::function with pointer type: " << get_data(xxx()) << '\n';
    }
    {
        fu2::unique_function<AAAP ()> xxx((xxxgp));
        std::cout << "fu2::unique_function with pointer type: " << get_data(xxx()) << '\n';
    }

    // function2 variants fail with reference return types
    {
        fu2::function<AAAR ()> xxx((xxxgr));
        std::cout << "fu2::function with reference type: " << get_data(xxx()) << '\n';
    }
    {
        fu2::unique_function<AAAR ()> xxx((xxxgr));
        std::cout << "fu2::unique_function with reference type: " << get_data(xxx()) << '\n';
    }
}

on a successful run I get the following output:

std::function with reference type: 42
std::function with pointer type: 42
fu2::function with pointer type: 42
fu2::unique_function with pointer type: 42

when testing a failed compile on https://www.onlinegdb.com/online_c++_compiler, I get the following output:

main.cpp: In instantiation of ‘constexpr auto fu2::abi_400::detail::type_erasure::tables::vtable<fu2::abi_400::detail::property<IsThrowing, HasStrongExceptGuarantee, FormalArgs ...> >::invoke(Args&& ...) const [with long unsigned int Index = 0ul; Args = {fu2::abi_400::detail::type_erasure::data_accessor*, const long unsigned int&}; bool IsThrowing = true; bool HasStrongExceptGuarantee = false; FormalArgs = {Blinky&()}]’:
<span class="error_line" onclick="ide.gotoLine('main.cpp',1215)">main.cpp:1215:36</span>:   required from ‘static constexpr auto fu2::abi_400::detail::type_erasure::erasure<IsOwning, Config, Property>::invoke(Erasure&&, Args&& ...) [with long unsigned int Index = 0ul; Erasure = fu2::abi_400::detail::type_erasure::erasure<true, fu2::abi_400::detail::config<true, true, fu2::capacity_default>, fu2::abi_400::detail::property<true, false, Blinky&()> >&; Args = {}; bool IsOwning = true; Config = fu2::abi_400::detail::config<true, true, fu2::capacity_default>; Property = fu2::abi_400::detail::property<true, false, Blinky&()>]’
<span class="error_line" onclick="ide.gotoLine('main.cpp',775)">main.cpp:775:1</span>:   required from ‘Ret fu2::abi_400::detail::type_erasure::invocation_table::operator_impl<Index, fu2::abi_400::detail::function<Config, Property>, Ret(Args ...)>::operator()(Args ...) [with long unsigned int Index = 0ul; Config = fu2::abi_400::detail::config<true, true, fu2::capacity_default>; Property = fu2::abi_400::detail::property<true, false, Blinky&()>; Ret = Blinky&; Args = {}]’
<span class="error_line" onclick="ide.gotoLine('main.cpp',1825)">main.cpp:1825:73</span>:   required from here
main.cpp:995:45: error: ‘Blinky::Blinky(const Blinky&)’ is private within this context
     return thunk(std::forward<Args>(args)...);
                                             ^
main.cpp:1784:2: note: declared private here
  Blinky(const Blinky&) = delete;
  ^~~~~~
main.cpp:995:45: error: use of deleted function ‘Blinky::Blinky(const Blinky&)’
     return thunk(std::forward<Args>(args)...);
                                             ^
main.cpp:1784:2: note: declared here
  Blinky(const Blinky&) = delete;
  ^~~~~~
main.cpp: In instantiation of ‘Ret fu2::abi_400::detail::type_erasure::invocation_table::operator_impl<Index, fu2::abi_400::detail::function<Config, Property>, Ret(Args ...)>::operator()(Args ...) [with long unsigned int Index = 0ul; Config = fu2::abi_400::detail::config<true, true, fu2::capacity_default>; Property = fu2::abi_400::detail::property<true, false, Blinky&()>; Ret = Blinky&; Args = {}]’:
<span class="error_line" onclick="ide.gotoLine('main.cpp',1825)">main.cpp:1825:73</span>:   required from here
main.cpp:771:38: error: invalid initialization of non-const reference of type ‘Blinky&’ from an rvalue of type ‘Blinky’
           std::forward<Args>(args)...);                                        \
                                      ^
main.cpp:495:3: note: in expansion of macro ‘FU2_DEFINE_FUNCTION_TRAIT’
   F(, , , , &)                                                                 \
   ^
main.cpp:775:1: note: in expansion of macro ‘FU2_DETAIL_EXPAND_QUALIFIERS’
 FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

Your Environment

tested on https://www.onlinegdb.com/online_c++_compiler

And quite likely similar errors occur using Visual Studio 2017 Update 3 compiler as I got to this example by attempting to trim down some code giving a warning on that compiler about a reference to a local variable or a temporary being returned. (The warning output there is completely useless as it just points to some FU2_DETAIL_EXPAND_QUALIFIERS() macro call and shows no additional code or information :-()

Naios commented 4 years ago

Thanks for your report, I fixed this in 93089d56b. It will be available once the fix passed the CI.

jurko-gospodnetic commented 4 years ago

Confirmed this fixes both the problem reported here and the one I was seeing in my Visual Studio project, :-) Thanks!