更好的方法使用C ++命名参数成语?

时间:2008-10-17 02:37:15

标签: c++ named-parameters

我一直在为Windows开发一个GUI库(作为个人辅助项目,没有用处的愿望)。对于我的主窗口类,我已经设置了一个选项类的层次结构(使用Named Parameter Idiom),因为有些选项是共享的,而其他选项是特定于特定类型的窗口(如对话框)。

命名参数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() { };
};

它可以工作,但正如你所看到的,它需要大量的额外工作:每个函数的返回类型的类型转换,额外的模板类等等。

我的问题是,在这种情况下,是否有更简单的方法来实现命名参数成语,不需要所有额外的东西?

6 个答案:

答案 0 :(得分:9)

也许不是你想听到的,但是我觉得在库代码中有很多丑陋的类型转换和模板参数,或多或少地隐藏在客户端中,只要< / em>它是安全的使客户的生活更容易。库代码中的优点不在于代码本身,而是在代码中它使客户端能够编写代码。以STL为例。

我还开发了一个小型GUI库作为个人项目,与你的愿望基本相同,其中一些代码变得非常丑陋,但最终它允许我编写漂亮的客户端代码(至少在我的(可能是变态的)眼中)这就是恕我直言。

答案 1 :(得分: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(); };   // !
    // ...
};

答案 2 :(得分:2)

你能用反向的继承顺序链接方法调用吗?

所以在你的例子中你会做类似

的事情

窗口窗口= CreateWindow(“foo”)。menu(hmenu).owner(hwnd).at(0,0).background(hbr);

我意识到它不是100%透明,但似乎更容易,几乎是正确的。

答案 3 :(得分: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() ) ) ) );

当然,关于这一点的令人讨厌的事情是必须使用静态方法调用语法和所有额外的括号。如果使用非成员函数替换静态成员函数,则可以将其删除。但它确实避免了类型转换和额外的模板类。

就我个人而言,我宁愿在你的方法中使用库中的奇怪代码,而不是像我一样使用库。

答案 4 :(得分:0)

模板很热。

但POP(普通老式多态性)并没有死亡。

为什么不返回一个(智能)指针到子类?

答案 5 :(得分:0)

我知道我迟到了一年,短了一美元,但无论如何我会投入我的解决方案。

//////// 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.我重复参数以表明它们可以按任何顺序排列。