これはヘッダーのみのライブラリとしてはコードが多すぎますか?
-
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 ++標準ライブラリの実装を行い、ブーストおよびMicrosoft(例えば、ATL)ます。
他のヒント
DirectoryIteratorImpl
での関数の実装だろう多くの質問に開いているとして私を打つ部分のみ。それは本当にヘッダーである必要はありませんし、それはやや長いルーチン(「リアル」コンストラクタおよび増分)のカップルを持っているので、それはテンプレートではありません。
残りの部分はあなたがそれらをしたいと思い、このような些細な機能で構成される他のいずれかのテンプレートやインラインいずれにしてもいる(例えば、FileData
のメンバー)。それらは、いずれの場合にヘッダーになってしまいます。
限り、ヘッダの長さが行くように、あなたがあなたのヘッダファイルに好きな多くのコードとして持つことができます。トレードオフは、あなたのプログラムが構築されるたびに再コンパイルされなければならないコードの量です。あなたのCPPファイルに配置されたコードは、オブジェクトファイルにコンパイルされ、後続の各ビルドの上にリンクすることができます。
私はDirectoryIteratorImpl
ためのメソッド定義のそれぞれが.cpp
ファイルに移動する必要があることを示唆しています。あなたはクラス定義の内部でメソッドをインラインで定義していない場合、それはヘッダファイルに含まれるために理由はありません。
脇無関係:避け書き込みinline DirectoryIteratorImpl();
- 実際にインラインであなたのインライン関数を記述、またはインラインそれらをマークしません。 : C ++よくある質問LiteとするからP>
これは、関数の定義({...}との間の部分)は通常不可欠です ヘッダファイルに配置します。あなたは.cppファイルにインライン関数の定義を置く場合 ファイル、そしてそれは他のいくつかの.cppファイルから呼び出された場合、あなたは「未解決の外部」を取得します リンカからのエラー。
あなたの関数はヘッダファイルに書き込むには「大きすぎる」であれば、彼らはインラインには大きすぎるしていると、コンパイラはおそらくとにかくあなたのインライン提案を無視します。
ここでは Windows 用にプログラミングしているようですが、Visual Studio を使用していると仮定しましょうか?
とにかく、ヘッダーにそれほど多くのコードがあるとは思えません。
それは主にトレードオフの問題です。
- コンパイルが遅くなります (ただし、マルチコアとプリコンパイル済みヘッダーがあります)
- より頻繁な再コンパイル (やはりマルチコア)
- おそらくコードが肥大化している可能性があります...
(私が思うに)唯一気になる点は、最新のものです...ここで助けが必要になります:関数がインライン化されることは確かですか。コンパイラとリンカーが関数をインライン化せずに通常の呼び出しに変換することを決定する可能性はありませんか?
率直に言って、それについてはあまり心配する必要はありません。いくつかの Boost
ライブラリは、テンプレート以外の部分であってもヘッダーのみです。これは単に、統合が容易になる (リンクが不要になる) ためです。