Reverse std::binary_function parameters
為了繼續寫某一篇自去年年中[1]開始寫,到現在還沒完工的文章,不得已,只好把其中一部分岔開來,另外開了這篇,以免太複雜。那篇文章之所以會拖這麼久,當然是因為斷斷續續地寫,以及其中的技術我尚未完全掌握的緣故啦。
言歸正傳,C++ 的 <functional> 的概念很好用,可是標準裡有提供的功能,實在是太少了,所以常常會發生,要兜卻兜不出來,只好退回去全部用迴圈重寫。碰到這樣的情形,又無法拋棄 C++ 的偏執的話,頗令人沮喪。所以有機會的話,最好還是自己把缺的東西刻一刻,累積下來,希望總有一天能夠快快樂樂用 C++,無憂無慮沒煩惱。
這裡介紹的 functor,正是最近為了那篇還沒寫完的文章而刻的一個 functor。設這個 functor 叫做 g,g 要做的事情很簡單,就是把要傳給另外一個 functor f 的兩個參數先對調之後,再傳給 f。也就是說,本來是 f(lhs, rhs) 這樣子呼叫,透過 g,變成 f(rhs, lhs) 的呼叫方式。
概念很簡單,所以實做起來也很簡單,其實作與 helper function 如下:
template <class Op>class param_reverser : public binary_function<typename Op::second_argument_type, typename Op::first_argument_type, typename Op::result_type>{public:
param_reverser(Op op) : op_(op) { }
typename Op::result_type operator()(const typename Op::second_argument_type& lhs, const typename Op::first_argument_type& rhs) const { return op_(rhs, lhs); }
private:
Op op_;
}; // class param_reverser<>
template <class Op>param_reverser<Op> param_reverse(Op op){ return param_reverser<Op>(op);}
用以下的程式測試:
struct digits_printer : public binary_function<int, int, void>{ void operator()(int lhs, int rhs) const { cout << " [" << lhs << ", " << rhs << "]"; }};
int main(){ cout << "Let's try (3, 4) => (4, 3) first." << endl; param_reverse(digits_printer())(3, 4); cout << endl;
int xs[] = { 10, 11, };
cout << "Without parameter reverse: 2 should be at the second position." << endl; for_each( xs, xs + (sizeof(xs) / sizeof(xs[0])), bind2nd(digits_printer(), 2) ); cout << endl;
cout << "With parameter reverse: 2 should be at the first position." << endl; for_each( xs, xs + (sizeof(xs) / sizeof(xs[0])), bind2nd(param_reverse(digits_printer()), 2) ); cout << endl;
return 0;}
其執行結果如下:
SHELL> ./a.outLet's try (3, 4) => (4, 3) first. [4, 3]Without parameter reverse: 2 should be at the second position. [10, 2] [11, 2]With parameter reverse: 2 should be at the first position. [2, 10] [2, 11]
耶,講完了,這麼快。好吧,我承認這篇只是為了要把程式碼另外列出來,以免那篇未完成的文章過長且失焦罷了。
- 西元 2005 年 6 月。 ↩
10 Comments
既然用了 OPerator 指涉要被包裹的 functor,是否這個 functor 的名字,改取做 operand_reverser 比較好呢?
即然用了 template, 就要盡可能利用它的各種優點,比如說....抽像性! 這邊我指的是型別名稱的抽像概念:
以 digits_printer 來說,我會把你原來寫的
void operator()(int lhs, int rhs) const;
改成
result_type operator()(first_argument_type lhs, second_argument_type);
這樣若你以後改變 template parameter 時,就只要改一個地方了。
看一看....唔,應該用 const& 才對,因此就變成:
result_type operator()(const first_argument_type& lhs, const second_argument_type& rhs);
有點長 :p
av,
您好。是的,我有注意到「應該盡可能的抽象化 template code」這個要點。因此,真正要成為 library 給人呼叫的 param_reverser,便會依據給定的 concrete functor,推導出 first_argument_type、second_argument_type 與 result_type 為何。見 param_reverser 如何繼承自 binary_function。
而身為 concrete functor 的 digits_printer,既然只是作為範例程式而存在,因此我也就沒有使其盡可能的彈性了。
嗯,的確,如果是以 functor 的入門教學為目的,還是簡單點好,免得嚇跑太多人。強大的 template 已經嚇跑太多人了,不懂得利用 template 真是太可惜了。
參考一下用 Boost.Lambda 的作法:
#include
#include
using namespace std;
struct digits_printer : public binary_function
{
void operator()(int lhs, int rhs) const
{
cout
#include
using namespace boost;
using namespace boost::lambda;
int main()
{
cout (4, 3) first." (digits_printer(), _2, _1)(cref(3), cref(4));
cout
貼壞了, 還是自己下來看吧. Click here to view source.
fr3@K,
感謝分享。
Boost 啊,真是令人又愛又恨呢……:很想用,但也要部門裡所有人都看得懂才行啊。如果再加上「porting 到 embedded system 上出問題怎麼解」這個問題,那就更麻煩了。唉~
Jeff Hung
Jeff,
> 很想用,但也要部門裡所有人都看得懂才行啊
那是不是也不用 C++ Standard Library 呢? 不太可能吧. 我個人覺得從 C++ Standard Library 到 Boost 的確有一道溝, 但也沒有要跨那麼大的一步.
在 99% 的情況下, 用這兩個高品質的 library 會比使用你我手工打造的東西更高效率與 error free.
看不懂就給他些自學 pointer, 自學不會就教, 教不會就輔導改行 (從軟體工程師轉行為軟體作業員?) :P
> 再加上「porting 到 embedded system 上出問題怎麼解」
其他 (embedded system 上的) software 出了問題怎麼辦? 如果是 FOSS 還有機會看看 source code 自己修修看. 也可以求助於 mailing list/irc and/or fill bug report. 其實我不是很確定你的這句話的意思, 上面這些方法不適用於 Boost 嗎? 好奇中.
從 C 到 C++,確實要跨很大一步。XD
啊~~ 了解~~
Post a Comment