日志打印中对容器(包括多级容器)的通用输出

news2024/11/18 14:01:29

        在日志打印中,往往有打印一个数组、集合等容器中的每个元素的需求,这些容器甚至可能嵌套起来,如果每个地方都用for循环打印,将会特别麻烦。基于这种需求,作者尝试实现一个通用的打印函数SeqToStr(),将容器序列化。

通用打印函数

        首先这个函数需要接收这个容器arr,然后接收一个打印函数的回调func,形式如下:

template <typename Iterable, typename PrintFunc>
std::string SeqToStr(const Iterable& arr, PrintFunc&& func) {
    std::stringstream ss;
    ss << '[';
    for (auto&& i : arr) func(ss, i) << ",";
    auto ret_str = ss.str();
    if (ret_str.length() == 1) {
        return std::move(ret_str += ']');
    } else {
        ret_str.back() = ']';
        return std::move(ret_str);
    }
}

        每个元素被逗号隔开,所有元素被方括号框住。

一级容器

        当然,每次调用都写一个func也是很难用的,所以对于一级容器,可以写一个重载,用默认的打印函数:


inline std::string SeqToStr(const Iterable& arr) {
    return SeqToStr(
        arr, [](auto& ss, auto&& ele) -> decltype(auto) { return ss << ele; });
}
// p

        ss是一个支持流操作符<<(stringstream,basic_ostream等)的参数,返回值decltype(auto)防止<<返回的引用被当做值传回,实际上basic_ostream不支持复制,所以不显式指定返回值类型推导的话,编都编不过。

多级容器        

        然后对于多级容器,还需要一个递归的重载:

inline std::string SeqToStr(const Iterable& arr) {
    return SeqToStr(arr, [](auto&& ss, auto&& arr) -> decltype(auto) {
        return ss << SeqToStr(arr);
        });
}

        最后就是需要区分一级和多级容器这两种情况。

自动区分级别

C++14实现

        对于C++14,只能用SFINAE特性了,要知道容器里元素的类型,以及元素类型是否可直接打印。获得容器的元素类型,只需要获得std::begin(arr)的返回值类型即可,这样写避免某些自定义的容器不支持默认构造,或者没有begin()成员函数。

template <typename T>
struct ValueType {
  using type = std::decay_t<decltype(*std::begin(*static_cast<T*>(nullptr)))>;
};
template <typename T, std::size_t Size>
struct ValueType<T[Size]> {
  using type = T;
};
template <typename T>
using value_type_t = typename ValueType<T>::type;

        然后就是判断元素是否可以打印,这里以是否能被cout<<接收为标准,同时,考虑到原生数组可以以指针的形式被打印,所以判断的时候,需要排除这种情况:

template <typename T>
class Printable {
  template <typename U>
  static std::true_type test(decltype(std::cout << U{}, int{})*);
  template <typename U>
  static std::false_type test(...);

 public:
  static constexpr bool value =
      !std::is_array<T>::value &&
      std::is_same<decltype(test<T>(nullptr)), std::true_type>::value;
};

        最终,上面一级和多级的重载,可以加上条件模板了:

// print an iterable struct(vector<int>, set<int>, int[3], array<int,3>, etc)
template <typename Iterable,
          std::enable_if_t<Printable<value_type_t<Iterable>>::value, int> = 0>
std::string SeqToStr(const Iterable& arr) {
  return SeqToStr(
      arr, [](auto& ss, auto&& ele) -> decltype(auto) { return ss << ele; });
}
// print an multi-dimensional struct(int[3][3], set<vector<set<int>>>, etc)
template <typename Iterable,
          std::enable_if_t<!Printable<value_type_t<Iterable>>::value, int> = 0>
std::string SeqToStr(const Iterable& arr) {
  return SeqToStr(arr, [](auto&& ss, auto&& arr) -> decltype(auto) {
    return ss << SeqToStr(arr);
  });
}

C++20实现

        对于C++20,可以用concept来代替上面的Printable、一级和多级容器的重载,大意一样,但更为简洁:

template <typename Iterable>
std::string SeqToStr(const Iterable& arr) {
    using ele_type = value_type_t<Iterable>;
    if constexpr (requires{std::cout << ele_type{}; } && 
                  !std::is_array_v<ele_type>) {
        return SeqToStr(
            arr, [](auto& ss, auto&& ele) -> decltype(auto) { return ss << ele; });
    } else {
        return SeqToStr(arr, [](auto&& ss, auto&& arr) -> decltype(auto) {
            return ss << SeqToStr(arr);
            });
    }

}

测试代码

#include <string>
#include <sstream>
#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <unordered_map>
#include <array>

int main() {
  using std::cout, std::endl;
  std::initializer_list<int> list{1, 2, 3, 4, 5};
  int arr[][2]{{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}};
  std::vector<int> vec{2, 4, 6, 8, 0};
  std::array<std::vector<int>, 2> arr_vec{std::vector<int>{1, 2, 3, 4, 5},
                                          std::vector<int>{11, 22, 33, 44, 55}};
  std::set<int> set{1, 3, 5, 7};
  std::unordered_map<int, char> umap{{1, 'a'}, {2, 'b'}, {3, 'c'}};
  cout << SeqToStr(std::initializer_list{1, 2, 3, 4, 6}) << endl;
  cout << SeqToStr(list) << endl;
  cout << SeqToStr(arr) << endl;
  cout << SeqToStr(vec) << endl;
  cout << SeqToStr(arr_vec) << endl;
  cout << SeqToStr(set) << endl;
  cout << SeqToStr(umap, [](auto&& ss, auto&& ele) -> decltype(auto) {
    return ss << "{key=" << ele.first << ",value=" << ele.second << '}';
  }) << endl;
}

   期望输出为    

[1,2,3,4,6]
[1,2,3,4,5]
[[1,2],[2,3],[3,4],[4,5],[5,6]]
[2,4,6,8,0]
[[1,2,3,4,5],[11,22,33,44,55]]
[1,3,5,7]
[{key=1,value=a},{key=2,value=b},{key=3,value=c}]

、如果包含了下面这篇文档对bitset遍历的实现,用基于范围的for循环遍历bitset的所有有效位置_bitset 遍历-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/sinat_39088557/article/details/116431911        还可以这么用:

#include <bitset>
int main() {
    using std::cout, std::endl;
    std::bitset<65537> bs{ 0xc };
    bs.set(63);
    bs.set(64);
    bs.set(65);
    bs.set(264);
    bs.set(364);
    bs.set(664);
    bs.set(6663);
    bs.set(64645);
    bs.set(65536);

    cout << SeqToStr(BitsetRange(std::bitset<65537>(0xfabcd0))) << endl;
    cout << SeqToStr(BitsetRange(bs)) << endl;

    auto bs1 = bs;
    bs1.set(7);
    bs1.set(63, false);
    cout << SeqToStr(std::vector{BitsetRange(bs), BitsetRange(bs1)});

}

期望输出为:

[4,6,7,10,11,12,13,15,17,19,20,21,22,23]
[2,3,63,64,65,264,364,664,6663,64645,65536]
[[2,3,63,64,65,264,364,664,6663,64645,65536],[2,3,7,64,65,264,364,664,6663,64645,65536]]

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

47岁TVB儿童节目主持20多年美貌零走样

现年47岁的香港艺人张洁莲&#xff08;Jackeline姐姐&#xff09;自从2020年嫁给拍拖多年的TVB「不老型男」袁文杰后&#xff0c;不时都在社交平台分享合照&#xff0c;夫妻间甜蜜恩爱&#xff0c;相信正是她多年来的保养秘诀。 最近有网友在社交平台分享了与张洁莲的合照&…

docker配置redis主从复制

下载redis,复制redis.conf 主节点(6379) 修改redis.conf # bind 127.0.0.1 # 注释掉这里 protected-mode no # 改为no port 6379从节点(6380) 修改redis.conf bind 127.0.0.1 protected-mode no # 改为no port 6380 replicaof 172.17.0.2 6379 # 这里的ip为主节点容器的i…

CS与MSF的权限互相传递/mimikatz抓取windows 2012明文密码

目录 CS和MSF的简单介绍 Metasploit Cobalt Strike 1、CS权限传递到MSF 2、MSF权限传递到CS 3、使用mimikatz抓取明文密码 通过修改注册表用户重新登录后抓取明文密码 今天的任务是两个 一个是CS与MSF的权限互相传递一个是抓取windows2012的明文密码 那就分别来完成 …

结构冒险,控制冒险,数据冒险实例分析

目录 1.结构冒险&#xff1a; 2.数据冒险&#xff1a; 3.控制冒险&#xff1a; 指令执行过程&#xff1a; 取指(IF)&#xff1a;从指令存储器或 Cache 中取指令。 译码/读寄存器(ID)&#xff1a;操作控制器对指令进行译码&#xff0c;同时从寄存器堆中取操作数。 执行/计算地…

文字实录|Checkout.com大中华区总经理项尧:品牌全球化发展中的支付运营策略

大家好&#xff0c;很高兴在此次【品牌全球化营销增长峰会】与大家一起分享和交流。 我叫项尧&#xff0c;是 Checkout.com 大中华区的总经理&#xff0c;在支付领域有将近15年的经验。 我们 Checkout.com 是一家总部位于英国的支付公司&#xff0c;专注于线上收单&#xff0…

深入理解组合模式(Composite Pattern)及其实际应用

引言 在软件开发中&#xff0c;我们经常会遇到树形结构的数据&#xff0c;这种结构包含了简单和复杂的对象。组合模式&#xff08;Composite Pattern&#xff09;通过将对象组织成树形结构来表示部分和整体的层次关系&#xff0c;使得客户端对单个对象和组合对象的使用具有一致…

OpenCV 张氏标定法

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 “张正友标定法”是由张正友教授于1998年提出的单平面棋盘格的摄像头标定方法,该方法介于传统标定法和自标定法之间,克服了传统标定法需要高精度标定物的缺点,仅需要一个棋盘格即可。作为一种非常经典的相机内参标定…

Redis发布、订阅模式(Pub/Sub)详解

Redis发布、订阅模式&#xff08;PUB-SUB&#xff09;详解 Redis的发布订阅&#xff08;Pub/Sub&#xff09;机制是一种消息通信模式&#xff0c;用于消息的广播。它允许多个客户端订阅&#xff08;Subscribe&#xff09;特定的频道&#xff08;Channel&#xff09;&#xff0c…

机器人控制系列教程之动力学建模(2)

接昨天的推文&#xff1a;https://editor.csdn.net/md/?articleId139991958 &#xff0c;动力学的求解通常是个相对比较复杂的过程&#xff0c;但现在基本上不用人工来推算求解各种公式和求解过程了&#xff0c;大家只需要知道其中的步骤即可&#xff0c;现代对于动力学问题的…

51单片机STC8H8K64U通过RA8889/RA8876如何控制彩屏(源码下载)

【硬件部份】 一、硬件连接实物&#xff1a; STC8H系列单片机不需要外部晶振和外部复位&#xff0c;在相同的工作频率下&#xff0c;速度比传统的8051单片机要快12倍&#xff0c;具有高可靠抗干扰的优秀特性&#xff0c;与瑞佑的RA8889/RA8876控制芯片刚好可以完美搭配用于工…

java易错题型(复习必看)

java易错题型&#xff1a; 下列符号中&#xff0c;哪个用于分隔throws关键字抛出的多个异常 逗号&#xff0c; Java中用来声明一个方法可能抛出某种异常的关键字是throw 对于catch子句的排列&#xff0c;下列哪种是正确的&#xff1a;子类异常在先&#xff0c;父类异常在后&a…

鸿蒙NEXT开发知识:工具常用命令—ohpm config

设置ohpm用户级配置项。 命令格式 ohpm config set <key> <value> ohpm config get <key> ohpm config delete <key> ohpm config list 说明 配置文件中信息以键值对<key> <value>形式存在。 功能描述 ohpm 从命令行和 .ohpmrc 文件中…

Android SurfaceFlinger——动画进程销毁(十七)

在动画播放完成后&#xff0c;对动画相关资源释放的同时还需要销毁动画进程。这里我们就来分析一下动画进程的销毁流程。 一、动画进程销毁 动画进程的销毁一般是在桌面进程准备显示的时候&#xff0c;而桌面准备显示是在桌面 Activity 的 Resume 生命周期&#xff0c;我们来看…

江科大—读写内部闪存FLASH读取芯片ID

读写内部闪存FLASH 右下角是OLED&#xff0c;然后左上角在PB1和PB11两个引脚&#xff0c;插上两个按键用于控制。下一个代码读取芯片ID&#xff0c;这个也是接上一个OLED&#xff0c;能显示测试数据就可以了。 STM32-STLINK Utility 本节的代码调试&#xff0c;使用辅助软件…

Sparse4Dv2

Sparse4D: Multi-view 3D Object Detection with Sparse Spatial-Temporal Fusion 相关内容&#xff1a;总览&#xff0c;Sparse4D v1&#xff0c;Sparse4D v3&#xff0c; 单位&#xff1a;地平线(Sparse4D v1 原班人马) GitHub&#xff1a;https://github.com/HorizonRobo…

计算机网络之数据通信原理(中)

上节内容传送口&#xff1a;数据通信原理基础 1.数据传输方式 1.1并行传输 并行传输: 字符编码的各个比特同时传输 特点&#xff1a; 一个比特时间内可传输一个字符&#xff0c;传输速度快&#xff0c;每个比特传输要求一个单独的信道支持&#xff0c;通信成本高&#xf…

基于单片机和 Arduino 平台的六自由度可控机械手臂

摘 要 : 为了降低机械手臂的设计开发难度 &#xff0c; 并使之尽早地投入应用 &#xff0c; 设计一种基于单片机和 Arduino 平台的六自由度可控机械手臂 。提出六自由度可控机械手臂的控制方案&#xff0c; 给出机械手臂控制系统的结构框图 。 详细设计六自由度可控机械手臂…

《UDS协议从入门到精通》系列——图解0x35:请求上传

《UDS协议从入门到精通》系列——图解0x35&#xff1a;请求上传 一、简介二、数据包格式2.1 服务请求格式2.2 服务响应格式2.2.1 肯定响应2.2.2 否定响应 三、通信示例 Tip&#x1f4cc;&#xff1a;本文描述中但凡涉及到其他UDS服务的&#xff0c;将陆续提供链接跳转方式以便快…

Power BI可视化表格矩阵如何保持样式导出数据?

故事背景&#xff1a; 有朋友留言询问&#xff1a;自己从Power BI可视化矩阵表格中导出数据时&#xff0c;导出的表格样式会发生改变&#xff0c;需要线下再手动调整&#xff0c;重新进行透视组合成自己想要的格式。 有没有什么办法让表格导出来跟可视化一样&#xff1f; Po…

pd虚拟机 Parallels Desktop 19 for Mac 破解版小白安装使用指南

Parallels Desktop 19 for Mac 乃是一款适配于 Mac 的虚拟化软件。它能让您在 Mac 计算机上同时运行多个操作系统。您可借此创建虚拟机&#xff0c;并于其中装设不同的操作系统&#xff0c;如 Windows、Linux 或 macOS。使用 Parallels Desktop 19 mac 版时&#xff0c;您可在 …