对于仅包含头文件的库来说,这样的代码是否太多了?
-
25-09-2019 - |
题
看来我必须在这里内联相当多的代码。我想知道将其完全留在头文件中是否是不好的设计实践,如下所示:
#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)
}}
解决方案
我在我的一些更多的代码,如果任何安慰的说的。等做所有C ++标准库实现中,升压和微软(例如,ATL)。
其他提示
这令我开口于多的问题是在DirectoryIteratorImpl
的函数的实现的唯一部分。这不是一个模板,所以它并不真的要在一个头,而且它有几个稍微长一些例程(“真实”的构造和增量)。
的其余部分是你想要他们要么模板否则这样的琐碎函数组成内嵌在任何情况下(例如,FileData
的成员)。那些将在任何情况下,一个标题中结束。
至于头部的长度的话,你可以有你想在你的头文件,如过多的代码。这种交易是必须每个程序是建立时间重新编译代码的数量;放置在你的CPP文件中的代码可被编译成目标文件并在每个后续的构建链接。
我建议每一个用于DirectoryIteratorImpl
方法定义的应该被移动到一个.cpp
文件。如果你没有定义一个类定义的方法内联,没有理由为它包含在头文件。
这是不相关的旁白:避免写inline DirectoryIteratorImpl();
- 实际上写你的内联函数内联,或不在线将它们标记。从 C ++ FAQ精简版:
它通常是至关重要的是,函数定义(之间的部分的{...}) 被放置在头文件。如果将内联函数的定义放在.cpp 文件,如果它是从其他一些.cpp文件调用,你会得到一个“无法解析的外部” 从连接错误。
如果你的职责是“太大”在头文件中写,他们是太大内联和编译器可能会忽略反正你的网线的建议。
看来您正在为 Windows 编程,我们可以假设您正在使用 Visual Studio 吗?
无论如何,我不认为标头中有太多代码。
这主要是一个权衡的问题:
- 编译速度较慢(但我们有多核和预编译头)
- 更频繁的重新编译(同样是多核)
- 也许代码膨胀...
唯一令人烦恼的一点(在我看来)是最新的......我在这里需要帮助:我们确定这些函数将被内联吗?编译器和链接器是否有可能决定不内联它们并将它们转换为常规调用?
坦白说,我不会对此太担心。一些 Boost
即使对于非模板部分,库也仅包含头文件,因为它使集成更容易(不需要链接)。