C++ 流畅的界面
C++ fluent interface
我正在为我的 C++ GUI 库实现流畅的界面。基本概念是每个函数都指向 return 指向模板的指针。
这里有两个class。
template <class T>
class wnd {
public:
T* set_id(int id) { id_ = id; return (T*)this; }
T* set_text(std::string text) { text_ = text; return (T*)this; }
protected:
int id_;
std::string text_;
};
class app_wnd : public wnd<app_wnd> {
public:
app_wnd* show() { std::cout << text_ << " " << id_; return (app_wnd*) this; }
};
效果很好。当使用 app_wnd 时,子函数 return 正确 T* 即 app_wnd*。因此我可以做 (new app_wnd())->set_id(0)->set_text("hello world")->show();
当我进行另一次推导时,问题开始了。
class main_wnd : public app_wnd {
public:
main_wnd* set_app_instance(int instance) { instance_ = instance; return (main_wnd *) this; }
protected :
int instance_;
};
这个 class 不知道底层模板,一旦我调用 set_id() 它 return 就是 app_wnd class, main_wnd 的丢失方法即 (new main_wnd())->set_id(0)->set_app_instance(10);
将失败,因为 set_id 函数将 return app_wnd* 而不是 main_wnd*.
有没有干净、漂亮、不复杂的解决方案?
你可以这样做:
template <class T>
class wnd {
public:
T* set_id(int id) { id_ = id; return (T*)this; }
T* set_text(std::string text) { text_ = text; return (T*)this; }
protected:
int id_;
std::string text_;
};
template<class Derivate>
class app_wnd : public wnd<Derivate> {
public:
app_wnd* show() { std::cout << wnd<app_wnd<Derivate>>::text_ << " " << wnd<app_wnd<Derivate>>::id_; return (Derivate*) this; }
};
class main_wnd : public app_wnd<main_wnd> {
public:
main_wnd* set_app_instance(int instance) { instance_ = instance; return (main_wnd *) this; }
protected :
int instance_;
};
int main(){
(new main_wnd())->set_id(0)->set_app_instance(10);
}
(受 CRTP 启发)
无论如何你将无法再实例化 app_wnd
感觉年轻了一些,因为我找到了答案on Stack Exchange。需要的是使用默认模板参数的修改后的 CRTP。这是 class 代码。
template <typename Q, typename T>
struct fluent_type {
typedef Q type;
};
template <typename T>
struct fluent_type<void, T> {
typedef T type;
};
template <typename T>
class wnd {
public:
typedef typename fluent_type<T, wnd<void>>::type ftype;
ftype* set_id(int id) { id_ = id; return static_cast<ftype*>(this); }
ftype* set_text(std::string text) { text_ = text; return static_cast<ftype*>(this); }
protected:
int id_;
std::string text_;
};
template <typename T = void>
class app_wnd : public wnd<app_wnd<T>> {
public:
typedef typename fluent_type<T, wnd<app_wnd<T> > >::type ftype;
ftype* show() { std::cout << text_ << " " << id_; return static_cast<ftype*>(this);
}
};
template <typename T = void>
class main_wnd : public app_wnd<main_wnd<T>> {
public:
typedef typename fluent_type<T, app_wnd<main_wnd<T> > >::type ftype;
ftype* set_app_instance(int instance) { instance_ = instance; return static_cast<ftype*>(this); }
protected :
int instance_;
};
下面是调用示例。
auto aw = (new app_wnd<>())
->set_id(0)
->set_text("hello app_wnd")
->show();
auto mw = (new main_wnd<>())
->set_id(0)
->set_text("hello main_wnd")
->show()
->set_app_instance(123);
我正在为我的 C++ GUI 库实现流畅的界面。基本概念是每个函数都指向 return 指向模板的指针。
这里有两个class。
template <class T>
class wnd {
public:
T* set_id(int id) { id_ = id; return (T*)this; }
T* set_text(std::string text) { text_ = text; return (T*)this; }
protected:
int id_;
std::string text_;
};
class app_wnd : public wnd<app_wnd> {
public:
app_wnd* show() { std::cout << text_ << " " << id_; return (app_wnd*) this; }
};
效果很好。当使用 app_wnd 时,子函数 return 正确 T* 即 app_wnd*。因此我可以做 (new app_wnd())->set_id(0)->set_text("hello world")->show();
当我进行另一次推导时,问题开始了。
class main_wnd : public app_wnd {
public:
main_wnd* set_app_instance(int instance) { instance_ = instance; return (main_wnd *) this; }
protected :
int instance_;
};
这个 class 不知道底层模板,一旦我调用 set_id() 它 return 就是 app_wnd class, main_wnd 的丢失方法即 (new main_wnd())->set_id(0)->set_app_instance(10);
将失败,因为 set_id 函数将 return app_wnd* 而不是 main_wnd*.
有没有干净、漂亮、不复杂的解决方案?
你可以这样做:
template <class T>
class wnd {
public:
T* set_id(int id) { id_ = id; return (T*)this; }
T* set_text(std::string text) { text_ = text; return (T*)this; }
protected:
int id_;
std::string text_;
};
template<class Derivate>
class app_wnd : public wnd<Derivate> {
public:
app_wnd* show() { std::cout << wnd<app_wnd<Derivate>>::text_ << " " << wnd<app_wnd<Derivate>>::id_; return (Derivate*) this; }
};
class main_wnd : public app_wnd<main_wnd> {
public:
main_wnd* set_app_instance(int instance) { instance_ = instance; return (main_wnd *) this; }
protected :
int instance_;
};
int main(){
(new main_wnd())->set_id(0)->set_app_instance(10);
}
(受 CRTP 启发)
无论如何你将无法再实例化 app_wnd
感觉年轻了一些,因为我找到了答案on Stack Exchange。需要的是使用默认模板参数的修改后的 CRTP。这是 class 代码。
template <typename Q, typename T>
struct fluent_type {
typedef Q type;
};
template <typename T>
struct fluent_type<void, T> {
typedef T type;
};
template <typename T>
class wnd {
public:
typedef typename fluent_type<T, wnd<void>>::type ftype;
ftype* set_id(int id) { id_ = id; return static_cast<ftype*>(this); }
ftype* set_text(std::string text) { text_ = text; return static_cast<ftype*>(this); }
protected:
int id_;
std::string text_;
};
template <typename T = void>
class app_wnd : public wnd<app_wnd<T>> {
public:
typedef typename fluent_type<T, wnd<app_wnd<T> > >::type ftype;
ftype* show() { std::cout << text_ << " " << id_; return static_cast<ftype*>(this);
}
};
template <typename T = void>
class main_wnd : public app_wnd<main_wnd<T>> {
public:
typedef typename fluent_type<T, app_wnd<main_wnd<T> > >::type ftype;
ftype* set_app_instance(int instance) { instance_ = instance; return static_cast<ftype*>(this); }
protected :
int instance_;
};
下面是调用示例。
auto aw = (new app_wnd<>())
->set_id(0)
->set_text("hello app_wnd")
->show();
auto mw = (new main_wnd<>())
->set_id(0)
->set_text("hello main_wnd")
->show()
->set_app_instance(123);