any實現(基于LLVM中libcxx實現分析)

??本文根據LLVM中libcxx的實現,分析了std::any和std::variant的具體實現。

1 簡介

??在 C++17 標準中,std::any提供了一種類型安全的方式來存儲任意類型的值。它使用類型擦除(type erasure)技術實現,使得一個對象可以包含任何類型的值而不需要提前知道該類型。std::any的使用比較簡單。

#include <any>
#include <iostream>int main()
{std::cout << std::boolalpha;// any typestd::any a = 1;std::cout << a.type().name() << ": " << std::any_cast<int>(a) << '\n';a = 3.14;std::cout << a.type().name() << ": " << std::any_cast<double>(a) << '\n';a = true;std::cout << a.type().name() << ": " << std::any_cast<bool>(a) << '\n';// bad casttry{a = 1;std::cout << std::any_cast<float>(a) << '\n';}catch (const std::bad_any_cast& e){std::cout << e.what() << '\n';}// has valuea = 2;if (a.has_value())std::cout << a.type().name() << ": " << std::any_cast<int>(a) << '\n';// reseta.reset();if (!a.has_value())std::cout << "no value\n";// pointer to contained dataa = 3;int* i = std::any_cast<int>(&a);std::cout << *i << '\n';
}

2 實現

內存處理
??std::any的實現簡單的說來就是通過一個指針存儲數據和一個額外的類型信息來保留類型。

class any{union _Storage {_LIBCPP_HIDE_FROM_ABI constexpr _Storage() : __ptr(nullptr) {}void* __ptr;__any_imp::_Buffer __buf;};_HandleFuncPtr __h_ = nullptr;_Storage __s_;
};

??__h_存儲不同情況對應處理的函數指針信息,__s_存儲具體的內存,根據不同情況使用不同的存儲方式。和常規的SSO優化類似小于某個大小的內存直接存儲在棧上,否則用堆。這里的大小是對齊到3倍機器字長。

using _Buffer = aligned_storage_t<3 * sizeof(void*), alignof(void*)>;

??不同大小的對象使用不同的handler對數據進行操作,棧內存使用SmallHandle,堆內存使用LargeHandle,二者唯一的區別只是操作的內存不同,一個操作ptr,一個操作buf

template <class _Tp>
struct _LIBCPP_TEMPLATE_VIS _SmallHandler {_LIBCPP_HIDE_FROM_ABI static void*__handle(_Action __act, any const* __this, any* __other, type_info const* __info, const void* __fallback_info) {switch (__act) {case _Action::_Destroy:__destroy(const_cast<any&>(*__this));return nullptr;case _Action::_Copy:__copy(*__this, *__other);return nullptr;case _Action::_Move:__move(const_cast<any&>(*__this), *__other);return nullptr;case _Action::_Get:return __get(const_cast<any&>(*__this), __info, __fallback_info);case _Action::_TypeInfo:return __type_info();}__libcpp_unreachable();}private:_LIBCPP_HIDE_FROM_ABI static void __destroy(any& __this) {typedef allocator<_Tp> _Alloc;typedef allocator_traits<_Alloc> _ATraits;_Alloc __a;_Tp* __p = static_cast<_Tp*>(static_cast<void*>(&__this.__s_.__buf));_ATraits::destroy(__a, __p);__this.__h_ = nullptr;}
};
//而對應的largeHandle的銷毀不僅僅要釋放對象還需要銷毀內存
template <class _Tp>
struct _LIBCPP_TEMPLATE_VIS _LargeHandler {
_LIBCPP_HIDE_FROM_ABI static void __destroy(any& __this) {typedef allocator<_Tp> _Alloc;typedef allocator_traits<_Alloc> _ATraits;_Alloc __a;_Tp* __p = static_cast<_Tp*>(__this.__s_.__ptr);_ATraits::destroy(__a, __p);_ATraits::deallocate(__a, __p, 1);__this.__h_ = nullptr;}
};

??具體使用哪種類型的handle則是在構造時根據類型確定的,_IsSmallObject用來判斷是否是小對象,小對象則使用_SmallHandler,否則使用_LargeHandler

template <class _Tp>
using _IsSmallObject =integral_constant<bool,sizeof(_Tp) <= sizeof(_Buffer) && alignof(_Buffer) % alignof(_Tp) == 0 &&is_nothrow_move_constructible<_Tp>::value >;template <class _Tp>
using _Handler = conditional_t< _IsSmallObject<_Tp>::value, _SmallHandler<_Tp>, _LargeHandler<_Tp>>;template <class _ValueType, class _Tp, class>
any::any(_ValueType&& __v) : __h_(nullptr) {__any_imp::_Handler<_Tp>::__create(*this, std::forward<_ValueType>(__v));
}

類型信息
??對于開啟了RTTI的場景,比較簡單直接返回當前對象的typeinfo即可。

  _LIBCPP_HIDE_FROM_ABI static void* __type_info() {
#  if !defined(_LIBCPP_HAS_NO_RTTI)return const_cast<void*>(static_cast<void const*>(&typeid(_Tp)));
#  elsereturn nullptr;
#  endif}

3 自己實現

??說實話LLVM的實現感覺很丑,這里通過繼承來實現類型擦除會好看很多(實現其實不全,拷貝構造等都沒有實現,也沒有實現類型decay的情況,但是基本功能是OK的)。

#include <iostream>
#include <exception>
#include <type_traits>
#include <memory>
#include <utility>
class AnyCastError : public std::exception {
public:const char* what() const noexcept override {return "Bad cast in Any";}
};template<class T>
class AnyImpl {
public:using Buffer = std::aligned_storage_t<3 * sizeof(void*), alignof(void*)>;using IsSmallTrivialObject = std::integral_constant<bool, sizeof(T) <= sizeof(Buffer)&&alignof(Buffer) % alignof(T) == 0 &&std::is_nothrow_move_constructible<T>::value >;public:union Storage {void* ptr;Buffer buffer;Storage() : ptr(nullptr) {}~Storage() {}};
};struct HolderBase {
public:virtual ~HolderBase() = default;virtual std::unique_ptr<HolderBase> clone() const = 0;virtual const std::type_info *typeInfo() const = 0;
};template<class T>
struct Holder : public HolderBase {
public:Holder(T&& value) {if constexpr (AnyImpl<T>::IsSmallTrivialObject::value) {new(&_storage.buffer)T(std::move(value));}else {_storage.ptr = new T(std::move(value));}}~Holder() {if constexpr (AnyImpl<T>::IsSmallTrivialObject::value) {reinterpret_cast<T*>(&_storage.buffer)->~T();}else {delete static_cast<T*>(_storage.ptr);}}virtual std::unique_ptr<HolderBase> clone() const override {return std::make_unique<Holder<T>>(getValue());}T getValue() const {if constexpr (AnyImpl<T>::IsSmallTrivialObject::value) {return *reinterpret_cast<const T*>(&_storage.buffer);}else {return *static_cast<T*>(_storage.ptr);}}virtual const std::type_info* typeInfo() const override {return &typeid(T);}public:AnyImpl<T>::Storage _storage;
};class Any {
public:Any() {_holder = nullptr;}template<class T>Any(T&& v) {_holder = std::make_unique<Holder<T>>(std::forward<T>(v));}bool hasValue() const {return !!_holder;}template<class T>T getValue() {return hasValue() ? static_cast<Holder<T>*>(_holder.get())->getValue() : T();}const std::type_info* typeInfo() const {return hasValue() ? _holder->typeInfo() : &typeid(int);}
private:std::unique_ptr<HolderBase> _holder{};
};int main(int argc, char **argv){try {Any a = 42; // 存儲 intstd::cout << "Value: " << a.getValue<int>() << ", Type: " << a.typeInfo()->name() << std::endl;Any b = std::string("Hello"); // 存儲 stringstd::cout << "Value: " << b.getValue<std::string>() << ", Type: " << b.typeInfo()->name() << std::endl;// 測試未存儲值的情況Any emptyAny;std::cout << "Has Value: " << emptyAny.hasValue() << std::endl;std::cout << "Type Info: " << emptyAny.typeInfo()->name() << std::endl;}catch (const AnyCastError& e) {std::cerr << e.what() << std::endl;}return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/88015.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/88015.shtml
英文地址,請注明出處:http://en.pswp.cn/web/88015.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網安系列【13】之滲透測試:前期信息收集

文章目錄 前期信息收集信息收集的分類信息收集的內容域名信息收集Whois備案信息whois反查SSL證書查詢域名收集工具IP收集CDN信息收集CDN判斷CDN繞過 端口信息收集常見端口介紹FTP-21SSH-22WWW-80NetBlOSSessionService-139/445MySQL-3306RDP-3389Redis-6379Tomcat-8080 端口掃描…

自動駕駛傳感器的標定與數據融合

目錄 IMU的標定 相機的標定 激光雷達和組合慣導標定 相機和激光雷達標定 傳感器數據融合 多傳感器融合數據處理 傳感器數據融合算法 環境感知與預測 應用實例——車道線識別 應用實例——車輛行人識別 應用實例——交通標志識別 定位系統 基于慣性導航儀的定位技術…

27.移除元素(快慢指針)

給你一個數組 nums 和一個值 val&#xff0c;你需要 原地 移除所有數值等于 val 的元素。元素的順序可能發生改變。然后返回 nums 中與 val 不同的元素的數量。 假設 nums 中不等于 val 的元素數量為 k&#xff0c;要通過此題&#xff0c;您需要執行以下操作&#xff1a; 更改…

Spring AI:ETL Pipeline

提取、轉換和加載&#xff08;ETL&#xff09;框架是檢索增強生成&#xff08;RAG&#xff09;用例中數據處理的支柱。ETL管道協調從原始數據源到結構化向量存儲的流程&#xff0c;確保數據以最佳格式供AI模型檢索。RAG用例是文本&#xff0c;通過從數據體中檢索相關信息來增強…

26.安卓逆向2-frida hook技術-解密響應

免責聲明&#xff1a;內容僅供學習參考&#xff0c;請合法利用知識&#xff0c;禁止進行違法犯罪活動&#xff01; 內容參考于&#xff1a;圖靈Python學院 工具下載&#xff1a; 鏈接&#xff1a;https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwdzy89 提取碼&#xff1…

人工智能與人工智障———仙盟創夢IDE

<!-- 桌面導航 -->&#x3C;nav class&#x22;hidden md:flex items-center space-x-8&#x22;&#x3E;&#x3C;a href&#x22;#home&#x22; class&#x22;nav-link text-gray-700 hover:text-primary font-medium&#x22;&#x3E;&#x9996;&…

車載通信架構 --- 以太網相關網絡安全

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 做到欲望極簡,了解自己的真實欲望,不受外在潮流的影響,不盲從,不跟風。把自己的精力全部用在自己。一是去掉多余,凡事找規律,基礎是誠信;二是…

行業實踐案例:金融行業數據治理體系全景解析

“金融行業是數據治理的試金石。” ——高密度數據、高合規要求、高業務依賴,決定了金融治理的復雜度和先進性。 ?? 本文目錄 為什么金融行業對數據治理要求高? 金融行業數據治理的獨特挑戰 金融行業治理框架搭建實踐 典型治理能力案例詳解 工具與平臺選型經驗 總結與啟示 …

C#讀取modbus值,C#讀寫modbus,支持讀寫uint32值,Modbus TCP工具類

C#讀取modbus值&#xff0c;C#讀寫modbus&#xff0c;支持讀寫uint32值&#xff1b;Modbus TCP工具類 需要首先安裝nuget包Modbus using Modbus.Device; using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; us…

Oracle注釋詳解

在Oracle SQL中&#xff0c;注釋是用于解釋代碼邏輯、提高可讀性的文本&#xff0c;不會被數據庫執行。Oracle支持兩種類型的注釋語法&#xff1a; 1. 單行注釋&#xff08;--&#xff09; 使用雙連字符--在一行中添加注釋&#xff0c;從--開始到行末的所有內容都會被視為注釋。…

關于 scrapy框架 詳解

scrapy 是一個純 Python 編寫的異步爬蟲框架&#xff0c;具備以下特點&#xff1a;優勢說明異步高效基于 Twisted&#xff0c;非阻塞 IO模塊化各部分可靈活配置/替換中間件機制支持代理、UA、cookie 控制等強大的解析內置 XPath、CSS 提取器自動去重Scheduler 內部維護請求 fin…

DHCP中繼實驗及其核心原理

DHCP 中繼&#xff08;DHCP Relay&#xff09;是一種允許跨網段分配 IP 地址的技術&#xff0c;無需在每個子網部署 DHCP 服務器。以下是其原理和配置方法的詳細說明&#xff1a;一、核心原理1. 為什么需要 DHCP 中繼&#xff1f;問題&#xff1a;DHCP 客戶端通過廣播&#xff…

ABP VNext + RediSearch:微服務級全文檢索

ABP VNext RediSearch&#xff1a;微服務級全文檢索 &#x1f680; &#x1f4da; 目錄ABP VNext RediSearch&#xff1a;微服務級全文檢索 &#x1f680;&#x1f4da; 一、背景與動機 &#x1f680;&#x1f6e0;? 二、環境與依賴 &#x1f433;2.1 Docker Compose 啟動 R…

TensorFlow深度學習實戰——基于自編碼器構建句子向量

TensorFlow深度學習實戰——基于自編碼器構建句子向量 0. 前言1. 句子向量2. 基于自編碼器構建句子向量2.1 數據處理2.2 模型構建與訓練 3. 模型測試相關鏈接 0. 前言 在本節中&#xff0c;我們將構建和訓練一個基于長短期記憶 (Long Short Term Memory, LSTM) 的自編碼器&…

C語言使用Protobuf進行網絡通信

筆者前面博文Go語言網絡游戲服務器模塊化編程介紹了Go語言在開發網絡游戲時如何進行模塊化編程&#xff0c;在其中使用了Protobuf進行網絡通信。在Protobuf官方實現中并沒有生成C語言的實現&#xff0c;不過有一個開源的protobuf-c可以使用。 先來看看protobuf-c生成的代碼&am…

vue3 隨手筆記12--組件通信方式9/5--useAttrs

一 什么是useAttrsuseAttrs 是 Vue 3 Composition API 中提供的一個函數&#xff0c;它屬于 Vue 的組合式 API 工具集的一部分。通過 useAttrs&#xff0c;你可以訪問傳遞給組件但未被聲明為 props 的所有屬性。這對于處理非 prop 特性&#xff08;attributes&#xff09;特別有…

HumanRisk-自動化安全意識與合規教育平臺方案

權威數據顯示&#xff0c;74%以上的數據泄露與網絡安全事件歸根結底與人為因素有關&#xff0c;60%以上的網絡安全事件是由內部人員失誤造成的。這一現狀揭示了一個核心命題&#xff1a;網絡安全威脅正從技術漏洞轉向“人為因素風險”。Gartner的調查發現&#xff0c;即便接受了…

2025年食品科學與健康大數據國際會議(SHBD 2025)

2025年食品科學與健康大數據國際會議 2025 International Conference on Food Science and Health Big Data&#xff08;一&#xff09;大會信息 會議簡稱&#xff1a;ICFSHBD 2025 大會地點&#xff1a;中國上…

CompareFace人臉識別算法環境部署

一、docker 安裝 步驟1&#xff1a;啟用系統功能 右鍵開始菜單 → 應用和功能 → 點擊 程序和功能 → 勾選 Hyper-V 和 Windows子系統Linux 步驟2&#xff1a;獲取安裝包 訪問Docker官網安裝包下載頁 &#xff0c;下載「Docker Desktop Installer.rar」壓縮包 步驟3&#…

STM32固件升級設計——內部FLASH模擬U盤升級固件

目錄 一、功能描述 1、BootLoader部分&#xff1a; 2、APP部分&#xff1a; 二、BootLoader程序制作 1、分區定義 2、 主函數 3、配置USB 4、配置fatfs文件系統 5、程序跳轉 三、APP程序制作 四、工程配置&#xff08;默認KEIL5&#xff09; 五、運行測試 結束語…