C ++ 11如何扩展类模板,如std :: packaged_task

时间:2016-02-11 09:50:43

标签: c++ c++11 visual-studio-2015

我试图扩展std::packaged_task以处理SEH异常,但我无法进行编译。

以下内容无法编译:

#include <stdio.h>
#include <functional>
#include <future>
#include <windows.h>

template<class RET, class... ARGS>
class SafePackagedTask : public std::packaged_task<RET(ARGS...)>
{
public:
  template<class F>
  explicit SafePackagedTask(F && f)
    : std::packaged_task*(std::forward<F>(f))
  {
  }

  void operator()(ARGS... args)
  {
    __try
    {
      std::packaged_task*(std::forward<ARGS>(args));
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
      printf("SEH Exception 0x%08lX in SafePackagedTask!\n", GetExceptionCode());
    }
  }

};

int main()
{
    SafePackagedTask<int()> task([] {
      //int *a = nullptr; *a = 1; // generate SEH
      return 1;
    });
    std::future<int> fut = task.get_future();
    task();
    int rc = fut.get();
    printf("result: %d\n", rc);
}

错误是:

source_file.cpp(9): error C2091: function returns function
source_file.cpp(37): note: see reference to class template instantiation 'SafePackagedTask<int (void)>' being compiled
source_file.cpp(38): error C2440: 'initializing': cannot convert from 'std::future<_Ret>' to 'std::future<int>'
        with
        [
            _Ret=int (__cdecl *)(void)
        ]
source_file.cpp(38): note: No constructor could take the source type, or constructor overload resolution was ambiguous

rextester.com上查看。

3 个答案:

答案 0 :(得分:2)

您正在尝试解决已经解决的问题 通过在VC ++上设置特定标志(您明显使用),可以像处理常规C ++标准异常一样处理SEH异常* * / / p>

转到项目 - &gt; C / C ++ - &gt;代码生成 - &gt;在“是和SEH例外”中设置“启用C ++例外”。

现在您可以使用catch(...)子句捕获SEH异常:

try{

}catch(std::exception& e){}
catch(...) {/*your code here*} 

除此之外,处理SEH异常的最佳方法是不在第一时间创建它们。不要试图超越数组边界,检查空指针,在需要时使用智能指针并仔细编写类以使用RAII可能会消除代码中95%的异常。

*基本上,在Windows上,所有C ++异常都是Windows SEH的子集,并以此方式实现。

答案 1 :(得分:1)

许多std类不打算继承。一个线索是,如果析构函数不是虚拟的,那么,至少,该类不打算以多态方式使用。

答案 2 :(得分:1)

终于有了工作。

#include <stdio.h>
#include <functional>
#include <future>
#include <windows.h>

template<class RET, class... ARGS>
class SafePackagedTask;

template<class RET, class... ARGS>
class SafePackagedTask<RET(ARGS...)> : public std::packaged_task<RET(ARGS...)>
{
public:
  template<class F>
  explicit SafePackagedTask(F && f)
    : std::packaged_task<RET(ARGS...)>(std::forward<F>(f))
  {
  }

  void operator()(ARGS... args)
  {
    __try
    {
      std::packaged_task<RET(ARGS...)>::operator()(std::forward<ARGS>(args)...);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
      printf("SEH Exception 0x%08lX in SafePackagedTask!\n", GetExceptionCode());
      // ... print stack trace ...
    }
  }

};

int main()
{
    SafePackagedTask<int()> task([] {
      int *a = nullptr; *a = 1; // generate SEH
      return 1;
    });
    std::future<int> fut = task.get_future();
    try
    {
      task();
      int rc = fut.get();
      printf("result: %d\n", rc);
    }
    catch (std::exception& e)
    {
      printf("error inside lambda: %s\n", e.what());
    }
    return 0;
}

不幸的是,它并没有在/EHa模式下执行我想要的操作,因为catch(...)中有一个packaged_task可以抓取所有内容,然后重新投入future::get() }。永远不会到达__except处理程序,原始堆栈跟踪将丢失。所以我放弃了这种方法并将底层任务包装起来:

template <typename F>
struct SafeTaskWrapper : F
{
  SafeTaskWrapper(F&& f) : F(std::move(f)) {}

  SafeTaskWrapper(SafeTaskWrapper&&) = default;
  SafeTaskWrapper& operator=(SafeTaskWrapper&&) = default;

  SafeTaskWrapper(const SafeTaskWrapper&) = default;
  SafeTaskWrapper& operator=(const SafeTaskWrapper&) = default;

  void operator()()
  {
    __try
    {
      F::operator()();
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
      printf("SEH Exception 0x%08lX in lambda!\n", GetExceptionCode());
      // ... print stack trace ...
      throw std::runtime_error("SEH Exception in lambda");
    }
  }
};

template <typename T>
auto make_safe_task(T&& t) -> SafeTaskWrapper<typename std::decay<T>::type>
{
  return std::move(t);
}

int main()
{
    std::packaged_task<int()> task(make_safe_task([] {
      //int *a = nullptr; *a = 1; // generate SEH
      return 1;
    }));
    std::future<int> fut = task.get_future();
    try
    {
      task();
      int rc = fut.get();
      printf("result: %d\n", rc);
    }
    catch (std::exception& e)
    {
      printf("error inside lambda: %s\n", e.what());
    }
    return 0;
}