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;
/