sven-johannsen.de
sven@sven-johannsen.de
c++ user group aachen
07-may-2015
platform independed filesystem wrapper
2 basic concepts:
Example:
int remove( const char * filename ); // error handling with errno
BOOL WINAPI DeleteFile( _In_ LPCTSTR lpFileName ); // error handling with GetLastError
Window is just an other kind of Linux
Note 1: remove is also part of the Posix support on Windows
Note 2: DeleteFileA vs. DeleteFileW
Note 3: Only DeleteFileW is able to delete all files on all windows systems, but different type (wchar_t / const unsigned short*)
It's easy to wrap different functions for simular logic,
but it's hard write portable code for different data types.
Solution: boost::filesystem::path
namespace boost {
namespace filesystem {
class path {
public:
typedef "see below" value_type; // char for ISO/IEC 9945, wchar_t for Windows
typedef std::basic_string<value_type> string_type;
constexpr value_type preferred_separator;
...
};
}}
Hide the platform depended types in filesystem::path to provide a portable interface.
Example remove:
namespace boost {
namespace filesystem {
bool remove(const path& p);
}
}
boost::filesystem::path p = "C:\\Temp\\bla.txt";
boost::filesystem::remove(p);
class path
{
typedef "see below" value_type; // char for ISO/IEC 9945, wchar_t for Windows
typedef std::basic_string<value_type> string_type;
...
// native format observers
const string_type& native() const noexcept; // native format, encoding
const value_type* c_str() const noexcept; // native().c_str()
template <class String>
String string(const codecvt_type& cvt=codecvt()) const; // native format
const string string(const codecvt_type& cvt=codecvt()) const; // native format
const wstring wstring(const codecvt_type& cvt=codecvt()) const; // ditto
// u16string(), u32string()
...
};
using namespace boost::filesystem; path p = "myfolder/myfile.txt"; std::string name1 = p.string(); // "myfolder/myfile.txt"; std::wstring name2 = p.wstring(); // L"myfolder/myfile.txt"; auto name3 = p.native(); std::cout << typeid(name3).name() << std::endl; // class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> > #ifdef _WIN32 assert(typeid(name3) == typeid(std::wstring)); #else assert(typeid(name3) == typeid(std::string)); #endif
class path
{
...
// generic format observers
template <class String>
String generic_string() const;
const string generic_string(const codecvt_type& cvt=codecvt()) const; // generic format
// generic_wstring, generic_u16string(), generic_u32string()
...
};
using namespace boost::filesystem; path p = "myfolder\\myfile.txt"; std::string native_name = p.string(); std::string generic_name = p.generic_string(); std::cout << "native_name: " << native_name << std::endl; // native_name: myfolder\myfile.txt std::cout << "generic_name: " << generic_name << std::endl; // generic_name: myfolder/myfile.txt std::cout << "pref. sep: " << (char)path::preferred_separator << std::endl; // pref. sep: \
The class path is wrapper for a string with filesystem reletated convenience functions like:
extension(), filename() )is_absolute(), has_extension() )using namespace boost::filesystem; using namespace std; path p = "r:\\develop\\git\\texte\\filesystem\\source\\path_test2.cpp"; string parent = p.parent_path().string(); // r:\develop\git\texte\filesystem\source string filename = p.filename().string(); // path_test2.cpp string stem = p.stem().string(); // path_test2 string ext = p.extension().string(); // .cpp bool bAbsolute = p.is_absolute(); // true bool bHasRoot = p.has_root_directory(); // true
| filesystem | my company | example |
|---|---|---|
| path | full_path | C:\temp\filename.txt |
| parent_path | path | C:\temp |
| filename | full_filename | filename.txt |
| stem | filenname | filename |
| extension | extension | .txt |
Exception plus error_code:
a set of 2 functions to support 2 different error handling concepts
// throw boost::filesystem_error bool remove(const path& p); // set the error code bool remove(const path& p, system::error_code& ec);
Exceptions
using namespace boost::filesystem;
using namespace std;
try {
path self(argv[0]);
remove(self); // should fail: program is in use
}
catch (const boost::filesystem::filesystem_error& ex) {
// std::exception is also OK
cout << ex.what() << endl;
// boost::filesystem::remove: Access is denied: "D:\build\filesystem\Debug\error_handling1.exe"
}
system::error_code
using namespace boost::filesystem;
using namespace std;
boost::system::error_code ec;
path self(argv[0]);
remove(self, ec); // should fail: program is in use
if (ec) {
cout << ec.message() << endl;
// Access is denied
}
Iterate over all directory entries
(no pattern matting!)
namespace fs = boost::filesystem;
for (fs::directory_iterator it(p);
it != fs::directory_iterator(); ++it) {
// pattern matching missing...
if(fs::is_directory(*it))
cout << "[" << it->path().filename().string() << "]" << endl;
else if(fs::is_regular_file(*it))
cout << it->path().filename().string() << endl;
else
cout << "error: " << it->path().string() << endl;
}
}
Iterate recursive over all directory entries
if (fs::is_directory(p)) {
for (fs::recursive_directory_iterator it(p), itEnd;
it != itEnd; ++it) {
cout << it->path().filename().string() << " " << endl;
}
} else {
cout << p.filename() << endl;
}
with error handling for increment
boost::system::error_code ec;
if (fs::is_directory(p)) {
for (fs::recursive_directory_iterator it(p), itEnd;
it != itEnd; it.increment(ec)) {
if(ec)
cout << "Error: " << ec.message() << " for " << p << endl;
else
cout << it->path().filename().string() << " " << endl;
}
} else {
cout << p.filename() << endl;
}
example: mkdir
namespace fs = boost::filesystem;
fs::path target(argv[1]);
if (!fs::exists(target)) {
fs::create_directory(target);
} else if (!is_directory(target)) {
cerr << "Error to create: " << target << endl;
}
example: pwd
namespace fs = boost::filesystem; fs::path p = fs::current_path(); std::cout << p.string() << std::endl;
| functions | description |
|---|---|
| copy | call copy_directory, copy_file or copy_symlink |
| copy_directory | copy folder without content |
| copy_file | copy single file |
| copy_symlink | copy symbolic link |
| create_directories | mulit level |
| create_directory | single level |
| create_hard_link | if supported by file system |
| create_symlink | if supported by file system |
| functions | description |
|---|---|
| absolute | make a relative path absolute (based on current path) |
| canonical | eliminate all .. and symbolic links |
| current_path | pwd |
| initial_path | the current_path on program start (at the time of the first call of initial_path ) |
| temp_directory_path | temp. folder |
| unique_path | a unique path based on a pattern |
| system_complete | resolve a path to a program / shared library / dll |
| functions | description |
|---|---|
| exists | file or folder exists |
| rename | rename file, folder or symbolic link |
| remove | delete file, folder or symbolic link |
| remove_all | delete recursive |
| permissions | check the permission of a file, folder or symbolic link |
| space | free space on a device |
| status | regular file, directory, socket, fifo, ... |
| status_known | check for a specific status |
| functions | description |
|---|---|
| file_size | file size |
| resize_file | resize a file |
| last_write_time | V3: std::time_t, n3940: std::chrono... |
| hard_link_count | . |
| read_symlink | . |
| symlink_status | . |
simple tests for path objects:
| functions | description |
|---|---|
| is_directory | folder |
| is_regular_file | file |
| is_symlink | simbolic link |
| is_other | no directory, no file, no symlink |
| is_empty | empty file |
std::ofstream(const char* filename);
std::ofstream(const string& filename);
boost::filesystem::ofstream(const boost::filesystem::path& path);
example: cat
namespace fs = boost::filesystem;
fs::path p = "C:\\temp\\a.txt";
fs::ifstream ifs(p);
if (ifs) {
for (std::istreambuf_iterator<char> it(ifs);
it != std::istreambuf_iterator<char>(); ++it) {
cout << *it;
}
}
cout << endl;
goto cmake_presentation;
/