避免调用可能会引发析构函数的函数

Avoid a call to a function that may throw in a destructor

我有一个 ODBC 包装器接口,使我能够在 C++ 中执行 SQL 查询。特别是,我使用 named parameter idiom 为 select 语句,例如:

Table.Select("foo").GroupBy("bar").OrderBy("baz");

要实现这个效果,classTable_treturns一个代理对象Select_t:

class Table_t
{
// ...

public:
  Select_t Select(std::string const &Stmt)
    { return {*this, Stmt}; }

  void Execute(std::string const &Stmt);
};

Select_t 将基本语句与附加子句结合起来,并在析构函数中执行实际语句:

class Select_t
{
private:
  Table_t &Table;
  std::string  SelectStmt,
               OrderStmt,
               GroupStmt;

public:
  Select_t(Table_t &Table_, std::string const &SelectStmt) :
    Table(Table_), SelectStmt(SelectStmt_) {}

  ~Select_t()
    { /* Combine the statements */ Table.Execute(/* Combined statement */); }

  Select_t &OrderBy(std::string const &OrderStmt_)
    { OrderStmt = OrderStmt_; return *this; }

  Select_t &GroupBy(std::string const &GroupStmt_)
    { GroupStmt = GroupStmt_; return *this; }
};

问题是 Table.Execute(Stmt) 可能会抛出而我不能抛出析构函数。有没有 我可以在保留命名参数习惯用法的同时解决这个问题吗?

到目前为止,我想到的唯一想法是向 Select_t 添加一个 Execute 函数,但我不想这样做:

Table.Select("foo").GroupBy("bar").OrderBy("baz").Execute();

抛出"inside"一个析构函数不是问题;问题是从析构函数中逃逸的异常。您需要捕获 ODBC 异常,并决定如何通过另一个接口传达错误。

实际上,将查询对象及其执行的关注点分开可能是个好主意。

惰性调用非常有用。

Execute 函数可以合理地成为一个免费函数。

例如:

auto myquery = Table.Select("foo").GroupBy("bar").OrderBy("baz");

auto future_result = marshal_to_background_thread(myquery);
//or
auto result = Execute(myquery);

这将有助于重新使用准备好的语句。

例如

auto myquery = Prepare(Table.Select("foo").Where(Equals("name", Param(1))).OrderBy("baz"));


auto result = Execute(myquery, {1, "bob"});
result = Execute(myquery, {1, "alice"});