[C/C++]_[Linux,Windows,MacOSX控制台即时响应按键消息]

本文介绍了在进行多线程单元测试时,如何优化控制台打印数据的方式,避免实时输出导致的回车键输入延迟和屏幕刷屏问题。通过实现`PressAnyKey`函数,用户只需按任意键即可继续,同时提供了针对Linux、Unix、MacOSX和Windows系统的不同实现方式。


场景:

1. 在做多线程单元测试时难免需要在控制台打印数据,如果是实时输出的话,通过getc方式还需要输入一个回车键,不能及时响应,还有就是屏幕刷屏时可能都看不清自己敲了什么字母. 关键代码PressAnyKey是参考cplusplus的。

参考: http://www.cplusplus.com/forum/articles/7312/


Linux,Unix,MacOSX

#include <stdio.h>
#include <assert.h>
#include <string>
#include "gtest/gtest.h"
#include "gtest/internal/gtest-filepath.h"
#include "gtest/internal/gtest-port.h"
#include <pthread.h>

#include <unistd.h>
#include <termios.h>

//http://www.cplusplus.com/forum/articles/7312/
int PressAnyKey( const char* prompt )
{
  #define MAGIC_MAX_CHARS 18
	struct termios initial_settings;
	struct termios settings;
	unsigned char  keycodes[ MAGIC_MAX_CHARS ];
	int            count;

	tcgetattr( STDIN_FILENO, &initial_settings );
	settings = initial_settings;

  /* Set the console mode to no-echo, raw input. */
  /* The exact meaning of all this jazz will be discussed later. */
	settings.c_cc[ VTIME ] = 1;
	settings.c_cc[ VMIN  ] = MAGIC_MAX_CHARS;
	settings.c_iflag &= ~(IXOFF);
	settings.c_lflag &= ~(ECHO | ICANON);
	tcsetattr( STDIN_FILENO, TCSANOW, &settings );

	printf( "%s", prompt ? prompt : "Press a key to continue..." );
	int fo = fileno(stdin);
	count = read( fo, (void*)keycodes, MAGIC_MAX_CHARS );

	tcsetattr( STDIN_FILENO, TCSANOW, &initial_settings );

	return (count == 1)
	? keycodes[ 0 ]
	: -(int)(keycodes[ count -1 ]);
}

void * StartPthread(void * arg)
{
	static int i = 0;
	while(true)
	{
		GTEST_LOG_(INFO) << "StartPthread: " << i++ ;
		sleep(2);
	}
}

int main(int argc, char const *argv[])
{
#if 1
	pthread_t t1;  
    pthread_create(&t1, NULL, StartPthread, NULL);  
    pthread_detach(t1);  

	while(true)
	{
		int c = PressAnyKey("Press any key to continue.");
		GTEST_LOG_(INFO) << "c: " << c ;
		if(c == 'q')
		{
			break;
		}
	}
#endif
	return 0;
}



Windows:

/* ---------------------------------------------------------------------------
 * PressAnyKey()
 * ---------------------------------------------------------------------------
 * Copyright 2008 Michael Thomas Greer
 * http://www.boost.org/LICENSE_1_0.txt
 *
 * function
 *   Optionally print a message and and wait for the user to press (and
 *   release) a single key.
 *
 * arguments
 *   The message to print. If NULL, uses a default message. Specify the empty
 *   string "" to not print anything.
 *
 * returns
 *   The virtual keycode for the key that was pressed.
 *
 *   Windows #defines virtual keycode values like
 *     VK_UP
 *     VK_DOWN
 *     VK_RIGHT
 *     VK_LEFT
 *   which you can use to identify special keys.
 *
 *   Letter keys are simply the upper-case ASCII value for that letter.
 */
#include <windows.h>
#include <iostream>


using namespace std;


int PressAnyKey( const char *prompt )
{
  DWORD        mode;
  HANDLE       hstdin;
  INPUT_RECORD inrec;
  DWORD        count;
  char         default_prompt[] = "Press a key to continue...";

  /* Set the console mode to no-echo, raw input, */
  /* and no window or mouse events.              */
  hstdin = GetStdHandle( STD_INPUT_HANDLE );
  if (hstdin == INVALID_HANDLE_VALUE
    || !GetConsoleMode( hstdin, &mode )
    || !SetConsoleMode( hstdin, 0 ))
    return 0;

  if (!prompt) prompt = default_prompt;

  /* Instruct the user */
  WriteConsole(
    GetStdHandle( STD_OUTPUT_HANDLE ),
    prompt,
    lstrlen( prompt ),
    &count,
    NULL
    );

  FlushConsoleInputBuffer( hstdin );

  /* Get a single key RELEASE */
  do ReadConsoleInput( hstdin, &inrec, 1, &count );
  while ((inrec.EventType != KEY_EVENT) || inrec.Event.KeyEvent.bKeyDown);

  /* Restore the original console mode */
  SetConsoleMode( hstdin, mode );

  return inrec.Event.KeyEvent.wVirtualKeyCode;
}

int main(int argc, char const *argv[])
{
  int keyCode = PressAnyKey(NULL);
  cout << "keyCode: " << (char)keyCode << endl;
  return 0;
}






In file included from /Users/avery/Workspace/bottle-server/src/WebSocketSession.cpp:1: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream:37: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/ios:214: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__locale:15: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/string:522: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/algorithm:653: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/functional:500: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__functional/function.h:18: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/allocator_traits.h:298:9: error: no matching function for call to 'construct_at' _VSTD::construct_at(__p, _VSTD::forward<_Args>(__args)...); ^~~~~~~~~~~~~~~~~~~ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__config:858:15: note: expanded from macro '_VSTD' #define _VSTD std::_LIBCPP_ABI_NAMESPACE ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/shared_ptr.h:296:37: note: in instantiation of function template specialization 'std::allocator_traits<std::allocator<Business>>::construct<Business, std::shared_ptr<MySqlDatabase> (&)(), void, void>' requested
最新发布
08-06
C++ 中,`std::allocator_traits` 提供了一组用于操作分配器的工具,包括构造、销毁对象等。如果在使用 `std::allocator_traits::construct_at` 时遇到编译错误 `no matching function for call to 'construct_at'`,通常是因为以下几种原因: ### 1. 缺少正确的构造函数或构造方式 `construct_at` 要求目标类型必须具有合适的构造函数来匹配传递的参数。如果尝试构造的对象没有与参数匹配的构造函数,或者参数类型无法隐式转换为目标构造函数的参数类型,则会导致编译错误。例如: ```cpp struct MyType { MyType(int x, double y); }; ``` 如果尝试使用 `construct_at` 构造该类型的对象,但提供的参数与构造函数匹配,就会引发错误: ```cpp MyType* ptr = ...; std::allocator_traits<std::allocator<MyType>>::construct_at(ptr, 10, 20); // 错误:第二个参数是 int,但期望 double ``` 应确保参数类型与构造函数的参数类型完全匹配[^1]。 ### 2. 分配器未正确定义或未启用 `construct_at` `construct_at` 是 C++20 引入的功能,如果使用的编译器或标准库支持 C++20 或更高版本,可能会导致该函数可用。确保编译器启用了 C++20 支持,例如在使用 `g++` 时添加 `-std=c++20` 选项。 此外,如果自定义分配器未正确实现 `construct_at` 的支持,也可能导致编译错误。标准库的 `std::allocator` 默认支持 `construct_at`,但如果使用了自定义分配器,则需要确保其符合 `Allocator` 概念的要求,并提供必要的构造支持。 ### 3. 使用方式正确 `construct_at` 的使用方式需要注意,它通常用于在已分配的内存中构造对象。例如: ```cpp MyType* ptr = std::allocator_traits<std::allocator<MyType>>::allocate(alloc, 1); std::allocator_traits<std::allocator<MyType>>::construct_at(ptr, 10, 3.14); // 正确:参数匹配构造函数 ``` 如果未正确分配内存或传递了错误的指针类型,也可能导致错误。 ### 解决方案示例 确保代码中使用了正确的构造函数和参数,并且编译器支持 C++20: ```cpp #include <memory> #include <iostream> struct MyType { MyType(int x, double y) { std::cout << "Constructed with x = " << x << ", y = " << y << std::endl; } }; int main() { std::allocator<MyType> alloc; MyType* ptr = std::allocator_traits<std::allocator<MyType>>::allocate(alloc, 1); std::allocator_traits<std::allocator<MyType>>::construct_at(ptr, 10, 3.14); // 正确:参数匹配构造函数 std::allocator_traits<std::allocator<MyType>>::destroy_at(ptr); std::allocator_traits<std::allocator<MyType>>::deallocate(alloc, ptr, 1); return 0; } ``` ### 相关问题 1. 如何在 C++ 中使用自定义分配器实现 `construct_at`? 2. `std::allocator_traits::construct_at` 和 `std::construct_at` 有什么区别? 3. 为什么 `construct_at` 需要 C++20 支持?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值