基于宏,实现C/C++更高效的输入input和更高效的输出print、println

news/2025/3/31 16:14:39/文章来源:https://www.cnblogs.com/tsunchiwong/p/18798642

Deepseek实现了本方法代码,作者只是自底向上一步步解释实现方式。若您只想获取本方法,而不想了解具体实现,也可阅读本文前面三节。

潜在问题

  1. 本方法中使用了名称 print,可能会与 C++ 23 标准中的 std::print 名称冲突。
  2. println中最终的换行用的是 '\n' 而不是 endl,若不用 endl 或其他方式刷新缓冲区,可能无法看到输出结果。可以把 '\n' 改成 std::endl 解决这一问题。

概览

方法实现具体代码在本节文末,本节简要介绍更简便的输入输出的具体表现。

input()

input(val1, val2, val3) 会展开为 std::cin >> val1 >> val2 >> val3,最多支持 20 个参数。

print()

print(val1, val2, val3) 会展开为 std::cout << val1 << val2 << val3,最多支持 20 个参数。

println()

println(val1, val2, val3) 会展开为 std::cout << val1 << ' ' << val2 << ' ' << val3 << ' ' << '\n',最多支持 20 个参数。

修改参数数量方法

若您想支持更多参数,请同时修改第四行与第三行两行有关 NUM 的代码,并增加 _FOREACH_ 的数量。下面假设我要修改为 25。

_NUM

增加数字到 24。注意是 24,而不是25。如下

#define _NUM(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, N, ...) N

NUM

增另数字到25。注意是从左往右递减,如下

#define NUM(...) EVAL(_NUM(__VA_ARGS__, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))

_FOREACH_

增加更多数量,直到25,每一个 _FOREACH_ 都调用前一个,如下

#define _FOREACH_21(m, x, ...) m(x) EVAL(_FOREACH_20(m, __VA_ARGS__))
#define _FOREACH_22(m, x, ...) m(x) EVAL(_FOREACH_21(m, __VA_ARGS__))
#define _FOREACH_23(m, x, ...) m(x) EVAL(_FOREACH_22(m, __VA_ARGS__))
#define _FOREACH_24(m, x, ...) m(x) EVAL(_FOREACH_23(m, __VA_ARGS__))
#define _FOREACH_25(m, x, ...) m(x) EVAL(_FOREACH_24(m, __VA_ARGS__))

具体代码

#define CONCAT(a, b) a##b
#define EVAL(...) __VA_ARGS__
#define _NUM(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, N, ...) N
#define NUM(...) EVAL(_NUM(__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))#define _FOREACH_1(m, x) m(x)
#define _FOREACH_2(m, x, ...) m(x) EVAL(_FOREACH_1(m, __VA_ARGS__))
#define _FOREACH_3(m, x, ...) m(x) EVAL(_FOREACH_2(m, __VA_ARGS__))
#define _FOREACH_4(m, x, ...) m(x) EVAL(_FOREACH_3(m, __VA_ARGS__))
#define _FOREACH_5(m, x, ...) m(x) EVAL(_FOREACH_4(m, __VA_ARGS__))
#define _FOREACH_6(m, x, ...) m(x) EVAL(_FOREACH_5(m, __VA_ARGS__))
#define _FOREACH_7(m, x, ...) m(x) EVAL(_FOREACH_6(m, __VA_ARGS__))
#define _FOREACH_8(m, x, ...) m(x) EVAL(_FOREACH_7(m, __VA_ARGS__))
#define _FOREACH_9(m, x, ...) m(x) EVAL(_FOREACH_8(m, __VA_ARGS__))
#define _FOREACH_10(m, x, ...) m(x) EVAL(_FOREACH_9(m, __VA_ARGS__))
#define _FOREACH_11(m, x, ...) m(x) EVAL(_FOREACH_10(m, __VA_ARGS__))
#define _FOREACH_12(m, x, ...) m(x) EVAL(_FOREACH_11(m, __VA_ARGS__))
#define _FOREACH_13(m, x, ...) m(x) EVAL(_FOREACH_12(m, __VA_ARGS__))
#define _FOREACH_14(m, x, ...) m(x) EVAL(_FOREACH_13(m, __VA_ARGS__))
#define _FOREACH_15(m, x, ...) m(x) EVAL(_FOREACH_14(m, __VA_ARGS__))
#define _FOREACH_16(m, x, ...) m(x) EVAL(_FOREACH_15(m, __VA_ARGS__))
#define _FOREACH_17(m, x, ...) m(x) EVAL(_FOREACH_16(m, __VA_ARGS__))
#define _FOREACH_18(m, x, ...) m(x) EVAL(_FOREACH_17(m, __VA_ARGS__))
#define _FOREACH_19(m, x, ...) m(x) EVAL(_FOREACH_18(m, __VA_ARGS__))
#define _FOREACH_20(m, x, ...) m(x) EVAL(_FOREACH_19(m, __VA_ARGS__))#define _FOREACH_N(N, m, ...) CONCAT(_FOREACH_, N)(m, __VA_ARGS__)
#define FOR_EACH(m, ...) EVAL(_FOREACH_N(NUM(__VA_ARGS__), m, __VA_ARGS__))#define COUT_STREAM(x) << x
#define print(...) std::cout FOR_EACH(COUT_STREAM, __VA_ARGS__)
#define CIN_STREAM(x) >> x
#define input(...) std::cin FOR_EACH(CIN_STREAM, __VA_ARGS__)
#define COUT_STREAM_2(x) << x << ' '
#define println(...) std::cout FOR_EACH(COUT_STREAM_2, __VA_ARGS__) << '\n'

接下来开始自底向上,逐步分析整个宏的实现。

CONCAT

简单地连接两个元素

EVAL

EVAL(...) 的直接作用在于把传入的内容展开,就是怎么拿来的,怎么摆出来。在一般情况下,用 EVAL 和不用 EVAL 似乎没有区别,但用 EVAL 能保证你的内容中有宏时,会先展开内部的宏,而不是错误地不展开。下面给个例子

#define A(x) a##x
#define B(x) A(x)
#define EVAL(...) __VA_ARGS__B(x) // 有可能展成A(x)而不是进一步展开成ax,为什么是“有可能”请见后文
EVAL(x) // 先展开内部的B变成EVAL(A(x)),由于有EVAL的存在,会继续展开内部的A变成EVAL(ax),最后变为ax

虽然理论上如此,但本人知识有限,没有测出过错误展开的情况。可能在更低版本的编译器会有错误的情况。

NUM与_NUM

通过NUM可以直接测出传给宏的参数个数,_NUM是NUM的辅助,但这个测试有数量上限,NUM中最大的数字,此处即20,就是这个上限。但可以人为增加它的上限。

先看NUM,当参传入NUM时,会把参数和一群数字进一步传给内置NUM,注意到此处数字是倒着递减到0。

我们再观察_NUM的传入参数形式,先给了比最大数少1的参数位置,再是个N,再是个变长参数。而后面展开的内容只需要N,说明前面那些数字参数,和变长参数,都是辅助作用。

这里的原理有点像游标卡尺,传给NUM有多少个参数,NUM会贴一个「标尺」,或者说「尺子」,在那具体参数的末尾,这时,传入_NUM的整体,就由「原具体参数+标尺」构成。然后N会卡住贴上的标尺中的某个数,这个数就是原具体参数的数量。原具体参数越多,标尺被卡住的位置就越靠前,所以标尺前面的数理应越大。

这里要注意,不要忘了会有0个具体参数的情况。

FOREACH

_FOREACH_[NUMBER]

先来看带数字的_FOREACH_[NUMBER]。

先看_FOREACH_1,它需要两个参数m和x,m是某个函数,x是要给m传入的参数,所以它的意思就是,给一个函数、它要的参数(只要一个参数),然后把函数表达式展出,或者说把它变成能用的函数调用写法,把函数用出来。

再看其他的,对于更高阶的,观察获取参数的列表,首先会截取一个目标函数m,再截取一个参数x,后面的是更低阶_FOREACH要用的参数。它先展开 m(x),然后空一格,再把后面那些参数,以及函数m,交给后面更低阶的展开。

这样,就把_FOREACH_[NUMBER](m, x1, x2, x3, ..., x_number) 展开成了 m(x1) m(x2) m(x3) ... m(x_number),注意中间有空格分隔。

_FOREACH_N

接收的参数,相比_FOREACH_[NUMBER],在最前面多出了个N。后面使用拼接。拼接了两个符号:_FOREACH_ 和N。容易看出,这个N,就指明了我们应该调用的第一个_FOREACH_[NUMBER],其实也是代表了参数m后面,要被应用到m中的参数有几个。

FOR_EACH

它只接收m,和需要被应用到m的参数,而不接收数量。所以在后面,它调用了NUM测量了参数的个数,把它作为_FOREACH_N中的N,再接上m和参数,传给_FOREACH_N。

总结

把一个函数,和若干个参数给FOR_EACH,然后把这若干个参数,放进m,并展开,在空间加入了空格。

COUT_STREAM、CIN_STREAM、COUT_STREAM_2

这几个效果都差不多,这里以较复杂的COUT_STREAM_2为例。

若一个参数x传入了该函数,则它会被展开成 << x << ' '

print、input、println

这里我们还是以最为复杂的println为例。

前面说过,COUNT_STREAM_2只接收一个参数,但我们之前拥有了能展开多个参数到同一个函数中的工具FOR_EACH,所以我们能想到这样的应用:FOR_EACH(COUT_STREAM_2, x1, x2, x3, ..., xn) 展开成 COUT_STREAM_2(x1) COUT_STREAM_2(x2) COUT_STREAM_2(x3) ... COUT_STREAM_2(xn) 进而展开成 << x1 << ' ' << x2 << ' ' << x3 << ' ' ... << xn << ' '。在此基础上,在头加上 std::cout,在尾加上 << '\n',就能完成完整的cout输出表达了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/907264.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

瑞芯微RK356X主板复用接口配置方法,触觉智能嵌入式方案商

本文介绍瑞芯微RK356X系列复用接口配置的方法,基于触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。 复用接口介绍 由下图可知,红圈内容当前引脚可配置为SPI0…

团队项目第二周作业

需求规格说明书 一、面向用户分析 网上点餐系统主要面向以下用户群体: 普通消费者:包括年轻人、上班族、学生等,他们希望通过便捷的方式快速点餐。 餐厅经营者:需要通过系统管理菜品、订单、顾客信息等,以提高运营效率。 外卖配送人员:负责将订单配送到消费者手中,系统需…

解惑:采购时亚克力板尺寸一般有多少?-郑州亚克力制品代加工-郑州水晶字logo代加工-亚克力切割雕刻-外协加工-委外加工-激光代加工-河南郑州-芯晨微纳(河南)

亚克力板的常规尺寸因生产厂家、用途和工艺(如挤出板或浇铸板)而有所不同,以下是常见的规格参考:厚度范围挤出板:通常为 1mm–10mm,部分厂家可生产更厚(如12mm、15mm)。 浇铸板:厚度范围更广,常见 1mm–50mm,特殊需求可定制更厚板材。常见标准厚度(单位:mm): 1、…

微服务引擎 MSE 及云原生 API 网关 2025 年 2 月产品动态

微服务引擎 MSE 及云原生 API 网关 2025 年 2 月产品动态

性能测试的基本理论

一、性能测试介绍 1、什么叫做性能测试?(1)通过某些工具或手段来检测软件的某些指标是否达到了要求,这就是性能测试 (2)指通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试2、性能测试的时间?在功能测试完成后才能进行性能测试3、…

服务器备份资料,怎么给服务器备份资料

在数字化时代,服务器承载着企业大量的关键数据,从客户信息、业务文档到重要的应用程序和数据库,这些数据是企业运营和发展的核心资产。一旦数据丢失或损坏,可能会给企业带来严重的经济损失和业务中断风险。因此,给服务器备份资料成为了保障数据安全的关键举措。以下将详细…

免去繁琐的手动埋点,Gin 框架可观测性最佳实践

本文将着重介绍 Gin 框架官方推荐的几种可观测性方案并进行对比,从而得出 Gin 框架可观测性的最佳实践。作者:牧思 背景 在云原生时代的今天,Golang 编程语言越来越成为开发者们的首选,而对于 Golang 开发者来说,最著名的 Golang Web 框架莫过于 Gin [ 1] 框架了,Gin 框架…

深入浅出WPF命令系统之InputBinding(MouseBinding与KeyBinding)

之前的随笔中剖析了WPF命令系统的一部分,文中是通过CommandBinding或Binding两种方式将命令(ICommand)与Button控件关联,如下: 方式一,CommandBinding1 <Window.Resources>2 3 <RoutedCommand x:Key="MyTestCommand"/>4 5 </Window.Resou…

20241217 实验二《Python程序设计》实验报告

20241217 2024-2025-2 《Python程序设计》实验二报告 课程:《Python程序设计》 班级: 2412 姓名: 黄迅 学号:20241217 实验教师:王志强 实验日期:2025年3月26日 必修/选修: 公选课 一、实验内容 1.设计并完成一个完整的应用程序,完成加减乘除模等运算,功能多多益善; …

教师教学质量分析评价系统 A教育大模型

教师教学质量分析评价系统基于YOLOv12+RNN的深度学习算法,教师教学质量分析评价系统精准地检测到学生是否在玩手机、举手、睡觉、交头接耳、趴桌子、行走运动等行为。同时,该模型还能够捕捉学生的情绪表情,如开心、厌恶、愤怒、悲伤、沮丧、恐惧、无表情等。这些行为和表情数…