任意の expression に透過的に hook を仕掛けるようなマクロが書けるのではありませぬか?

以下のコードでやっていることの説明.

EXCEPTION_HOOK((expr))

と書いておけば, (expr) という C++ の式を実行したら急に例外が来たので……というときに, call stack のようなもので殴られた跡 (この hook が仕掛けられている場所に関する情報) を boost::exception に乗せていきます. call stack の生成ぐらい言語でサポートして欲しいよね,うんうん.終わり. Microsoft Visual C++ 2010 と GCC 4.5.0 で動作を確認済みです.

工夫として,

EXCEPTION_HOOK((expr))

という文字列が式 expr と同等な C++expression として透過的に機能するように見せかけてあります.つまり,たとえば

std::string s("42");
int i = EXCEPTION_HOOK((boost:lexical_cast<int>(s)));

などと書けるようにしてあり,もちろん例外が通知されなければこのコードは

std::string s("42");
int i = boost::lexical_cast<int>(s);

と同じ挙動を示します.終わり.

透過性という点で重要な別の特徴として, preprocessing-time でこの hook を完全に無効化できます.無効化して,字句レベルで (expr) という構文として振舞わせるのは赤子の手をひねる (それをするなんてとんでもない!) よりも造作のないことよ,クックック…….

以下のコードの要点 ([&]lambda-introducer を伴う lambda-expression を用いて, hook を仕掛けた場所のスコープの文脈を持ち込みつつコールバック化する点) さえ抑えておけば,任意の expression に対して,透過的に preprocessing-time, compile-time, runtime レベルの pre-hook, post-hook および例外送出時 hook を仕掛けるようなマクロ (「えーマジマクロ?」「マクロが許されるのは……」) を構築できるんじゃないんでしょうか,知らんけど?

細かいこと言うと hook を仕掛ける対象となる式の型が void でない場合は MoveConstructible でないといけないという制約がががが.ただし,過去,似たようなものはいくつか提示されていますが (Bjarne が昔書いていた operator-> で hook を書く記事どこいったっぽ? *1 ) ,「hook を仕掛ける構文がそのまま expression として透過的に扱える」かつ「例外送出イベントにも hook を仕掛けられる」というのを C++ で達成したものはなかったはずなのでにょほほほほ.

以下,とても長〜いコードと出力例.

#include <boost/config.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/type_traits/is_array.hpp>
#include <boost/type_traits/is_function.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/exception/error_info.hpp>
#include <boost/exception/exception.hpp>
#include <boost/exception/info.hpp>
#include <boost/exception/get_error_info.hpp>
#include <boost/lexical_cast.hpp>

#include <string>
#include <vector>
#include <iostream>


class hook_stack
{
private:
  class Element
  {
  public:
    Element(char const *function,
            char const *file,
            int line,
            char const *expression)
      : function_(function),
        file_(file),
        line_(line),
	expression_(expression)
    {}

    // Compiler-generated cctor, dtor and assignment are fine.                                                                                               

    char const *getFunction() const
    { return function_; }

    char const *getFile() const
    { return file_; }

    int getLine() const
    { return line_; }

    char const *getExpression() const
    { return expression_; }

  private:
    char const *function_;
    char const *file_;
    int line_;
    char const *expression_;
  };

public:
  hook_stack()
    : stk_()
  {}

  void push(char const *function,
            char const *file,
            int line,
            char const *expression)
  {
    stk_.push_back(Element(function, file, line, expression));
  }

  friend std::ostream &operator<<(std::ostream &os, hook_stack const &x)
  {
    for (auto iter = x.stk_.begin(); iter != x.stk_.end(); ++iter) {
      os << "from\n";
      os << "  function  : " << iter->getFunction() << "\n";
      os << "  file      : " << iter->getFile() << "\n";
      os << "  line      : " << iter->getLine() << "\n";
      os << "  expression: " << iter->getExpression() << "\n";
    }
    return os;
  }

private:
  std::vector<Element> stk_;
}; // class hook_stack                                                                                                                                       

struct hook_stack_{};

typedef boost::error_info<hook_stack_, hook_stack> errinfo_hook_stack;

namespace detail {

/*                                                                                                                                                           
関数呼び出し構文・演算子の場合                                                                                                                               
  T -> remove_cv<T>::type                                                                                                                                    
  T & -> T &                                                                                                                                                 
  T && -> remove_cv<T>::type                                                                                                                                 
関数呼び出し構文・演算子以外の場合                                                                                                                           
  T & -> T & (lvalue)                                                                                                                                        
  T -> remove_cv<T>::type (rvalue)                                                                                                                           
*/
template<class T>
struct exception_hook_type_impl
  : public boost::remove_cv<T>
{
  static_assert(!boost::is_array<T>::value, "");
  static_assert(!boost::is_function<T>::value, "");
}; // struct exception_hook_type_impl                                                                                                                        

template<class T>
struct exception_hook_type_impl<T &&>
  : public boost::remove_cv<T>
{};

template<class R, class F>
R exception_hook_impl(char const *function,
                      char const *file,
                      int line,
                      char const *expression,
                      F f)
try {
  return f();
}
catch (boost::exception &e) {
  hook_stack *p = boost::get_error_info<errinfo_hook_stack>(e);
  if (!p) {
    e << errinfo_hook_stack(hook_stack());
    p = boost::get_error_info<errinfo_hook_stack>(e);
    char const * *p_throw_function = boost::get_error_info<boost::throw_function>(e);
    char const * *p_throw_file = boost::get_error_info<boost::throw_file>(e);
    int *p_throw_line = boost::get_error_info<boost::throw_line>(e);
    p->push(!!p_throw_function ? *p_throw_function : "N/A",
            !!p_throw_file ? *p_throw_file : "N/A",
            !!p_throw_line ? *p_throw_line : 0,
            "N/A");
  }
  p->push(function, file, line, expression);
  throw;
}

} // namespace detail                                                                                                                                        

// Workaround for MSVC (MSVC does not allow needless typename keywords)
#if defined(BOOST_MSVC)
#define EXCEPTION_HOOK(PARENTHESIZED_EXPRESSION)                                                                                      \
  ::detail::exception_hook_impl< ::detail::exception_hook_type_impl<decltype(PARENTHESIZED_EXPRESSION)>::type>(                       \
  __FUNCTION__,                                                                                                                       \
  __FILE__,                                                                                                                           \
  __LINE__,                                                                                                                           \
  BOOST_PP_STRINGIZE(PARENTHESIZED_EXPRESSION),                                                                                       \
  [&]() mutable -> ::detail::exception_hook_type_impl<decltype(PARENTHESIZED_EXPRESSION)>::type { return PARENTHESIZED_EXPRESSION; }) \
  /**/
#else
#define EXCEPTION_HOOK(PARENTHESIZED_EXPRESSION)                                                                                               \
  ::detail::exception_hook_impl<typename ::detail::exception_hook_type_impl<decltype(PARENTHESIZED_EXPRESSION)>::type>(                        \
  __FUNCTION__,                                                                                                                                \
  __FILE__,                                                                                                                                    \
  __LINE__,                                                                                                                                    \
  BOOST_PP_STRINGIZE(PARENTHESIZED_EXPRESSION),                                                                                                \
  [&]() mutable -> typename ::detail::exception_hook_type_impl<decltype(PARENTHESIZED_EXPRESSION)>::type { return PARENTHESIZED_EXPRESSION; }) \
  /**/
#endif


////////////////////////////////////////
//
// End of library code.
//
////////////////////////////////////////


class C
{
private:
  int f() const
  {
    std::string s("nya-");
    return EXCEPTION_HOOK((boost::lexical_cast<int>(s)));
  }

public:
  void g() const
  {
// Workaround for GCC (avoids the bug reported at http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43856)
#if defined(__GNUC__)
    int i = EXCEPTION_HOOK((this->f()));
#else
    int i = EXCEPTION_HOOK((f()));
#endif
  }

  void h() const
  {
// The same as above
#if defined(__GNUC__)
    EXCEPTION_HOOK((this->g()));
#else
    EXCEPTION_HOOK((g()));
#endif
  }
};

void g()
{
  C c;
  EXCEPTION_HOOK((c.g()));
}

void h()
{
  C c;
  EXCEPTION_HOOK((c.h()));
}

int main()
{
  try {
    g();
  }
  catch (boost::exception &e) {
    hook_stack *p = boost::get_error_info<errinfo_hook_stack>(e);
    if (!!p) {
      std::cout << *p << std::endl;
    }
  }

  std::cout << "========================================" << std::endl;
  std::string s("42");
  std::cout << EXCEPTION_HOOK((boost::lexical_cast<int>(s))) << std::endl;
  std::cout << "========================================" << std::endl;

  try {
    h();
  }
  catch (boost::exception &e) {
    hook_stack *p = boost::get_error_info<errinfo_hook_stack>(e);
    if (!!p) {
      std::cout << *p << std::endl;
    }
  }
}

出力例 (Microsoft Visual C++ 2010 の場合)

from
  function  : N/A
  file      : N/A
  line      : 0
  expression: N/A
from
  function  : C::f
  file      : d:\home\cryolite\visual studio 2010\projects\cpp0x_test\exception_hook\main.cpp
  line      : 169
  expression: (boost::lexical_cast<int>(s))
from
  function  : C::g
  file      : d:\home\cryolite\visual studio 2010\projects\cpp0x_test\exception_hook\main.cpp
  line      : 178
  expression: (f())
from
  function  : g
  file      : d:\home\cryolite\visual studio 2010\projects\cpp0x_test\exception_hook\main.cpp
  line      : 195
  expression: (c.g())

========================================
42
========================================
from
  function  : N/A
  file      : N/A
  line      : 0
  expression: N/A
from
  function  : C::f
  file      : d:\home\cryolite\visual studio 2010\projects\cpp0x_test\exception_hook\main.cpp
  line      : 169
  expression: (boost::lexical_cast<int>(s))
from
  function  : C::g
  file      : d:\home\cryolite\visual studio 2010\projects\cpp0x_test\exception_hook\main.cpp
  line      : 178
  expression: (f())
from
  function  : C::h
  file      : d:\home\cryolite\visual studio 2010\projects\cpp0x_test\exception_hook\main.cpp
  line      : 187
  expression: (g())
from
  function  : h
  file      : d:\home\cryolite\visual studio 2010\projects\cpp0x_test\exception_hook\main.cpp
  line      : 201
  expression: (c.h())

*1:追記:あった. リンク先 PDF 注意 http://www2.research.att.com/~bs/wrapper.pdf