PyTorch and TorchVision FasterRCNN interpreting the output in C++ GenericDict - c++

I'm trying to interpret the output of FasterRCNN in C++ and I'm fighting with the GenericDict type.
My code is as follows:
#include <opencv4/opencv2/opencv.hpp>
#include <opencv4/opencv2/shape.hpp>
#include <opencv4/opencv2/imgcodecs.hpp>
#include <opencv4/opencv2/highgui.hpp>
#include <opencv4/opencv2/imgproc.hpp>
#include <opencv4/opencv2/core/utility.hpp>
#include <opencv4/opencv2/core/mat.hpp>
#include <c10/cuda/CUDAStream.h>
#include <torch/csrc/autograd/grad_mode.h>
#include <torch/csrc/api/include/torch/torch.h>
#include <torch/script.h>
#include <torchvision/vision.h>
#include <torchvision/nms.h>
#include <iostream>
#include <memory>
#include <string>
int main(int argc, const char* argv[])
{
if (argc != 3)
{
printf("usage: %s <path-to-exported-script-module> <image_to_test>\n",argv[0]);
return -1;
}
std::string module_filename = argv[1];
std::string image_file = argv[2];
try
{
cv::Mat input_img = cv::imread(image_file, cv::IMREAD_GRAYSCALE);
torch::autograd::AutoGradMode guard(false);
// Deserialize the ScriptModule from a file using torch::jit::load().
torch::jit::script::Module module = torch::jit::load(module_filename);
assert(module.buffers().size() > 0);
module.eval();
// Assume that the entire model is on the same device.
// We just put input to this device.
auto device = (*std::begin(module.buffers())).device();
const int height = input_img.rows;
const int width = input_img.cols;
const int channels = 1;
auto input = torch::from_blob(input_img.data, {height, width, channels}, torch::kUInt8);
// HWC to CHW
// input = input.to(device, torch::kFloat).permute({2, 0, 1}).contiguous();
input = input.to(device, torch::kFloat).permute({2, 0, 1}).contiguous();
// run the network
std::vector<at::Tensor> inputs;
inputs.push_back(input);
auto output = module.forward({inputs});
if (device.is_cuda())
c10::cuda::getCurrentCUDAStream().synchronize();
std::cout << "output: " << output << std::endl;
auto outputs = output.toTuple()->elements();
std::cout << "outputs: " << outputs << std::endl;
for( auto& elem : outputs )
{
std::cout << "elem: " << elem << std::endl;
if( elem.isGenericDict() )
{
std::cout << "elem is generic dict: " << elem << std::endl;
c10::Dict<c10::IValue, c10::IValue> dict = elem.toGenericDict();
auto elem_vector_0 = dict.at(c10::IValue("scores")).toIntVector();
auto elem_vector_1 = dict.at(c10::IValue("boxes")).toIntVector();
auto elem_vector_2 = dict.at(c10::IValue("labels")).toIntVector();
for( auto& ee0 : elem_vector_0 )
{
std::cout << "elem_vector_0" << ee0 << std::endl;
}
for( auto& ee0 : elem_vector_1 )
{
std::cout << "elem_vector_1" << ee0 << std::endl;
}
for( auto& ee0 : elem_vector_2 )
{
std::cout << "elem_vector_2" << ee0 << std::endl;
}
}
}
cv::namedWindow("Display Image", cv::WINDOW_AUTOSIZE );
cv::imshow("Display Image", input_img);
cv::waitKey(0);
}
catch(const c10::Error& e)
{
std::cerr << e.what() << std::endl;
return -1;
}
catch(const cv::Exception& e)
{
std::cerr << e.what() << std::endl;
return -1;
}
catch(const std::exception& e)
{
std::cerr << e.what() << std::endl;
return -1;
}
catch(...)
{
std::cerr << "Unknown error" << std::endl;
return -1;
}
std::cout << "ok\n";
return 0;
}
and the output is:
(base) fstrati#fstrati-desktop:~/libtorch_shared_cuda_10.1/load_and_run_model/Release$ ./load_and_run_model ./torch_script_v0.2.pt test_img.png
[W faster_rcnn.py:95] Warning: RCNN always returns a (Losses, Detections) tuple in scripting (function )
output: ({}, [{boxes: [ CPUFloatType{0,4} ], labels: [ CPULongType{0} ], scores: [ CPUFloatType{0} ]}])
outputs: {} [{boxes: [ CPUFloatType{0,4} ], labels: [ CPULongType{0} ], scores: [ CPUFloatType{0} ]}]
elem: {}
elem is generic dict: {}
Argument passed to at() was not in the map.
I'm struggling to find a way to extract the boxes, labels and scores from the dictionary
GenericDict.
This map is strange, I cannot iterate on it and I cannot access first and second types...
with it->first it->second
Any ideas ?
Thanks in advance

I think the following method can resolve the main problem here,
output = module.forward(inputs);
auto detections = output.toTuple()->elements().at(1).toList().get(0).toGenericDict();
std::cout << ">>> detections labels: " << detections.at("labels") << std::endl;
std::cout << ">>> detections boxes: " << detections.at("boxes") << std::endl;
std::cout << ">>> detections scores: " << detections.at("scores") << std::endl;
Besides, I've added an executable file https://github.com/zhiqwang/yolov5-rt-stack/tree/master/deployment/libtorch to show how libtorch works.

Related

Cannot load traced_torch_model_pt in C++

I tried to load pre-built traced_torch_model.pt to C++; however, the error message showed "example-app.exe" existed with code -1
Please see below my code snippets and error message screenshot.
I appreciate your input.
using namespace std;
#include <torch/script.h>
#include <iostream>
#include <memory>
int main(int argc, const char* argv[]) {
if (argc != 2) {
std::cerr << "usage: example-app <C:\\Users\\C at work\\0813_2021\\traced_torch_model.pt\\>n";
return -1;
}
torch::jit::script::Module module;
try {
// Deserialize the ScriptModule from a file using torch::jit::load().
module = torch::jit::load(argv[1]);
}
catch (const c10::Error& e) {
std::cerr << "error loading the model\n";
return -1;
}
std::cout << "Model " << argv[1] << " loaded fine\n";
// Create a vector of inputs.
std::vector<torch::jit::IValue> inputs;
inputs.push_back(torch::randn({ 1, 1, 64, 101 }));
// Execute the model and turn its output into a tensor.
at::Tensor output = module.forward(inputs).toTensor();
std::cout << output << "\n";
int y_hat = output.argmax(1).item().toInt();
std::cout << "Predicted class: " << y_hat << "\n";
}

How to access the value from the json file using runtime key in c++

I'm new to json and trying to use nlohmann library and I'm quite stuck.
config.json file looks like this
{
"Version": 1.1,
"Size": 1024,
"integer": 600,
"Map": [{"0": "india"}, {"1": "usa"}, {"2": "china"}, {"2": "japan"}],
"name": "xxx",
}
I want to get the value of ["Map"]["1"] where "1" is a runtime entity. I have tried several ways but none of them is working.
std::string strRwFilePath = std::string(CONFIG_PATH) + "config.json";
std::ifstream RwFile(strRwFilePath);
if (RwFile.fail())
{
std::cout << "[ReadFileContents]RwFile doesnt exist" << std::endl;
}
else
{
// parsing input
RwFile >> conf_json;
if(!(conf_json.empty()))
{
//METHOD 1
for (auto it = conf_json["Map"].begin(); it != conf_json["Map"].end(); ++it)
{
std::cout << it.key() << " | " << it.value() << "\n";
std::string keyy = std::to_string(m_iRegionType);
std::string tempKey = it.key(); //ERROR- [json.exception.invalid_iterator.207] cannot use key() for non-object iterators
std::cout << "for loop: tempKey:" << tempKey << " \n";
if(!strcmp(tempKey.c_str(),(std::to_string(m_iRegionType)).c_str()))
{
std::cout << "SUCCESS 222" << std::endl;
break;
}
else
{
std::cout << "FAILURE 222" << std::endl;
}
}
//METHOD 2
for(auto& element : conf_json["Map"].items())
{
std::cout << element.key() << " | " << element.value() << "\n";
m_iRegionType = 2;
std::string keyy = std::to_string(m_iRegionType);
std::string tempKey = element.key(); //ERROR: [json.exception.type_error.302] type must be string, but is object
if(!strcmp(tempKey.c_str(),(std::to_string(m_iRegionType)).c_str()))
{
std::cout << "SUCCESS 333" << std::endl;
break;
}
else
{
std::cout << "FAILURE 333" << std::endl;
}
}
//METHOD 3
std::string strTempKey = std::to_string(m_iRegionType);
if(!conf_json["Map"][strTempKey.c_str()].is_null())
{
std::string strName = conf_json["Map"][strTempKey.c_str()]; //ERROR: [json.exception.type_error.305] cannot use operator[] with a string argument with array
std::cout << "key found. RegionName: " << strName << '\n';
}
else
{
std::cout << "key not found" << std::endl;
}
//METHOD 4
// create a JSON object
std::string strKey = "/Map/" + std::to_string(m_iRegionType);
// call find
auto it_two = conf_json.find(strKey.c_str());
if(true == (it_two != conf_json.end()))
{
std::cout << "Region key was found. RegionBucketName: " << *it_two << '\n';
std::string strRegionName = conf_json["Map"][m_iRegionType];
std::string strRegionName2 = *it_two;
std::cout << "strRegionName: " << strRegionName << '\n';
std::cout << "strRegionName2: " << strRegionName2 << '\n';
}
else
{
//getting this as OUTPUT even though key is available
std::cout << "invalid region type, key not available m_iRegionType: " << m_iRegionType << std::endl;
}
}
else
{
std::cout << "[ReadFileContents]Read Write Json object is empty" << std::endl;
}
}
I have also tried using the small json structure where I am not able to access the value using the runtime key.
json j =
{
{"integer", 1},
{"floating", 42.23},
{"string", "hello world"},
{"boolean", true},
{"object", {{"key1", 1}, {"key2", 2}}},
{"array", {1, 2, 3}}
};
int avail4 = j.value("/integer"_json_pointer, 444);
std::cout << "avail4 : " << avail4 << std::endl; //OUTPUT- avial4 : 1
int avail5 = j.value("/object/key2"_json_pointer, 555);
std::cout << "avail5 : " << avail5 << std::endl; //OUTPUT- avial5 : 2
auto strKey3 = "/object/key2";
int avail6 = j.value(strKey3, 666);
std::cout << "avail6 : " << avail6 << std::endl; //OUTPUT- avial6 : 666
Could any one help me.
You can't use ["Map"]["1"] to find the inner array objects. You need iterator through the array
A code example may like this:
#include <nlohmann/json.hpp>
#include <iostream>
// for convenience
using json = nlohmann::json;
int main() {
json j = R"(
{
"Version": 1.1,
"Size": 1024,
"integer": 600,
"Map": [{
"0": "india"
}, {
"1": "usa"
}, {
"2": "china"
}, {
"2": "japan"
}],
"name": "xxx"
}
)"_json;
for (const auto& ele : j["Map"]) {
if (ele.contains("1")) {
std::cout << ele["1"] << std::endl;
}
}
}
Output:
usa
Demo

reading nested json file w/ boost

I'm trying to read a JSON formatted file which is nested with the BOOST library in C++.
I tried a couple of things, browsed through many questions in this forum, but I couldn't find anything really similar, or that would work.
the json file im looking at has the following structure
{
"ABC": {
"FF:[10.0,20.0]": {
"GHG:[-2.5,-2.0]": {
"value": 1.1176470518112183,
"error": 0.013857235087295462
},
"GHG:[1.566,2.0]": {
"value": 0.9958805441856384,
"error": 0.027176504445043704
}
},
"FF:[30.0,50.0]": {
"GHG:[-2.5,-2.0]": {
"value": 1.1176470518112183,
"error": 0.013857235087295462
},
"GHG:[2.0,2.5]": {
"value": 1.0464363098144531,
"error": 0.061588554028766326
}
}
},
"ZXY": {
"FF:[10.0,20.0]": {
"GHG:[-2.5,-2.0]": {
"value": 1.1176470518112183,
"error": 0.013857235087295462
},
"GHG:[1.566,2.0]": {
"value": 0.9958805441856384,
"error": 0.027176504445043704
}
},
"FF:[30.0,50.0]": {
"GHG:[-2.5,-2.0]": {
"value": 1.1176470518112183,
"error": 0.013857235087295462
},
"GHG:[2.0,2.5]": {
"value": 1.0464363098144531,
"error": 0.061588554028766326
}
}
}
}
i was trying to use the BOOST_FOREACH method as is described here:
http://www.ce.unipr.it/people/medici/boost_ptree.html
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/foreach.hpp>
#include <iostream>
namespace pt = boost::property_tree;
pt::ptree tree;
int main() {
pt::read_json("bla.json", tree);
BOOST_FOREACH( pt::ptree::value_type const&v, tree.get_child("ABC") ) {
const std::string & key = v.first;
const pt::ptree & subtree = v.second;
//const pt::ptree & subsubtree = v.second;
std::cout << key << std::endl;
if(subtree.empty()) {
std::cout << "subtree empty" << std::endl;
} else {
std::cout << "subtree not empty" << std::endl;
BOOST_FOREACH( pt::ptree::value_type const&sv, subtree.get_child(key) ) {
const std::string & subkey = sv.first; // key
const pt::ptree & subsubtree = sv.second; // value (or a subnode)
if(subsubtree.empty()) {
std::cout << "subsubtree empty" << std::endl;
} else {
std::cout << "subsubtree not empty" << std::endl;
std::cout << subsubtree.data() << std::endl;
}
}
}
}
return 0;
}
but i always get an error that there's no such node:
terminate called after throwing an instance of 'boost::wrapexcept<boost::property_tree::ptree_bad_path>'
what(): No such node (FF:[10.0,20.0])
Aborted
i'm pretty sure this is not a hard problem, but i'm kind of a newbie with c++ and i really appreciate any help!
Finally, I got it that your get_child usage is not correct! Just pass the tree to range-based for.
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/foreach.hpp>
#include <iostream>
namespace pt = boost::property_tree;
pt::ptree tree;
int main() {
pt::read_json("bla.json", tree);
for(auto&& v : tree.get_child("ABC")) {
const std::string & key = v.first; // key
const pt::ptree & subtree = v.second; // value (or a subnode)
//const pt::ptree & subsubtree = v.second; // value (or a subnode)
std::cout << key << std::endl;
if(subtree.empty()) {
// This is a key:value
// use subtree.data() as string value or subtree.get_value<T>()
std::cout << "subtree empty" << std::endl;
} else {
// This is a subtree
// use subtree as child
std::cout << "subtree not empty" << std::endl;
for(auto&& sv : subtree/*.get_child(pt::path(key, '\0'))*/) {
[[maybe_unused]]const std::string & subkey = sv.first; // key
const pt::ptree & subsubtree = sv.second; // value (or a subnode)
std::cout << subkey << std::endl;
if(subsubtree.empty()) {
std::cout << "subsubtree empty" << std::endl;
} else {
std::cout << "subsubtree not empty" << std::endl;
std::cout << subsubtree.data() << std::endl;
for(auto&& ssv: subsubtree) {
[[maybe_unused]]const std::string & subsubkey = ssv.first; // key
const pt::ptree & subsubsubtree = ssv.second; // value (or a subnode)
std::cout << subsubkey << std::endl;
if(subsubsubtree.empty()) {
std::cout << "subsubsubtree empty" << std::endl;
if (auto subsubsubtree_v = subsubsubtree.get_value_optional<double>()) {
std::cout << subsubsubtree_v.get() << std::endl;
}
}
else {
std::cout << "subsubtree not empty" << std::endl;
std::cout << subsubtree.data() << std::endl;
}
}
}
}
}
}
return 0;
}
https://wandbox.org/permlink/uRub7N5VeZ9WrDKd

How to convert tensor to image array?

I would like to convert a tensor to image array and use tensor.data() method.
But it doesn't work.
#include <torch/script.h> // One-stop header.
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkImageRegionIterator.h"
//////////////////////////////////////////////////////
//Goal: load jit script model and segment myocardium
//Step: 1. load jit script model
// 2. load input image
// 3. predict by model
// 4. save the result to file
//////////////////////////////////////////////////////
typedef short PixelType;
const unsigned int Dimension = 3;
typedef itk::Image<PixelType, Dimension> ImageType;
typedef itk::ImageFileReader<ImageType> ReaderType;
typedef itk::ImageRegionIterator<ImageType> IteratorType;
bool itk2tensor(ImageType::Pointer itk_img, torch::Tensor &tensor_img) {
typename ImageType::RegionType region = itk_img->GetLargestPossibleRegion();
const typename ImageType::SizeType size = region.GetSize();
std::cout << "Input size: " << size[0] << ", " << size[1]<< ", " << size[2] << std::endl;
int len = size[0] * size[1] * size[2];
short rowdata[len];
int count = 0;
IteratorType iter(itk_img, itk_img->GetRequestedRegion());
// convert itk to array
for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter) {
rowdata[count] = iter.Get();
count++;
}
std::cout << "Convert itk to array DONE!" << std::endl;
// convert array to tensor
tensor_img = torch::from_blob(rowdata, {1, 1, (int)size[0], (int)size[1], (int)size[2]}, torch::kShort).clone();
tensor_img = tensor_img.toType(torch::kFloat);
tensor_img = tensor_img.to(torch::kCUDA);
tensor_img.set_requires_grad(0);
return true;
}
bool tensor2itk(torch::Tensor &t, ImageType::Pointer itk_img) {
std::cout << "tensor dtype = " << t.dtype() << std::endl;
std::cout << "tensor size = " << t.sizes() << std::endl;
t = t.toType(torch::kShort);
short * array = t.data<short>();
ImageType::IndexType start;
start[0] = 0; // first index on X
start[1] = 0; // first index on Y
start[2] = 0; // first index on Z
ImageType::SizeType size;
size[0] = t.size(2);
size[1] = t.size(3);
size[2] = t.size(4);
ImageType::RegionType region;
region.SetSize( size );
region.SetIndex( start );
itk_img->SetRegions( region );
itk_img->Allocate();
int len = size[0] * size[1] * size[2];
IteratorType iter(itk_img, itk_img->GetRequestedRegion());
int count = 0;
// convert array to itk
std::cout << "start!" << std::endl;
for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter) {
short temp = *array++; // ERROR!
std::cout << temp << " ";
iter.Set(temp);
count++;
}
std::cout << "end!" << std::endl;
return true;
}
int main(int argc, const char* argv[]) {
int a, b, c;
if (argc != 4) {
std::cerr << "usage: automyo input jitmodel output\n";
return -1;
}
std::cout << "========= jit start =========\n";
// 1. load jit script model
std::cout << "Load script module: " << argv[2] << std::endl;
std::shared_ptr<torch::jit::script::Module> module = torch::jit::load(argv[2]);
module->to(at::kCUDA);
// assert(module != nullptr);
std::cout << "Load script module DONE" << std::endl;
// 2. load input image
const char* img_path = argv[1];
std::cout << "Load image: " << img_path << std::endl;
ReaderType::Pointer reader = ReaderType::New();
if (!img_path) {
std::cout << "Load input file error!" << std::endl;
return false;
}
reader->SetFileName(img_path);
reader->Update();
std::cout << "Load image DONE!" << std::endl;
ImageType::Pointer itk_img = reader->GetOutput();
torch::Tensor tensor_img;
if (!itk2tensor(itk_img, tensor_img)) {
std::cerr << "itk2tensor ERROR!" << std::endl;
}
else {
std::cout << "Convert array to tensor DONE!" << std::endl;
}
std::vector<torch::jit::IValue> inputs;
inputs.push_back(tensor_img);
// 3. predict by model
torch::Tensor y = module->forward(inputs).toTensor();
std::cout << "Inference DONE!" << std::endl;
// 4. save the result to file
torch::Tensor seg = y.gt(0.5);
// std::cout << seg << std::endl;
ImageType::Pointer out_itk_img = ImageType::New();
if (!tensor2itk(seg, out_itk_img)) {
std::cerr << "tensor2itk ERROR!" << std::endl;
}
else {
std::cout << "Convert tensor to itk DONE!" << std::endl;
}
std::cout << out_itk_img << std::endl;
return true;
}
The runtime log is showed below:
Load script module:model_myo_jit.pt
Load script module DONE Load
image: patch_6.nii.gz
Load image DONE!
Input size: 128, 128, 128
Convert itk to array DONE!
Convert array to tensor DONE!
Inference DONE!
tensor dtype = unsigned char
tensor size = [1, 1, 96, 96, 96]
start!
Segmentation fault (core dumped)
Why and how to convert?
I have found the solution. When I convert the y to kCPU, it works. Because it in CUDA before.

How to sort files by descending the file size

I need to output the 5 largest files from the directory. For this, I use a boost filesystem c++. In the process of writing the program, I encountered difficulties. I can output all files from the directory, file size, file creation date and file attributes. In the vector I put the names of the files, but I just can not figure out how to sort by size. I need to output the 5 largest files from the specified directory. I think that you must first sort by file size by descending. That is, from a larger value to a smaller one. And then the scans are not needed. Most likely it needs to be done in a loop. Help me please.
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <boost/filesystem.hpp>
using namespace std;
using namespace boost::filesystem;
void ShowAttributes(DWORD attributes);
void AttribFile(const char* str);
void Attrib();
int main(int argc, char* argv[])
{
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
if (argc < 2)
{
cout << "Using Name Directory" << endl;
return 1;
}
path Part(argv[1]);
try
{
if (exists(Part))
{
if (is_regular_file(Part))
{
cout << Part << " Size " << file_size(Part) << " bytes ";
time_t Time = last_write_time(Part);
cout << asctime(localtime(&Time)) << endl;
}
else if (is_directory(Part))
{
cout << "Directory " << Part << " include:" << endl;
vector<string> vecList;
for (auto j : directory_iterator(Part))
vecList.push_back(j.path().filename().string());
sort(vecList.begin(), vecList.end());
string filePath;
for (auto i : vecList)
{
cout << " " << i;
filePath = Part.parent_path().string() + "/" + i;
if (is_regular_file(filePath))
{
if (Is_Executable_File(filePath))
cout << "*";
cout << " Size " << file_size(filePath) << " bytes ";
time_t Time = last_write_time(Part);
cout << asctime(localtime(&Time)) << endl;
AttribFile(filePath.c_str());
}
cout << endl;
}
}
}
else
cout << Part << " Erroe!" << endl;
}
catch (const filesystem_error& ex)
{
cout << ex.what() << endl;
}
return 0;
}
void ShowAttributes(DWORD attributes)
{
if (attributes & FILE_ATTRIBUTE_ARCHIVE)
cout << " archive" << endl;
if (attributes & FILE_ATTRIBUTE_DIRECTORY)
cout << " directory" << endl;
if (attributes & FILE_ATTRIBUTE_HIDDEN)
cout << " hidden" << endl;
if (attributes & FILE_ATTRIBUTE_NORMAL)
cout << " normal" << endl;
if (attributes & FILE_ATTRIBUTE_READONLY)
cout << " read only" << endl;
if (attributes & FILE_ATTRIBUTE_SYSTEM)
cout << " system" << endl;
if (attributes & FILE_ATTRIBUTE_TEMPORARY)
cout << " temporary" << endl;
}
void AttribFile(const char* str)
{
DWORD attributes;
attributes = GetFileAttributesA(str);
ShowAttributes(attributes);
}
void Attrib()
{
char filename[MAX_PATH];
DWORD attributes;
cout << "Name of file: ";
cin >> filename;
attributes = GetFileAttributesA(filename);
ShowAttributes(attributes);
}
create a class or struct to hold the information you need on each file, e.g.
struct MyFile
{
std::string name;
size_t size;
}
create a vector of these and read all files from your folder
then sort the vector and give a custom comparison (e.g. in form of a lambda), see Sorting a vector of custom objects for details on that
Here's a program based on just the standard library that does what you seem to intend:
Live On Coliru
Update: Using C++11 and Boost Filesystem instead: Live On Coliru
#include <algorithm>
#include <filesystem>
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
namespace fs = std::filesystem;
struct tm *last_modified(fs::path const &p) {
auto ftime = fs::last_write_time(p);
auto cftime = decltype(ftime)::clock::to_time_t(ftime);
return std::localtime(&cftime);
}
bool is_executable(fs::path const& p) {
return fs::perms::none != (fs::status(p).permissions() &
(fs::perms::owner_exec |
fs::perms::group_exec |
fs::perms::others_exec));
}
void report(fs::path const& file) {
if (is_executable(file))
std::cout << "*";
std::cout << file << "\tSize:" << fs::file_size(file);
std::cout << "\tModified:" << std::asctime(last_modified(file));
}
template <typename Accessor, typename Cmp = std::less<> >
static auto compare_by(Accessor&& f, Cmp cmp = {}) {
return [f=std::forward<Accessor>(f),cmp](auto const& a, auto const& b) {
return cmp(f(a), f(b));
};
}
int main(int argc, char *argv[]) {
if (argc < 2) {
std::cout << "Using: " << argv[0] << " [Name|Directory]" << std::endl;
return 1;
}
fs::path filespec(argv[1]);
try {
if (is_regular_file(filespec)) {
// print
report(filespec);
} else if (is_directory(filespec)) {
std::cout << "Directory " << filespec << " include:" << std::endl;
std::vector<fs::directory_entry> const entries { fs::directory_iterator{filespec}, {} };
// filter just files
std::vector<fs::path> files;
std::remove_copy_if(entries.begin(), entries.end(),
back_inserter(files),
[](auto& de) { return de.is_directory(); });
// get the top 5, or fewer
auto b = files.begin(),
top5 = b + std::min(5ul, files.size()),
e = files.end();
// ordered by size, descending
std::partial_sort(b, top5, e,
compare_by([](auto& p) { return fs::file_size(p); }, std::greater<>{}));
files.erase(top5, e);
// print
for (auto& f : files)
report(f);
} else {
std::cout << filespec << " Error!" << std::endl;
}
} catch (const fs::filesystem_error &ex) {
std::cout << ex.what() << std::endl;
}
}
Prints, e.g. for ./a.out /usr/lib:
Directory "/usr/lib/" include:
"/usr/lib/libruby-1.9.1-static.a" Size:3654748 Modified:Wed Nov 19 21:41:25 2014
"/usr/lib/libruby-1.9.1.so.1.9.1" Size:2087600 Modified:Wed Nov 19 21:41:20 2014
"/usr/lib/libruby-1.9.1.so" Size:2087600 Modified:Wed Nov 19 21:41:20 2014
"/usr/lib/libruby-1.9.1.so.1.9" Size:2087600 Modified:Wed Nov 19 21:41:20 2014
"/usr/lib/libc++.so.1" Size:1460461 Modified:Mon Sep 8 20:01:17 2014