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

C++11 how to extend a class template such as std::packaged_task

本文关键字:std ackaged task 何扩展 扩展 C++11      更新时间:2023-10-16

我正在尝试扩展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: %dn", 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 上看到它。

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

转到项目 -> C/C++ ->

代码生成 ->在"是,有 SEH 例外"上设置"启用C++例外"。

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

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

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

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

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

终于让它工作了。

#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: %dn", rc);
    }
    catch (std::exception& e)
    {
      printf("error inside lambda: %sn", e.what());
    }
    return 0;
}

不幸的是,它/EHa模式下没有做我想做的事,因为packaged_task有一个catch(...)可以捕获所有内容,然后重新投掷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: %dn", rc);
    }
    catch (std::exception& e)
    {
      printf("error inside lambda: %sn", e.what());
    }
    return 0;
}