在未命名的命名空间中定义C回调函数?

6
我有一个C++项目,使用了一个C bison解析器。该C解析器使用一个函数指针结构体来调用函数,在bison减少产生式时创建适当的AST节点:
typedef void Node;
struct Actions {
  Node *(*newIntLit)(int val);
  Node *(*newAsgnExpr)(Node *left, Node *right);
  /* ... */
};

现在,在项目的C++部分,我填充这些指针。

class AstNode {
  /* ... */
};
class IntLit : public AstNode { 
  /* ... */
};

extern "C" {
  Node *newIntLit(int val) {
    return (Node*)new IntLit(val);
  }

  /* ... */
}

Actions createActions() {
  Actions a;
  a.newIntLit = &newIntLit;
  /* ... */
  return a;
}

现在我把它们放在extern "C"中的唯一原因是我希望它们具有C调用约定。但是最理想的情况是,我希望它们的名称仍然被混淆。它们从未被C代码按名称调用,因此名称混淆不是问题。将它们混淆将避免名称冲突,因为某些操作被称为error,而C++回调函数具有丑陋的名称,只是为了避免与其他模块发生名称冲突。

extern "C" {
  void uglyNameError(char const *str) {
    /* ... */
  }

  /* ... */
}

a.error = &uglyNameError;

我想知道是否仅通过给函数类型C链接就可以实现

extern "C" void fty(char const *str);
namespace {
  fty error; /* Declared! But i can i define it with that type!? */
}

有什么想法吗?我正在寻找标准C++解决方案。


你不能将Bison的输出编译为C++代码,从而完全避免这个问题吗? - Konrad Rudolph
@Konrad 我的同事说 bison C++ 模式不好用,因此我们使用纯C来完成该部分,并将其抽象化,这样与扫描器一起组成了一个纯C库。 - Johannes Schaub - litb
1个回答

3

我不太理解这个问题。extern关键字并不会影响调用约定,只是影响链接器所使用的名称。一个在C++中编写的非实例方法的函数仍然是__cdecl约定的,无论是否使用extern "C"。此外,只要你将createActions()保留在同一源代码文件中,这些函数就不需要外部链接。你可以将它们声明为static或将它们放入未命名的命名空间中以避免冲突。


2
啊,我可以把它们设为“static”。好主意,为什么我没想到呢 :) 我只想到了匿名命名空间,但这对它们的链接没有任何影响,并且无法防止“extern "C"”函数的冲突。但是,“static”实际上可能有效!至于您答案中剩余的部分:如果实现希望这样做,它可以影响调用约定。我不想依赖于任何特定的实现,所以我将它们设置为“extern "C"”。 - Johannes Schaub - litb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接