スポンサーサイト返却値の扱い

Home > ----- / スポンサー広告 > This Entry 2008-08 / NTTL > This Entry [com : 2][Tb : 0]

--------

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

2008-08-29

前回予告したとおり、今回は関数の返却値の扱いについての話題です。

例外処理がない環境では、関数の返却値で失敗を呼び出し元に通知するのが最も自然な設計です。しかし、利便性を考慮すれば、関数の返却値は本来の出力を表すほうがよいのも確かです。この両方を満たすための欲張りなクラステンプレートを用意することにしましょう。


namespace nttl {
  template <typename T>
  struct result
  {
    T value;
    result(T const& arg) : value(arg) {}
    operator bool() const { return !bad(value); }
  };
}

このクラステンプレートは、次のように使います。

if (nttl::result<Foo> foo = func())
{
  // 成功時の処理
}
else
{
  // 失敗時の処理
}

nttl::result は、nttl::bad(value) がゼロのときに true を返す変換関数を持っていますので、上のように if 文などの条件で、宣言と条件判定を同時に行うことができます。nttl::bad と論理が逆なのは、その方が直感的だからです。

しかし、実際には if 文の then 節でエラー処理をすぐに行いたいことも多いことでしょう。それには、

namespace nttl {
  template <typename T>
  struct not
  {
    T value;
    not(T const& arg) : value(arg) {}
    operator bool() const { return bad(value); }
  };
}

を用意し、

if (nttl::not<Foo> foo = func())
{
  // 失敗時の処理
}

とすることができます。

返却値が、関数の成否を表す状態を保持しないような場合には、std::pair を用いるなどして、成否とともに返すことになるかと思います。

前回紹介した nttl::bad といい、今回の nttl::resultnttl::not といい、なぜこのようなことをするかというと、シンタックスを統一する必要があるからです。関数テンプレートの中で、エラー処理を記述するには、関数のエラー通知方法が統一されていなければなりません。すべての関数を新規設計するならよいのですが、そうでなければ既存の関数も含めてシンタックスを統一できる方法がいるのです。

また、コピー1回分のオーバーヘッドを許容できるのであれば、この方法を用いることで、コンストラクタから例外を送出できない問題点を解消できます。つまり、オブジェクトの生成は、必ず生成関数を用いて一時オブジェクトを返すようにすれば、if 文の中だけでオブジェクトを有効にすることが簡単に実現できます。

if (nttl::result<Foo> foo = create_Foo())
{
  // この中だけで foo は有効
}

if 文を抜ける際には、もちろん foo のデストラクタが呼び出されますので、後始末を自動化することができます。

Comment

う~ : 2008-08-29(Fri) 12:33 URL edit
operator bool() があると意図せず整数値に変換できてしまうので Safe bool イディオムを使った方がいいと思います。
http://ja.wikibooks.org/wiki/More_C%2B%2B_Idioms/%E5%AE%89%E5%85%A8%E3%81%AA_bool(Safe_bool)


> 返却値が、関数の成否を表す状態を保持しないような場合には、std::pair を用いるなどして、成否とともに返すことになるかと思います。

こんなイメージでしょうか…

namespace hoge {
struct detail { int error_code; };

template<typename T>
int bad(std::pair<T, detail> const& result)
{
return result.second.error_code;
}
}

bad 関数を定義する名前空間には気を付けなければいけませんね。
std::pair<int, int> だと std 名前空間の中に bad 関数を定義するわけにもいきませんし…(nttl に入れればいいのかな?)。
これってADL(argument-dependent lookup) の知識が必須ですが、そもそも ADL を理解しているC++プログラマはそれほど多くないのではないかと…思ってしまったりなんかします。

あと、自分なら、not という名前よりも failed の方がしっくりきますね。
たかぎ : 2008-08-29(Fri) 20:39 URL edit
Safe boolを使った場合のオーバーヘッドは許容できるでしょうかね?

> nttl に入れればいいのかな?

badはnttl名前空間に入れなければなりません。

> これってADL(argument-dependent lookup) の知識が必須ですが、そもそも ADL を理解しているC++プログラマはそれほど多くないのではないかと…思ってしまったりなんかします。

建前だけをいえば、C++はCと同じで、プログラマは言語仕様を理解していることは前提ですので...。
今回の場合は、nttl::badとすることが必須なので、それほど問題にはならないかと思います。
nttl::stringやnttl::vectorを作り出すとADLの問題も出てきますが、intやdoubleはstd::absとしなければならず、std::complex<double>はabsだけでよいのと状況は同じですので、そんなに問題はないでしょう(本当かな?)。

> あと、自分なら、not という名前よりも failed の方がしっくりきますね。

よく考えてみると、notはC++のキーワードなので使えませんね( ! の代替表記)。
もともと、nttl::not<nttl::result<Foo> > のような使い方を考えていたのですが、記述が面倒なのでnotだけにしました。
failed にすると、falseを返す場合は値が有効なので、failed だけどセーフという変な状況ができてしまいます。
名前については再検討してみます。
Post a Comment









管理者にだけ表示を許可

Trackback

http://cppemb.blog17.fc2.com/tb.php/112-3d2800e2

C++と組み込み環境 | Page Top▲

New >>
返却値の扱い (2)
<< old
例外を使わないテンプレートライブラリ
ブログ内検索
RSSフィード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。