3

インスタンス メソッドまたはラムダ関数が必要です。または、select 関数の引数として scandir に渡されるのと同等です。これを行う方法はありますか?

私が達成しようとしている重要なことは、select 関数 (コールバック) が、それを呼び出すクラスのインスタンスごとに異なるパラメーターを参照するようにすることです。これをスレッドセーフにするために、またはひどく醜くならないようにするために、パラメーターをグローバル変数に格納することはできません。それがクラスインスタンスの目的です。

C++11 でラムダ関数を使用すると、次のようになります。

myclass:getFilesMatching(char startChar)
{
   ...
   mParam = startChar;
   auto lfunc = [this] (const struct dirent * dent) { return (*(dent->d_name) == mParam); };
   mNumFiles = scandir((char *)fullDirPath, &mfileList, lfunc, NULL);
}

これにより、名前が指定された文字で始まるすべてのファイルが取得されます。関数にローカル変数またはインスタンス変数を渡すかどうかは気にしません。

scandir 自体がスレッドセーフであることを願っています。もちろん、セマフォやミューテックスを使用することもできますが、それは本当に必要なのでしょうか?

もちろん、これは select 関数の単純な例にすぎません。私が実際にやりたいことはもっと複雑です。

4

3 に答える 3

3

私は、scandirまたは関連する C 関数の前知識がないことを認めなければなりません。しかし、ドキュメントを読んで理解したところによると、<dirent.h>これは下位レベルの API への複数の呼び出しをラップするヘルパー関数です。

このような場合、C++ のような API を使用して、同じ機能を実装する C++ ラッパーを作成することを好みます。

DIR適切にクリーンアップされるように、型のラッパーから始めます。

namespace cpp {

    struct DIR { 
        ::DIR * dir;

        DIR(const std::string & path) : dir(opendir(path.c_str()))
        {
            if(dir==0) throw std::runtime_error("Unable to open path");
        }

        ~DIR() { if(dir!=0) closedir(dir); } 

        operator ::DIR * () { return dir; }
    };
}

関数は次のscandirように実装できます。

template<class Filter, class Compare>
std::vector<dirent> scandir(const std::string & path, const Filter& filter, const Compare& compare) {
    cpp::DIR dir(path);
    std::vector<dirent> res;
    dirent entry, *entry_ptr = &entry;
    while( ::readdir_r(dir, &entry, &entry_ptr)==0 ) {
        if( entry_ptr==0 ) break;
        if( filter(entry) ) res.push_back(entry);
    }

    std::sort(begin(res), end(res), compare);

    return res;
}

そして、次のように呼び出します。

std::string path = ...;
...
auto filter = [] (const dirent& entry) { return entry.d_name[0]!='.'; };
auto compare = [](const dirent & lhs, const dirent & rhs) {
    return std::strcmp(lhs.d_name, rhs.d_name)<0; 
};

auto entries = cpp::scandir(path, filter, compare);

を使用するとreaddir_r()、上記の実装がスレッドセーフになりますが、それによって返されるエラーを報告するためにさらにチェックを追加する必要があります。

注: 上記のコードには、次のヘッダーを含める必要があります。

#include <dirent.h>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <vector>
于 2013-06-21T20:16:03.803 に答える
1

また、ラムダ式との結合も試みましたscandirが、興味深い結果が得られました。gcc (Debian 4.9 .2-10)。

#include <iostream>
#include <dirent.h>
#include <cstring>
using std::cout;
using std::endl;

char *ext;
unsigned int lext;

int main (int argc, char **argv)
{
    struct dirent **listing;
    ext = argv[2];
    lext = strlen (ext);
    int n = scandir (argv[1],
                     &listing,
                     [ext , lext] (const struct dirent *ent) -> int
                     {
                        // Select only files with extension EXT
                        auto lon = strlen (ent->d_name);
                        return (lon > lext  and
                                 !strcmp (ent->d_name + (lon-lext), ext))
                            ? 1 : 0;
                     },
                     alphasort);
    if (n==-1)
    {
        cout << "scandir has failed" << endl;
        return 1;
    }
    else {
        cout << "scandir has found "<< n <<" matching entries.\n";
        for (int i=0 ; i<n ; ++i)
            cout << i+1 << " - "<< listing[i]->d_name << "\n";
        return 0;
    }
}

キャプチャ リスト変数のスコープを修正してブロック ローカルにしようとすると、次のエラーでコンパイルが拒否されます。

エラー:引数 '3' を 'int scandir(const char*, dirent***, int ( ) (const dirent ), int (*)(const dirent**, const dirent**))'</p>

これは、ラムダを関数にコンパイルできることを意味しているように思われるので、それへのポインタを機能する方法で与えることができますscandir。ただし、他の2つの回答のC++アプローチを好みます。

これがコンパイラのバグと見なされるべきかどうかはわかりません。

于 2015-11-08T19:12:29.590 に答える