22

If this is a duplicate I appologize. I couldn't find anything in my searches though.

I can use any of the newest features c++11/c++14. I can upgrade to VS2015 if necessary.

I'm trying to write a class that will auto cast into a std::function with a specific signature when assigned. I have code that works with GCC, but it failed on MSVC2013. The code is a snippet that recreates the error. WTF MSVC?!

Also I know this is risky code, automatically casting function pointers and such, but it's for the private implementation of a plugin library and I only want to define the function signature once.

If there is another way to write the code that accomplishes the same functionality in main() and works on both, I'm all ears.

GCC c++11 works fine - Demo

#include <functional>
#include <string>
#include <iostream>

class FunctionPointer
{
    void* fp;
public:
    FunctionPointer(void* ptr)
        : fp(ptr)
    {}

    // Overload casting operator to 
    // a certain function signiture
    template<class R, class... ARGS>
    operator std::function<R(ARGS...)>(){
        typedef R(*func_ptr)(ARGS...);
        return std::function<R(ARGS...)>((func_ptr)fp);
    }
};

void hello(std::string msg){
    std::cout << "Hello " << msg << std::endl;
}

int main() {

    FunctionPointer f((void*)hello);

    std::function<void(std::string)> func_hello = f;

    func_hello("World!");

    return 0;
}

MSVC works when I change the line to this...

std::function<void(std::string)> func_hello = f.operator std::function<void(std::string)>();

MSVC fails with the same error when I have this...

std::function<void(std::string)> func_hello = (std::function<void(std::string)>)f;

MSVC fails with the following error in a file that is hard to read to say the least. It seems to be deducing the wrong function signature.

xrefwrap.h:283 - error C2064: term does not evaluate to a function taking 1 arguments


1>c:\program files (x86)\microsoft visual studio 12.0\vc\include\xrefwrap(283): error C2064: term does not evaluate to a function taking 1 arguments
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(228) : see reference to function template instantiation '_Ret std::_Callable_obj<FunctionPointer,false>::_ApplyX<_Rx,_Ty>(_Ty &&)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Rx=void
1>  ,            _Ty=std::basic_string<char,std::char_traits<char>,std::allocator<char>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(228) : see reference to function template instantiation '_Ret std::_Callable_obj<FunctionPointer,false>::_ApplyX<_Rx,_Ty>(_Ty &&)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Rx=void
1>  ,            _Ty=std::basic_string<char,std::char_traits<char>,std::allocator<char>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(226) : while compiling class template member function 'void std::_Func_impl<_MyWrapper,_Alloc,_Ret,std::string>::_Do_call(std::string &&)'
1>          with
1>          [
1>              _Alloc=std::allocator<std::_Func_class<void,std::string>>
1>  ,            _Ret=void
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(495) : see reference to class template instantiation 'std::_Func_impl<_MyWrapper,_Alloc,_Ret,std::string>' being compiled
1>          with
1>          [
1>              _Alloc=std::allocator<std::_Func_class<void,std::string>>
1>  ,            _Ret=void
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(396) : see reference to function template instantiation 'void std::_Func_class<_Ret,std::string>::_Do_alloc<_Myimpl,FunctionPointer&,_Alloc>(_Fty,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Alloc=std::allocator<std::_Func_class<void,std::string>>
1>  ,            _Fty=FunctionPointer &
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(396) : see reference to function template instantiation 'void std::_Func_class<_Ret,std::string>::_Do_alloc<_Myimpl,FunctionPointer&,_Alloc>(_Fty,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Alloc=std::allocator<std::_Func_class<void,std::string>>
1>  ,            _Fty=FunctionPointer &
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(385) : see reference to function template instantiation 'void std::_Func_class<_Ret,std::string>::_Reset_alloc<FunctionPointer&,std::allocator<std::_Func_class<_Ret,std::string>>>(_Fty,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Fty=FunctionPointer &
1>  ,            _Alloc=std::allocator<std::_Func_class<void,std::string>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(385) : see reference to function template instantiation 'void std::_Func_class<_Ret,std::string>::_Reset_alloc<FunctionPointer&,std::allocator<std::_Func_class<_Ret,std::string>>>(_Fty,_Alloc)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Fty=FunctionPointer &
1>  ,            _Alloc=std::allocator<std::_Func_class<void,std::string>>
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(671) : see reference to function template instantiation 'void std::_Func_class<_Ret,std::string>::_Reset<FunctionPointer&>(_Fty)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Fty=FunctionPointer &
1>          ]
1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\functional(671) : see reference to function template instantiation 'void std::_Func_class<_Ret,std::string>::_Reset<FunctionPointer&>(_Fty)' being compiled
1>          with
1>          [
1>              _Ret=void
1>  ,            _Fty=FunctionPointer &
1>          ]
1>          c:\users\cameron\desktop\desktop\programming\projects\c++ projects\garbage\templatetest\main.cpp(32) : see reference to function template instantiation 'std::function<void (std::string)>::function<FunctionPointer&>(_Fx)' being compiled
1>          with
1>          [
1>              _Fx=FunctionPointer &
1>          ]
1>          c:\users\cameron\desktop\desktop\programming\projects\c++ projects\garbage\templatetest\main.cpp(32) : see reference to function template instantiation 'std::function<void (std::string)>::function<FunctionPointer&>(_Fx)' being compiled
1>          with
1>          [
1>              _Fx=FunctionPointer &
1>          ]
LThode
  • 1,843
  • 1
  • 17
  • 28
extracrispy
  • 669
  • 5
  • 16
  • 3
    At least one part of this is the "`std::function` has a constructor that accepts everything under the sun" problem. This is a defect in the standard that has been fixed, but the fix requires expression SFINAE to implement, which MSVC doesn't yet support. – T.C. Jun 28 '15 at 23:09
  • Easy. Just remove `FunctionPointer` and directly construct `func_hello` from `hello`. There is no reason in your example main to round-trip through a `void*`. There may be a reason in your actual code base, but I haven't seen any evidence, and I doubt it. – Yakk - Adam Nevraumont Jun 29 '15 at 21:58
  • 1
    @Yakk: but GetProcAddress()/dlsym() bro. – extracrispy Jul 01 '15 at 16:08
  • This will go against all sane advice, but since you said this is only for internal use, and you're supposed to know exactly what you're doing, here it goes: a `my::function` template derived from `std::function`, with a constructor taking a `void*` that does basically the same thing as your conversion function. This means you get rid of `FunctionPointer` and work directly with `void*`. You only use this for initialization; when you pass it on, you copy / bind references to `std::function`. – bogdan Jul 02 '15 at 10:00
  • Just don't allocate `my::function<...>` objects with `new` and attempt to `delete` them through a pointer to `std::function<...>` - that would *technically* be undefined behaviour, although nothing bad should happen in any implementation that I know of, since the size doesn't change and the derived class' destructor doesn't do anything special. – bogdan Jul 02 '15 at 10:03
  • It looks like VS2015 RC does not compile it either. – Dean Seo Jul 03 '15 at 06:06
  • 1
    Sorry, just trying to wrap my head around what you're trying to accomplish so I can suggest something else. What does FunctionPointer give you over just using std::function directly? Type erasure? – Nir Friedman Jul 12 '15 at 00:13
  • If you make this a member function instead of a conversion operator (i.e. `template std::function as_func(){...}`) and then invoke it explicitly: `f.as_func()` there's no problem. :/ – melak47 Aug 10 '15 at 06:34
  • ICC 13 rejects this-- the = case fails with: "error: more than one user-defined conversion from "FunctionPointer" to "std::function" applies: function template "FunctionPointer::operator std::function()" function template "std::function<_Res (_ArgTypes...)>::function(_Functor, std::enable_if<, std::function::_Useless>::type) [with _Res=void, _ArgTypes=]"" while the cast also fails, but with "error: call of an object of a class type without appropriate operator() or conversion functions to pointer-to-function type" – LThode Aug 13 '15 at 15:12

2 Answers2

1

This is a different approach to solve your problem. If my understanding of MSVC 2015's capabilities is right, it should work there.

(I assume your problem is wanting to relatively transparently cast a void* to an unknown function into a std::function with the signature that unknown function actually has, without having to repeat the signature of the function.)

Instead of casting the void pointer at the point where we are cast to the std::function, instead I do it at the point where the function is called and the return value calculated (or, when no return value is calculated) via a "return type deduction" trick:

template<class...Args>
struct void_ptr_deferred_execution_t {
  std::tuple<Args&&...> args;
  void const* pf = nullptr;
  void_ptr_deferred_execution_t( std::tuple<Args&&...> a, void const* p ):
    args(std::move(a)),
    pf(p)
  {}
  template<class R, size_t...Is>
  R invoke( std::index_sequence<Is...> ){
    using f_t = R(*)(Args...);
    f_t f = f_t(pf);
    pf = nullptr;
    return f(std::forward<Args>(std::get<Is>(args))...);
  }
  template<class R>
  operator R()&&{
    return invoke<R>( std::index_sequence_for<Args...>{} );
  }
  ~void_ptr_deferred_execution_t() {
    if (pf) invoke<void>(std::index_sequence_for<Args...>{});
  }
};

class FunctionPointer
{
  void* fp;
public:
  FunctionPointer(void* ptr)
    : fp(ptr)
  {}

  template<class...Args>
  void_ptr_deferred_execution_t<Args...>
  operator()(Args&&...args)const {
    return { std::forward_as_tuple(std::forward<Args>(args)...), fp };
  }
};

live example.

When the std::function invokes their callable, they either discard the result or cast it to an R. From the arguments passed to the callable, plus the type the return value is cast to, I can reconstruct (mostly) the signature of the std::function that called me.

At that point, I cast my void* into a pointer to that kind of function, call it, and return the result.

If I am never cast to something, at the point of destruction I cast my function pointer to a void returning function, call it, and am done.


Cautions:

Note that directly calling FunctionPointer is as dangerous as passing it to a std::function in your example (extremely).

Really, storing a function pointer in a std::function is overkill.

Not tested in MSVC2015, but I don't see anything that wouldn't work in MSVC2015.

There is a possibility that some implementations of std::function may fail to work with the above if your function returns void. It would, however, be a compile-time error.

The above also assumes that there are no functions with rvalue reference arguments, in that at the point of calling, I cannot distinguish between being called from a std::function<void(T)> and a std::function<void(T&&)>. I presume it is a void(T) in that case.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

This is how I would avoid some casting outside the class:

class FunctionPointer
{
 void* fp;
    public:
FunctionPointer() = default;

template<class Ret, class Fn, class... Args> void assign_fn(Fn fn, Args... args)
{
    std::function<Ret(Args...)> *f = new std::function<Ret(Args...)>;
    *f = Fn; // Tip: you can use enable_if combined with is_assignable to make sure this can be done.
    // Both are regular pointers, so conversion shouldn't be a problem.
    fp = reinterpret_cast<void*>(f);
}

// Overload casting operator to 
// a certain function signature
template<class R, class... ARGS>
operator std::function<R(ARGS...)>(){
    typedef R(*func_ptr)(ARGS...);
    return std::function<R(ARGS...)>((func_ptr)fp);
}
};

Additionally, you might take advantage of the fact that std::function can point to any callable object, by making your function pointer callable (add an operator()), which would be very similar to converting to std::function.

template<class Ret, class... Args> Ret operator()(Args... args)
{
     std::function<Ret(Args...)> f = *(reinterpret_cast<std::function<Ret(Args...)>*>(fp));
     return f(args...);
}
Dani
  • 169
  • 2
  • 5