Подтвердить что ты не робот

Преобразование шаблона Variadic в std:: function <R (ARGS...)> работает с GCC, а не с MSVC2013, почему?

Если это дубликат, я утверждаю. В моих поисках я ничего не нашел.

Я могу использовать любую из новейших функций С++ 11/С++ 14. При необходимости я могу перейти на VS2015.

Я пытаюсь написать класс, который будет автоматически добавлен в std:: function с определенной подписью при назначении. У меня есть код, который работает с GCC, но это не удалось на MSVC2013. Код - это фрагмент, который воссоздает ошибку. WTF MSVC?!

Кроме того, я знаю, что это рискованный код, автоматически набрасывая указатели на функции и т.д., но это для частной реализации библиотеки плагинов, и я только хочу определить подпись функции один раз.

Если есть другой способ написать код, который выполняет те же функции в main() и работает на обоих, я все уши.

GCC С++ 11 отлично работает - Демо

#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 работает, когда я меняю строку на это...

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

MSVC терпит неудачу с той же ошибкой, когда у меня есть это...

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

Ошибка MSVC со следующей ошибкой в ​​файле, который трудно читать, если не сказать больше. Кажется, он выводит неправильную сигнатуру функции.

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>          ]
4b9b3361

Ответ 1

Это другой подход к решению вашей проблемы. Если мое понимание возможностей MSVC 2015 верное, оно должно работать там.

(Я предполагаю, что ваша проблема заключается в том, что вы хотите относительно прозрачно передать a void* неизвестной функции в std::function с сигнатурой, которая имеет неизвестную функцию, без необходимости повторять подпись функции.)

Вместо того, чтобы отбрасывать указатель void в точке, где мы отбрасываем std::function, вместо этого я делаю это в той точке, где вызывается функция, и вычисляется возвращаемое значение (или, когда не возвращается) трюк "возврат типа вывода":

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 };
  }
};

живой пример.

Когда std::function вызывает их вызываемые, они либо отбрасывают результат, либо отбрасывают его на R. Из аргументов, переданных вызываемому, плюс тип, возвращаемое значением, я могу восстановить (в основном) подпись std::function, которая вызвала меня.

В этот момент я передал свой void* в указатель на эту функцию, вызвал ее и вернул результат.

Если меня никогда не бросают на что-то, в момент разрушения я отбрасываю свой указатель на функцию возврата void, вызываю ее и делаю.


Предостережения:

Обратите внимание, что прямой вызов FunctionPointer столь же опасен, как передача его в std::function в вашем примере (крайне).

Действительно, сохранение указателя функции в std::function является излишним.

Не тестировался в MSVC2015, но я не вижу ничего, что бы не работало в MSVC2015.

Существует вероятность того, что некоторые реализации std::function могут не работать с указанным выше, если ваша функция возвращает void. Это, однако, было бы ошибкой времени компиляции.

Вышеупомянутое также предполагает, что нет функций с аргументами ссылки rvalue, поскольку в точке вызова я не могу различить, что вы вызываете из std::function<void(T)> и a std::function<void(T&&)>. Я предполагаю, что в этом случае это void(T).

Ответ 2

Вот как я мог бы избежать кастинга вне класса:

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);
}
};

Кроме того, вы можете воспользоваться тем фактом, что std:: function может указывать на любой вызываемый объект, сделав свой указатель на функцию вызываемым (добавьте оператор()), который будет очень похож на преобразование в 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...);
}