4

さまざまな種類のデータベース (sqlite、postgres など) を操作できるデータベース ラッパーを書きたいので、実際に使用しているデータベースに関係なく、ユーザーが書いているコードは変わりません。

私の頭では、これは次のような抽象基本クラスを呼び出します。

class database {
public:
    virtual bool query(const std::string &q) = 0;
    // Other stuff
};

class sqlite : public database {
public:
    bool query(const std::string &q) {
        // Implementation
    }
};

これは良さそうに見えますが、クエリ内で引数をエスケープするために可変個引数テンプレートを使用しています (そして、私はこのアイデアが本当に気に入っているので、それに固執したいと思います!)、残念ながら私の基本クラスはむしろ次のようになります:

class database {
public:
    template <typename... Args>
    bool query(const std::string &q, const Args &... args) {
        // Implementation
    }
};

ただし、テンプレート化された関数は仮想化できないため、これは抽象クラスの作成の妨げになります。これまでに思いついたのは、この構造だけです

template <class DatabaseType>
class database {
public:
    template <typename... Args>
    bool query(const std::string &q, const Args &... args) {
        return database_.query(q, args...);
    }
private:
    DatabaseType database_;
};

これは機能しているように見えますが、同じ名前の関数を呼び出すだけのすべてのラッパーではdatabase_、あまり良いスタイルとは思えません。これはここで選択したデザイン パターンですか、それともよりクリーンな、またはより慣用的な方法がありますか?

4

2 に答える 2

4

[...] よりクリーンな、またはより慣用的な方法はありますか

テンプレートでポリモーフィックな動作を実現するために、 CRTPを調べることができます。

template <class concrete_db> 
struct abstract_db { 
    template <typename... Args>
    void query(std::string const& q, Args const&... args) { 
        static_cast<concrete_db *>(this)->query(q, args...);
    }
};

struct postgres_db : abstract_db<postgres_db> {
    template <typename... Args>
    void query(std::string const& q, Args const&... args) { 
         // do something
    }
};
于 2012-05-02T19:49:58.827 に答える
4

関数への引数からクエリ オブジェクトを作成し、queryそれを仮想query_impl関数に渡すことができます。

于 2012-05-02T19:56:33.073 に答える