9

私は Windows 用の GUI ライブラリを開発してきました (個人的なサイド プロジェクトとして、実用性は期待していません)。私のメイン ウィンドウ クラスでは、オプション クラスの階層を ( Named Parameter Idiomを使用して) セットアップしました。これは、いくつかのオプションが共有され、他のオプションが特定の種類のウィンドウ (ダイアログなど) に固有であるためです。

名前付きパラメーターのイディオムが機能する方法では、パラメーター クラスの関数は、呼び出されたオブジェクトを返す必要があります。問題は、階層内で、それぞれが異なるクラス (createWindowOpts標準ウィンドウのcreateDialogOptsクラス、ダイアログのクラスなど) でなければならないことです。すべてのオプション クラス テンプレートを作成することで、これに対処しました。次に例を示します。

template <class T>
class _sharedWindowOpts: public detail::_baseCreateWindowOpts {
    public: ///////////////////////////////////////////////////////////////
    // No required parameters in this case.
    _sharedWindowOpts() { };

    typedef T optType;

    // Commonly used options
    optType& at(int x, int y) { mX=x; mY=y; return static_cast<optType&>(*this); }; // Where to put the upper-left corner of the window; if not specified, the system sets it to a default position
    optType& at(int x, int y, int width, int height) { mX=x; mY=y; mWidth=width; mHeight=height; return static_cast<optType&>(*this); }; // Sets the position and size of the window in a single call
    optType& background(HBRUSH b) { mBackground=b; return static_cast<optType&>(*this); }; // Sets the default background to this brush
    optType& background(INT_PTR b) { mBackground=HBRUSH(b+1); return static_cast<optType&>(*this); }; // Sets the default background to one of the COLOR_* colors; defaults to COLOR_WINDOW
    optType& cursor(HCURSOR c) { mCursor=c; return static_cast<optType&>(*this); }; // Sets the default mouse cursor for this window; defaults to the standard arrow
    optType& hidden() { mStyle&=~WS_VISIBLE; return static_cast<optType&>(*this); }; // Windows are visible by default
    optType& icon(HICON iconLarge, HICON iconSmall=0) { mIcon=iconLarge; mSmallIcon=iconSmall; return static_cast<optType&>(*this); }; // Specifies the icon, and optionally a small icon
    // ...Many others removed...
};

template <class T>
class _createWindowOpts: public _sharedWindowOpts<T> {
    public: ///////////////////////////////////////////////////////////////
    _createWindowOpts() { };

    // These can't be used with child windows, or aren't needed
    optType& menu(HMENU m) { mMenuOrId=m; return static_cast<optType&>(*this); }; // Gives the window a menu
    optType& owner(HWND hwnd) { mParentOrOwner=hwnd; return static_cast<optType&>(*this); }; // Sets the optional parent/owner
};

class createWindowOpts: public _createWindowOpts<createWindowOpts> {
    public: ///////////////////////////////////////////////////////////////
    createWindowOpts() { };
};

動作しますが、ご覧のとおり、かなりの量の追加作業が必要です。各関数の戻り値の型の型キャスト、追加のテンプレート クラスなどです。

私の質問は、この場合、すべての余分なものを必要としない名前付きパラメーター イディオムを実装する簡単な方法はありますか?

4

6 に答える 6

11

あなたが聞きたいことではないかもしれませんが、安全生活を営む限り、クライアントから(多かれ少なかれ)隠されているライブラリコードに多くの醜い型キャストとテンプレートパラメータがあっても大丈夫だと思いますクライアントのより簡単に。ライブラリ コードの美しさは、コード自体にあるのではなく、クライアントが記述できるようにするコードにあります。たとえば、STL を見てみましょう。

私はまた、基本的にあなたと同じ願望を持つ個人的なプロジェクトとして小さな GUI ライブラリを開発しました。コードの一部はかなり見苦しくなりますが、最終的には美しいクライアント コードを書くことができます (少なくとも私の (おそらく倒錯した)目)そしてそれが私見を数えるものです。

于 2008-10-17T07:24:29.067 に答える
5

どうですか…?

template <class T>
class _sharedWindowOpts: public detail::_baseCreateWindowOpts {

protected: // (protected so the inheriting classes may also use it)

    T & me() { return static_cast<T&>(*this); }               // !

public:
    // No required parameters in this case.
    _sharedWindowOpts() { };

    typedef T optType;

    // Commonly used options
    optType& at(int x, int y) { mX=x; mY=y; return me(); };   // !
    // ...
};
于 2008-10-17T10:08:17.393 に答える
2

継承の逆順でメソッド呼び出しをチェーンすることはできますか?

あなたの例では、次のようなことをします

ウィンドウ window = CreateWindow("foo").menu(hmenu).owner(hwnd).at(0,0).background(hbr);

100% 透明ではないことはわかっていますが、少し簡単でほぼ正しいようです。

于 2008-10-17T03:16:12.233 に答える
1

この回答に満足しているかどうかはわかりませんが、テンプレートの引数推定を使用する可能性があります。 : 私は自分のコンパイラを持っていません。他の誰かが試してみたいと思わない限り、明日再確認します。

class sharedWindowOpts
{
public:

  sharedWindowOpts() {};

  // Commonly used options
  template <class optType>
  static optType& at(int x, int y, optType& opts) { opts.mX=x; opts.mY=y; return opts; };

  template <class optType>
  static optType& background(HBRUSH b, optType& opts) { opts.mBackground=b; return opts; };

  // etc...
}

class createWindowOpts : public sharedWindowOpts
{
public:
  createWindowOpts() : sharedwindowOpts() {};

  // These can't be used with child windows, or aren't needed
  template <class optType>
  static optType& menu(HMENU m, optType& opts) { opts.mMenuOrId=m; return opts; };

  template <class optType>
  static optType& owner(HWND hwnd, optType& opts) { opts.mParentOrOwner=hwnd; return opts; };
 }

次に、次のように CreateWindow を呼び出します。

CreateWindow( createWindowOpts::owner(hwnd,
              createWindowOpts::at(0, 100,     // can use createWindowOpts because it doesn't hide sharedWindowsOpts::at
              createWindowOpts::menu(hmenu, createWindowOpts() ) ) ) );

もちろん、これに関する不快な点は、静的メソッド呼び出し構文とすべての余分な括弧を使用する必要があることです。静的メンバー関数を非メンバー関数に置き換えると、これを排除できます。ただし、型キャストと追加のテンプレート クラスは回避されます。

個人的には、私のようにライブラリがどこでも使用されているよりも、あなたのメソッドのように奇妙なコードをライブラリに入れたいと思います。

于 2008-10-17T04:48:03.390 に答える
0

1 年遅れて 1 ドル不足していることは承知していますが、とにかく自分のソリューションを提案します。

//////// Base.. 

template<typename DerivedBuilder, typename Options>
class Builder
{
protected:
    Builder() {}
    DerivedBuilder& me() { return *static_cast<DerivedBuilder*>(this); }

    Options options;
};


//////////////////////////       A       //////////////////////////


class Options_A
{
public:
    Options_A() : a(7) {}
    int a;
};

class Builder_A;

class A 
{
public:
    virtual ~A() {}
    virtual void print() { cout << "Class A, a:" << a << endl; }

protected:
    friend class Builder_A;
    A(const Options_A& options) : a(options.a) {}
    int a;
};



template<typename DerivedBuilder, typename Options = Options_A>
class BuilderT_A : public Builder<DerivedBuilder, Options>
{
public:
    using Builder<DerivedBuilder, Options>::options;
    using Builder<DerivedBuilder, Options>::me;
    DerivedBuilder& a(int p) { options.a = p; return me(); }
};


class Builder_A : public BuilderT_A<Builder_A>
{
public:
    shared_ptr<A> create()
    {
        shared_ptr<A> obj(new A(options));
        return obj;
    }
};

//////////////////////////      B       //////////////////////////



class Options_B : public Options_A
{
public:
    Options_B() : b(8) {}
    int b;
};

class Builder_B;

class B : public A 
{
public:
    virtual ~B() {}
    virtual void print() { cout << "Class B, a:" << a << ", b:" << b << endl; }

protected:
    friend class Builder_B;
    B(const Options_B& options) : A(options), b(options.b) {}
    int b;
};


template<typename DerivedBuilder, typename Options = Options_B>
class BuilderT_B : public BuilderT_A<DerivedBuilder, Options>
{
public:
    using Builder<DerivedBuilder, Options>::options;
    using Builder<DerivedBuilder, Options>::me;
    DerivedBuilder& b(int p) { options.b = p; return me(); }
};


class Builder_B : public BuilderT_B<Builder_B>
{
public:
    shared_ptr<B> create()
    {
        shared_ptr<B> obj(new B(options));
        return obj;
    }
};



//////////////////////////       C       //////////////////////////



class Options_C : public Options_B
{
public:
    Options_C() : c(9) {}
    int c;
};

class Builder_C;

class C : public B 
{
public:
    virtual ~C() {}
    virtual void print() { cout << "Class C, a:" << a << ", b:" << b << ", c:" << c << endl; }

protected:
    friend class Builder_C;
    C(const Options_C& options) : B(options), c(options.c) {}
    int c;
};


template<typename DerivedBuilder, typename Options = Options_C>
class BuilderT_C : public BuilderT_B<DerivedBuilder, Options_C>
{
public:
    using Builder<DerivedBuilder, Options>::options;
    using Builder<DerivedBuilder, Options>::me;
    DerivedBuilder& c(int p) { options.c = p; return *static_cast<DerivedBuilder*>(this); }
};


class Builder_C : public BuilderT_C<Builder_C>
{
public:
    shared_ptr<C> create()
    {
        shared_ptr<C> obj(new C(options));
        return obj;
    }
};





///////////////////////////////////////////////////////////////////////////


int main()
{
    shared_ptr<A> a = Builder_A().a(55).a(1).create();
    a->print();

    shared_ptr<B> b = Builder_B().b(99).b(2).a(88).b(4).a(2).b(3).create();
    b->print();

    shared_ptr<C> c = Builder_C().a(99).b(98).c(97).a(96).c(6).b(5).a(4).create();
    c->print();

    return 0;
}

/* Output:

Class A, a:1
Class B, a:2, b:3
Class C, a:4, b:5, c:6

*/

C は B から派生し、B は A から派生します。パラメーターを繰り返して、任意の順序で配置できることを示しました。

于 2010-05-11T00:52:05.937 に答える
0

テンプレートはホットです。

しかし、POP (Plain old Polymorphism) は死んだわけではありません。

(スマート) ポインターをサブクラスに返さないのはなぜですか?

于 2008-10-17T11:17:17.817 に答える