Parece que tuve que poner bastante código aquí. Me pregunto si es una práctica mal diseño de dejar esta totalmente en un fichero de cabecera de esta manera:¿Esto es demasiado código para una biblioteca solo encabezado?
#include <list>
#include <string>
#include <boost/noncopyable.hpp>
#include <boost/make_shared.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <Windows.h>
#include "../Exception.hpp"
namespace WindowsAPI { namespace FileSystem {
class NonRecursiveEnumeration;
class RecursiveEnumeration;
struct AllResults;
struct FilesOnly;
template <typename Filter_T = AllResults, typename Recurse_T = NonRecursiveEnumeration>
class DirectoryIterator;
template <typename Recurse_T>
struct FileData;
class NonRecursiveEnumeration : public boost::noncopyable
{
WIN32_FIND_DATAW currentData;
HANDLE hFind;
std::wstring root;
public:
NonRecursiveEnumeration() : hFind(INVALID_HANDLE_VALUE) {
};
NonRecursiveEnumeration(const std::wstring& pathSpec) {
std::wstring::const_iterator lastSlash =
std::find(pathSpec.rbegin(), pathSpec.rend(), L'\\').base();
if (lastSlash != pathSpec.end())
root.assign(pathSpec.begin(), lastSlash);
hFind = FindFirstFileW(pathSpec.c_str(), ¤tData);
if (hFind == INVALID_HANDLE_VALUE)
WindowsApiException::ThrowFromLastError();
while (!wcscmp(currentData.cFileName, L".") || !wcscmp(currentData.cFileName, L"..")) {
increment();
}
};
void increment() {
BOOL success =
FindNextFile(hFind, ¤tData);
if (success)
return;
DWORD error = GetLastError();
if (error == ERROR_NO_MORE_FILES) {
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
} else {
WindowsApiException::Throw(error);
}
};
~NonRecursiveEnumeration() {
if (hFind != INVALID_HANDLE_VALUE)
FindClose(hFind);
};
bool equal(const NonRecursiveEnumeration& other) const {
if (this == &other)
return true;
return hFind == other.hFind;
};
const std::wstring& GetPathRoot() const {
return root;
};
const WIN32_FIND_DATAW& GetCurrentFindData() const {
return currentData;
};
};
//Not implemented yet
class RecursiveEnumeration : public boost::noncopyable
{
};
template <typename Recurse_T>
struct FileData //Serves as a proxy to the WIN32_FIND_DATA struture inside the iterator.
{
const Recurse_T* impl;
template <typename Filter_T, typename Recurse_T>
FileData(const DirectoryIterator<Filter_T, Recurse_T>* parent) : impl(parent->impl.get()) {};
DWORD GetAttributes() const {
return impl->GetCurrentFindData().dwFileAttributes;
};
bool IsDirectory() const {
return (GetAttributes() & FILE_ATTRIBUTE_DIRECTORY) != 0;
};
bool IsFile() const {
return !IsDirectory();
};
bool IsArchive() const {
return (GetAttributes() & FILE_ATTRIBUTE_ARCHIVE) != 0;
};
bool IsReadOnly() const {
return (GetAttributes() & FILE_ATTRIBUTE_READONLY) != 0;
};
unsigned __int64 GetSize() const {
ULARGE_INTEGER intValue;
intValue.LowPart = impl.GetCurrentFindData().nFileSizeLow;
intValue.HighPart = impl.GetCurrentFindData().nFileSizeHigh;
return intValue.QuadPart;
};
std::wstring GetFolderPath() const {
return impl->GetPathRoot();
};
std::wstring GetFileName() const {
return impl->GetCurrentFindData().cFileName;
};
std::wstring GetFullFileName() const {
return GetFolderPath() + GetFileName();
};
std::wstring GetShortFileName() const {
return impl->GetCurrentFindData().cAlternateFileName;
};
FILETIME GetCreationTime() const {
return impl->GetCurrentFindData().ftCreationTime;
};
FILETIME GetLastAccessTime() const {
return impl->GetCurrentFindData().ftLastAccessTime;
};
FILETIME GetLastWriteTime() const {
return impl->GetCurrentFindData().ftLastWriteTime;
};
};
struct AllResults
{
template <typename Recurse_T>
bool operator()(const FileData<Recurse_T>&) {
return true;
};
};
struct FilesOnly
{
template <typename Recurse_T>
bool operator()(const FileData<Recurse_T>& arg) {
return arg.IsFile();
};
};
#pragma warning(push)
#pragma warning(disable: 4355)
template <typename Filter_T, typename Recurse_T>
class DirectoryIterator : public boost::iterator_facade<DirectoryIterator<Filter_T>, const FileData<Recurse_T>, std::input_iterator_tag>
{
friend class boost::iterator_core_access;
boost::shared_ptr<Recurse_T> impl;
FileData<Recurse_T> derefData;
Filter_T filter;
void increment() {
do {
impl->increment();
} while (! filter(derefData));
};
bool equal(const DirectoryIterator& other) const {
return impl->equal(*other.impl);
};
const FileData<Recurse_T>& dereference() const {
return derefData;
};
public:
typedef FileData<Recurse_T> DataType;
friend struct DataType;
DirectoryIterator(Filter_T functor = Filter_T()) :
impl(boost::make_shared<Recurse_T>()),
derefData(this),
filter(functor) {
};
explicit DirectoryIterator(const std::wstring& pathSpec, Filter_T functor = Filter_T()) :
impl(boost::make_shared<Recurse_T>(pathSpec)),
derefData(this),
filter(functor) {
};
};
#pragma warning(pop)
}}
Si solo es encabezado, creo que la mejor opción es dividirlo en varios archivos. Como "detail/DirectoryIterator.hpp", etc., por lo menos no está contenido en un solo archivo. Esto es lo que hace Boost. Por cierto, Boost tiene una biblioteca de sistema de archivos, por si acaso no lo sabía. – GManNickG
@GMan: +1. Sí, boost tiene una biblioteca de sistema de archivos, pero está diseñada para compatibilidad multiplataforma que requiere abrir una lata de gusanos que no quiero (tiene su propio procesador de ruta, por ejemplo). Con esto, creo que puedo hacer que el código del cliente sea más claro y que no se preocupe por la portabilidad multiplataforma. –
Es bueno ver que está obteniendo un buen uso de: http://stackoverflow.com/questions/2531874/how-might-i-wrap-the-findxfile-style-apis-to-the-stl-style-iterator -pattern-in-c –