C++11 function 类模板与__stdcall的问题

ULTRON19 2020-07-08 09:19:21
先上代码

#include <cstdio>
#include <functional>
#include <windows.h>

class TEST
{
public:
auto Get_Func() -> std::function <int __stdcall (char)>;

private:
int CALLBACK call_func(char);
};

auto TEST::Get_Func() -> std::function <int __stdcall (char)>
{
return std::bind (&TEST::call_func, this, std::placeholders::_1);
}

int CALLBACK TEST::call_func(char a)
{
return a <= '9' && a >= '0';
}

int main_func(std::function <int __stdcall (char)> abc, char c)
{
return abc (c);
}

int main()
{
TEST t;
return main_func(t.Get_Func(), getchar ());
}


这段代码在DEV下的64位编译器能正常编译,但32位就会报错,在VS2019下32/64位均能编译。我的初衷是想返回一个__stdcall函数的指针,不过因为this指针的原因我选用了function 类模板,不过还是报错了。具体报错信息如下:

[Error] return type 'class std::function<int(char)>' is incomplete


然后我把__stdcall删了就可以编译,对此还是感到很奇怪,想请教一下各位大神,是我的代码不规范还是编译器的锅,亦或者是__stdcall在32/64位是不同的?感激不尽!
...全文
632 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
ULTRON19 2020-07-20
  • 打赏
  • 举报
回复
问题总算解决了!结贴!
ULTRON19 2020-07-20
  • 打赏
  • 举报
回复
引用 13 楼 _mervyn 的回复:
[quote=引用 12 楼 ULTRON19 的回复:]然后转发的函数是不需要的?
当然不需要啊。。[/quote]好像确实如此诶,我注意到 MSDN 的一段示例代码里:
LRESULT WndProc (HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
		static LRESULT CALLBACK G_WndProc (HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);

LRESULT CALLBACK WinApp <T>::G_WndProc (HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	WinApp* pThis = nullptr;
    LRESULT lRet = 0;
    
    if (Message == WM_NCCREATE)
    {
    	auto pcs = reinterpret_cast <LPCREATESTRUCT> (lParam);
        pThis = reinterpret_cast <WinApp*> (pcs -> lpCreateParams);

        SetWindowLongPtr (hwnd, GWLP_USERDATA, reinterpret_cast <LONG_PTR> (pThis));
        lRet = DefWindowProc (hwnd, Message, wParam, lParam);
	}
	
	else
	{
		pThis = reinterpret_cast <WinApp*> (GetWindowLongPtr (hwnd, GWLP_USERDATA));
		
        if (pThis)
        {
            lRet = pThis -> WndProc (hwnd, Message, wParam, lParam);
        }
        
        else
        {
            lRet = DefWindowProc (hwnd, Message, wParam, lParam);
        }
	}
	
	return lRet;
}
果然是不需要__stdcall的,看来是我太固执了……
ULTRON19 2020-07-20
  • 打赏
  • 举报
回复
这几天电脑坏了
_mervyn 2020-07-11
  • 打赏
  • 举报
回复
引用 12 楼 ULTRON19 的回复:
然后转发的函数是不需要的?
当然不需要啊。。
_mervyn 2020-07-10
  • 打赏
  • 举报
回复
引用 楼主 ULTRON19 的回复:
然后我把__stdcall删了就可以编译,对此还是感到很奇怪,
你之前还说删了可以编译,现在怎么删了不可以了?
ULTRON19 2020-07-10
  • 打赏
  • 举报
回复
引用 7 楼 _mervyn 的回复:
[quote=引用 楼主 ULTRON19 的回复:] 然后我把__stdcall删了就可以编译,对此还是感到很奇怪,
你之前还说删了可以编译,现在怎么删了不可以了?[/quote]这个删除包含了int CALLBACK call_func 的 CALLBACK,全部删掉就可以,怪我之前没说清楚……
ULTRON19 2020-07-10
  • 打赏
  • 举报
回复
引用 10 楼 _mervyn 的回复:
[quote=引用 8 楼 ULTRON19 的回复:] 这个删除包含了int CALLBACK call_func 的 CALLBACK,全部删掉就可以,怪我之前没说清楚……
成员函数在vs里默认就是被调用者清栈的,本来就不需要加。不过,我不太清楚这个是否属于标准。 你为什么一定要给成员函数加上stdcall呢?,能对你产生什么影响? 你实际需求是啥?为什么这么关注调用约定?[/quote]我是想将Window窗体程序的那些做初始化工作的代码,包括注册窗体类,创建窗体,消息主循环这些封装起来,但众所周知,在这里面有一个消息队列函数
LRESULT CALLBACK WndProc (HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
由于这个函数可以监听消息,程序有时候需要根据这个消息做出变化,所以这个函数理应不该被完全封装,所以我才想到传入一个外部类的函数指针和一个外部类的对象来实现具体的代码细节,后来发现需要使用function,大概就是下面这样:
LRESULT CALLBACK WndProc (HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{	
	if (call_func) // call_func 是 std::function <LRESULT /* CALLBACK */ (HWND, UINT, WPARAM, LPARAM)>; 
		call_func (hwnd, Message, wParam, lParam); // 转发给外部类处理 
	
	switch (Message)
	{
		// ...
	}

	return 0;
}
会不会这个__stdcall 只需要出现在原有的消息队列就可以了?然后转发的函数是不需要的?
ULTRON19 2020-07-10
  • 打赏
  • 举报
回复
引用 9 楼 uouo88 的回复:
我记得返回值也是function模板参数的一部分的,貌似需要加上去,或者你可以直接使用mem_fn模板函数或者bind来代替。 men_fn和bind内部会转化this指针作为参数的调用,会自动使用形如object.*func的方式解引用从而调用函数指针。
怎么感觉 bind 好像无法识别调用约定……
_mervyn 2020-07-10
  • 打赏
  • 举报
回复
引用 8 楼 ULTRON19 的回复:
这个删除包含了int CALLBACK call_func 的 CALLBACK,全部删掉就可以,怪我之前没说清楚……
成员函数在vs里默认就是被调用者清栈的,本来就不需要加。不过,我不太清楚这个是否属于标准。 你为什么一定要给成员函数加上stdcall呢?,能对你产生什么影响? 你实际需求是啥?为什么这么关注调用约定?
uouo88 2020-07-10
  • 打赏
  • 举报
回复
我记得返回值也是function模板参数的一部分的,貌似需要加上去,或者你可以直接使用mem_fn模板函数或者bind来代替。 men_fn和bind内部会转化this指针作为参数的调用,会自动使用形如object.*func的方式解引用从而调用函数指针。
ULTRON19 2020-07-09
  • 打赏
  • 举报
回复
引用 1 楼 Simple-Soft 的回复:
Dev 是gcc,可以这样试试 __attribute__((__stdcall__))
这个也不行,报错信息是
[Error] template argument 1 is invalid
应该是function类模板中
std::function <int __attribute__((__stdcall__)) (char)>
编译器将__attribute__((__stdcall__))括号内的__stdcall__当成了形参,请问这个是否有解决方案?
ULTRON19 2020-07-09
  • 打赏
  • 举报
回复
引用 4 楼 _mervyn 的回复:
[quote=引用 3 楼 _mervyn 的回复:]所以你定义成 std::function <int(char)> 就完全可以了。
删了__stdcall , 定义成std::function <int(char)> 就可以了。 [/quote]其实我最初的代码是这样子的
#include <cstdio>
#include <functional>
#include <windows.h>

class TEST
{
public:
	auto Get_Func() -> int (TEST::*) (char);

private:
	int CALLBACK call_func(char);
};

auto TEST::Get_Func() -> int (TEST::*) (char)
{
	return & call_func;
}

int CALLBACK TEST::call_func(char a)
{
	return a <= '9' && a >= '0';
}

int main_func(int (TEST::* abc) (char), TEST* t, char c)
{
	return (t ->* abc) (c);
}

int main ()
{
	TEST t;
	return main_func(t.Get_Func(), &t, getchar ());
}
但是由于this指针的原因,return & call_func; 这句代码是非法的 (虽然它能在DEV的 64位编译通过),而且我也不想再写一个自定义类适配器,(比如这篇文章讲的),于是我最终才采用了function的写法……
ULTRON19 2020-07-09
  • 打赏
  • 举报
回复
引用 4 楼 _mervyn 的回复:
[quote=引用 3 楼 _mervyn 的回复:]所以你定义成 std::function <int(char)> 就完全可以了。
删了__stdcall , 定义成std::function <int(char)> 就可以了。 [/quote]我刚刚试了一下,还是在dev的32位编译出了问题,报错信息如下
[Error] could not convert 'std::bind(_Func&&, _BoundArgs&& ...) [with _Func = int (TEST::*)(char); _BoundArgs = {TEST*, const std::_Placeholder<1>&}; typename std::_Bind_helper<std::__or_<std::is_integral<typename std::decay<_Tp>::type>, std::is_enum<typename std::decay<_Tp>::type> >::value, _Func, _BoundArgs ...>::type = std::_Bind<std::_Mem_fn<int (TEST::*)(char)>(TEST*, std::_Placeholder<1>)>]((* &((TEST*)this)), (* & std::placeholders::_1))' from 'std::_Bind_helper<false, int (TEST::*)(char), TEST*, const std::_Placeholder<1>&>::type {aka std::_Bind<std::_Mem_fn<int (TEST::*)(char)>(TEST*, std::_Placeholder<1>)>}' to 'std::function<int(char)>'
这下感觉更懵逼了
Simple-Soft 2020-07-09
  • 打赏
  • 举报
回复
Dev 是gcc,可以这样试试 __attribute__((__stdcall__))
_mervyn 2020-07-09
  • 打赏
  • 举报
回复
引用 3 楼 _mervyn 的回复:
所以你定义成 std::function <int(char)> 就完全可以了。
删了__stdcall , 定义成std::function <int(char)> 就可以了。
_mervyn 2020-07-09
  • 打赏
  • 举报
回复
不知道你在说什么,你代码显示你想要function ,可你的文字描述却是想要函数指针??, 你到底是想要 函数指针 还是 function ? 另外 :
引用 楼主 ULTRON19 的回复:
我的初衷是想返回一个__stdcall函数的指针,不过因为this指针的原因我选用了function 类模板,
没听明白你在表达什么。 __stdcall 是 __stdcall, __thiscall 是 __thiscall , 是不一样的。 1、从你代码例子来看你是想返回function, function的模板参数不需要你传什么调用约定,它的形参并不是真正的函数指针类型,它是自动根据你的实参来决定用什么版本,来决定用哪个构造或赋值重载的。所以你定义成 std::function <int(char)> 就完全可以了。 你可以传任何返回值为int,只带一个char参数的可调用对象。 至于调用约定,它能根据你实际传入的实参正确选择到。 2、如果你是已知一个function,想得到其保存的函数指针,可以使用其target函数。target的形参才是真正的函数指针类型,比如:

f.target<int(__stdcall*)(char)>();
//or
typedef int (__stdcall* YourFuncType)(char);
f.target<YourFuncType>();

//上面 f 的类型为std::function<int(char)>, 且其实际存储的函数指针类型符合int(__stdcall*)(char),target()才能返回值,否则返回null
也可以使用其target_type函数来判断其类型

f.target_type() == typeid(int(__stdcall*)(char));
另外,我简单表达一下function的实现原理。有助于你理解它的模板形参。

template<typename T>
struct MyFunction;   //前向声明;

//////////////////////////////////////////////////////////////////////////
//为各种情况特化;
//函数别名:
template<typename Ret, typename... Args>
struct MyFunction< Ret(Args...)>、
{
//...
}
//函数指针:
template<typename Ret, typename... Args>
struct MyFunction<Ret(*)(Args...)> : MyFunction<Ret(Args...)> {};
//stdcall 版:
template<typename Ret, typename... Args>
struct MyFunction<Ret(__stdcall *)(Args...)> : MyFunction<Ret(Args...)> {};
//...其他各种调用约定...略

//各种类方法(成员函数):
#define METHOD_TRAITS(...)   \
template <typename Ret, typename Cx, typename... Args>  \
struct MyFunction<Ret(Cx::*)(Args...) __VA_ARGS__> : MyFunction<Ret(Args...)> {};

METHOD_TRAITS()
METHOD_TRAITS(const)  //const成员函数;
METHOD_TRAITS(volatile)
METHOD_TRAITS(const volatile)

// 对拥有 cv 限定符的类型的特化;
template<typename T>
struct MyFunction<const T> : MyFunction<T> {};

template<typename T>
struct MyFunction<volatile T> : MyFunction<T> {};

// 对拥有引用限定符的类型的特化;
template<typename T>
struct MyFunction<T&> : MyFunction<T> {};

template<typename T>
struct MyFunction<T&&> : MyFunction<T> {};	
用户在使用MyFunction的时候,实际的实参是啥,自然就能找到对应的版本。 而不需要用户手动传入实参的准确类型。

64,683

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧