microsoft / Range-V3-VS2015

A fork of the popular range-v3 C++ library with support for the Visual Studio 2015 Update 3 VC++ compiler.
Other
115 stars 22 forks source link

`Callable` concept doesn't play well with the calling convention decl of MSVC #11

Closed LYP951018 closed 7 years ago

LYP951018 commented 7 years ago
#include <range/v3/utility/functional.hpp>

void __stdcall Foo() {}

int main() 
{
    static_assert(ranges::Callable<decltype(Foo)>::value, "");
}

leads to compile error.

CaseyCarter commented 7 years ago

That repro technically should result in a compile error, since the Callable concept only admits function objects and decltype(Foo) is a function type, not an object type. It would, however, be nice if this nearly identical program worked:

#include <range/v3/utility/functional.hpp>

void __stdcall Foo() {}

int main() 
{
    CONCEPT_ASSERT(ranges::Callable<decltype(&Foo)>::value);
}

which does test a function object type.

The root failure here is that range-v3 wants to wrap function pointers in an object of type ptr_fn_ when passing them through as_function:

template<typename R, typename...Args>
struct ptr_fn_
{
private:
    R (*pfn_)(Args...);
public:
    ptr_fn_() = default;
    explicit ptr_fn_(R (*pfn)(Args...))
        : pfn_(pfn)
    {}
    R operator()(Args...args) const
    {
        return (*pfn_)(std::forward<Args>(args)...);
    }
};
// ... later, defining an operator() overload of `as_function`:
template<typename R, typename ...Args>
ptr_fn_<R, Args...> operator()(R (*p)(Args...)) const
{
    return ptr_fn_<R, Args...>(p);
}

This overload is not viable for non-default calling conventions, and the others refuse function pointers of all kinds, so as_function effectively does not accept non-default calling convention function pointers. However, there seems to be no reason to wrap function pointers: they are already perfectly good function object types. I think we can eliminate the ptr_fn_ class template and relax the constraints on the as_function_fn::operator() overload that simply returns its argument so that it also accepts function pointers and have an equivalent as_function that also works with non-default calling conventions.

ericniebler commented 7 years ago

I recall creating ptrfn because I was inheriting from the result of as_function in places to take advantage of EBO.

CaseyCarter commented 7 years ago

I recall creating ptrfn because I was inheriting from the result of as_function in places to take advantage of EBO.

That is no longer the case. If it were, we could easily wrap the result in a box<> that can provide EBO-as-a-service.