1. 程式人生 > >[譯]C++17,標準庫變化的更多細節

[譯]C++17,標準庫變化的更多細節

看到一個介紹 C++17 的系列博文(原文),有十來篇的樣子,覺得挺好,看看有時間能不能都簡單翻譯一下,這是第四篇~

之前的文章中我簡單介紹了一些C++17標準庫的新變化,這次我會介紹更多的相關細節.

圖

讓我們首先來看下之前未提到過的新內容.

std::byte

獨立型別 std::byte 實現了 C++ 語言定義中的位元組概念,他能用於訪問物件的原始記憶體,不同於 char 等位元組型別(也可以用於訪問物件的原始記憶體), std::byte 只提供了位邏輯運算相關的介面方法.

namespace std 
{ 
  template <class IntType>
    constexpr byte operator<<(byte b, IntType shift);
  template <class IntType>
    constexpr byte operator>>(byte b, IntType shift);
  constexpr byte operator|(byte l, byte r);
  constexpr byte operator&(byte l, byte r);
  constexpr byte operator~(byte b);
  constexpr byte operator^(byte l, byte r);
} 

你可以使用 std::to_integer(std::byte b) 方法將 std::byte 轉化為整型或者使用 std::byte{integer} 將整型轉化為 std::byte,不過參與轉型的整數必須是一個小於 std::numeric_limits<unsigned_char>::max() 的非負數.

接著來看一些你應該已經知道的內容:

The filesystem library

我在之前的文章中介紹了C++17新引入的檔案系統庫.新的檔案系統庫基於3個概念: 檔案(file), 檔名(file name) 以及 檔案路徑(path). file 可以是目錄,硬連結,符號連結或者常規檔案.path 則可以是絕對路徑,規範路徑或者相對路徑(所謂規範路徑,是指不帶有符號連結, “.” 和 “…” 的檔案路徑).

你可以建立刪除目錄,遍歷目錄內容或者檢查檔案的各類屬性(示例程式碼如下).

#include <fstream>
#include <iostream>
#include <string>
#include <filesystem>
namespace fs = std::filesystem;

int main()
{
	std::cout << "Current path: " << fs::current_path() << std::endl;

	std::string dir = "sandbox/a/b";
	fs::create_directories(dir);

	std::ofstream("sandbox/file1.txt");
	fs::path symPath = fs::current_path() /= "sandbox";
	symPath /= "syma";
	fs::create_symlink("a", symPath);

	std::cout << "fs::is_directory(dir): " << fs::is_directory(dir) << std::endl;
	std::cout << "fs::exists(symPath): " << fs::exists(symPath) << std::endl;
	std::cout << "fs::symlink(symPath): " << fs::is_symlink(symPath) << std::endl;

	for (auto& p : fs::recursive_directory_iterator("sandbox"))
	{
		std::cout << p.path() << std::endl;
	}
	fs::remove_all("sandbox");
	
	return 0;
}

檔案系統庫還有更多的功能,這次我會介紹一些(至少對我來說)沒那麼明顯的特性,內容包括:

  • 如何操作檔案許可權
  • 如何讀取檔案修改時間
  • 如何獲取檔案系統的空間大小

讓我們首先來看看如何操作檔案許可權.

Permissions

std::filesystem::perms 型別用以表示檔案許可權,他是一個位掩碼型別(BitmaskType),所以可以對其進行位運算操作.檔案的訪問許可權基於的是POSIX標準.

以下的示例來自於cppreference.com, 程式碼展示瞭如何讀取和操作 owner(擁有者), group(使用者組) 及 other(其他使用者)相關的檔案許可權.

#include <fstream>
#include <bitset>
#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

void printPerms(fs::perms perm) 
{
	std::cout << ((perm & fs::perms::owner_read) != fs::perms::none ? "r" : "-")
		      << ((perm & fs::perms::owner_write) != fs::perms::none ? "w" : "-")
		      << ((perm & fs::perms::owner_exec) != fs::perms::none ? "x" : "-")
		      << ((perm & fs::perms::group_read) != fs::perms::none ? "r" : "-")
		      << ((perm & fs::perms::group_write) != fs::perms::none ? "w" : "-")
		      << ((perm & fs::perms::group_exec) != fs::perms::none ? "x" : "-")
		      << ((perm & fs::perms::others_read) != fs::perms::none ? "r" : "-")
		      << ((perm & fs::perms::others_write) != fs::perms::none ? "w" : "-")
		      << ((perm & fs::perms::others_exec) != fs::perms::none ? "x" : "-")
		      << std::endl;
}

int main()
{
	std::ofstream("rainer.txt");

	std::cout << "Initial file permissions for a file: ";
	printPerms(fs::status("rainer.txt").permissions());

	fs::permissions("rainer.txt", fs::perms::owner_all | fs::perms::group_all, fs::perm_options::add);
	std::cout << "Adding all bits to owner and group:  ";
	printPerms(fs::status("rainer.txt").permissions());

	fs::permissions("rainer.txt", fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write, fs::perm_options::remove);
	std::cout << "Removing the write bits for all:     ";
	printPerms(fs::status("rainer.txt").permissions());

	fs::remove("rainer.txt");
	
	return 0;
}

程式碼24行中我建立了一個檔案(rainer.txt),通過使用全域性函式 std::filesystem::status::permissions, 我獲取了檔案的許可權資訊並使用 printPerms(第8行至20行) 函式來顯示他們.第29行中我使用 fs::perm_options::add 為檔案添加了 owner 和 group 的所有相關許可權,同樣在33行,我使用
fs::perm_options::remove 移除了owner, group 和 others 的檔案修改許可權,也就是說,我們可以移除一個檔案所有的修改許可權.

程式的輸出如下:

圖

除了許可權,檔案還有修改時間的概念.

Time values

通過全域性函式 std::filesystem::last_write_time, 我們可以讀取和寫入一個檔案的最後修改時間.

#include <iostream>
#include <chrono>
#include <fstream>
#include <filesystem>

namespace fs = std::filesystem;
using namespace std::chrono_literals;

int main() 
{
	fs::path path = fs::current_path() / "rainer.txt";
	std::ofstream(path.c_str());
	auto ftime = fs::last_write_time(path);

	std::time_t cftime = std::chrono::system_clock::to_time_t(ftime);
	std::cout << "Write time on server " << std::asctime(std::localtime(&cftime));
	std::cout << "Write time on server " << std::asctime(std::gmtime(&cftime)) << std::endl;

	fs::last_write_time(path, ftime + 2h);
	ftime = fs::last_write_time(path);

	cftime = std::chrono::system_clock::to_time_t(ftime);
	std::cout << "Local time on client " << std::asctime(std::localtime(&cftime)) << std::endl;

	fs::remove(path);
	
	return 0;
}

程式碼第13行我讀取了新建立檔案(rainer.txt)的修改時間(ftime),並使用該時間初始化了第15行的 std::chrono::system_clock. ftime 的型別為 std::filesystem::file_time_type, 在伺服器上(譯註:作者在cppreference.com網站上執行示例程式碼,所以有伺服器一說)似乎是 std::chrono::system_clock 型別的別名(譯註:就目前而言,MSVC中並非如此,std::filesystem::file_time_type 和 std::chrono::system_clock 是單獨的型別).第16行我使用轉換後的檔案修改時間初始化了 std::localtime 並文字化輸出了該日曆時間.如果我改用 std::gmtime(第17行),程式輸出卻並沒有什麼變化,這一度困擾了我,因為協調世界時(Coordinated Universal Time (UTC))在德國(譯註:作者為德國人)與本地時間應該有2個小時的時差,但是後來想到程式碼是在伺服器上執行的,而伺服器上的協調世界時與本地時間沒有時差,所以程式的輸出也就沒有變化了.

程式的輸出如下,程式碼第19行我手動為檔案的最後修改時間增加了2個小時,由此便得到了德國的本地時間(檔案的最後修改時間).

圖

現在介紹一下新的檔案系統庫中最讓我吃驚的特性.

Space info

全域性函式 std::filesystem::space 可以返回一個 std::filesystem::space_info 物件,該物件包含了3個成員:
capacity, free 和 available.

  • capacity: 檔案系統的總空間大小
  • free: 檔案系統的空閒空間大小
  • available: 可用於非特權程序的空閒空間大小(<= free)

這3個數據都是以位元組為單位,下面的示例程式碼中展示了基本用法(程式碼中的檔案路徑都在同一檔案系統下,所以相關的空間大小也是相同的).

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main() 
{
	fs::space_info root = fs::space("/");
	fs::space_info usr = fs::space("/usr");

	std::cout << ".        Capacity       Free      Available\n"
		      << "/    " << root.capacity << "   "
		      << root.free << "   " << root.available << "\n"
		      << "usr  " << usr.capacity << "   "
		      << usr.free << "   " << usr.available;
		      
    return 0;
}

程式的輸出如下:

圖