2

我发布了这个问题SpongeBobFan说答案是反思。我找到了Boost.Reflect并且想知道如何使用该库或其他 C++ 反射库来实现这一点。请解释你的答案,因为我不能只看一眼代码就知道发生了什么。我的问题是这样的:

好的,我有一个问题。说我有这个代码:

int myfunc(int arg-a, int arg-b);
int mywrapperfunc(obj a, obj b);

mywrapperfunc应该换行myfuncmywrapperfunc丢弃第一个参数并采用第二个参数,这是一个数组。然后我使用数组项作为参数。但是假设我不知道需要多少参数myfunc,也不知道数组类型对象(b)中有多少项。我将如何 myfunc以正确数量的参数以编程方式调用?移交的 args 数量将与数组类型对象中的项目数相同。

编辑:arg-a并且arg-b应该来自数组类型对象。我将对象拆分为 args。

编辑:好的,好的,我正在尝试以某种意义包装 Python C API,隐藏大多数后台作业。

4

3 回答 3

3

你看过反射吗?该工具使用GCC-XML创建一个类似于 Java 中的反射库。

这是我最初的看法(我目前没有太多空闲时间来进一步冲洗它):

// Use the Reflex library to look up "myfunc" reflectively
Type t = Type::ByName("myfunc");
// The Reflex type 'Type" has a "FunctionParameterSize()" that tells
// you how many args in a function
size_t num_params = t.FunctionParameterSize();
// Use this information to call "myfunc" with objects from "b"

如果 Reflex 只提供对类成员的调用能力,那么您可以创建myfunc()一个静态类成员,并尝试如下操作:

// Load MyClass reflectively (where MyClass contains a static member function "myfunc")
Type t = Type::ByName("MyClass");
// Find the member function "myfunc"
Member m = t.MemberByName("myfunc");
// Pack the parameters from "b" into a vector
// (Assumes "b" has indexing operators, and a "length()" function)
std::vector params(&b[0], &b[b.length()-1]);
// Invoke the method "myfunc" reflectively
m.Invoke(params);

你将不得不摆弄这个,我不确定我是否正确地传递参数等等,但希望这会给你一些新的想法。

于 2013-04-15T16:59:18.233 回答
2

另一个答案中建议的 GCCXML/Reflex 方案本质上建议您运行一个步骤来预处理代码,并提取有关您拥有的类/函数/字段集的信息,以便您可以以编程方式访问它们。鉴于您的说法,这听起来是正确的方向。

出现反射方案(除了其他答案中发布的内容外,我一无所知)通过一个看起来像其他可反射语言提供的动态反射的库在运行时提供这些额外的信息,代价是膨胀应用程序包含您通常不使用的所有这些反射数据(C++ 的借口是,“不要为不使用的东西付费”)。关键是你仍然必须编写这些伪反射调用来实现你的目的,所以你必须提前知道你真正想要做什么。

你不必那样。如果你必须预处理源代码,你可以得到一个预处理器来提取描述性信息(有点像 GCCXML),并简单地生成对目标函数的调用。生成的程序将执行“反射”程序会执行的操作,但没有运行时反射库或膨胀。

理论上,您可以通过任何程序转换来做到这一点。在实践中,这样的工具必须能够处理 C++,这非常困难。

地球上只有三个工具可以做到这一点:我们的 DMS 软件再造工具包及其 C++ 前端、Clang 和(遥远的第三个)GCC。

都提供代码解析、构建 AST 和符号表(这是您想要的反射数据),包括所有微观细节,例如“参数数量”(这似乎是 Reflect 方案的缺点,基于其他答案)。

GCC 无法重新生成有效的 C++ 代码,尽管有一些名为 GCCMelt 的扩展对此提出了一些声明,但我没有经验。(GCCXML 是 GCC 的自定义 hack,用于转储符号表数据)。Clang 确实提供了修改 C++ AST 的能力,但是您必须通过程序接口来破解树,这使得编写转换变得困难。DMS 提供源到源的表面语法转换功能,使编写转换变得更容易。(YMMV)。

关键是通过使用这些工具,您可以编写自定义代码来遍历符号表以提取您想要的函数定义等,并生成执行您想要的 C++ 代码(可变长度参数列表和所有)。[这可能比您预期的要复杂,但这与您已经知道的在线反射解决方案没有什么不同)。不需要运行时反射。

不要期望这些解决方案中的任何一个都易于实施。C++ 是一门复杂的语言,无论你选择什么实现方案,你都会为它的复杂性付出代价,除非你正在做一些非常简单的事情。

于 2013-04-17T17:45:48.427 回答
1

不幸的是,我不认为你想要的在 C++ 中是可行的。由于 C++ 代码经过 2 个连续阶段,1) 编译和 2) 执行,因此您通常不能将直到执行时才知道的东西(myfunc 所需的参数数量)交给编译器,以便编译器可以找到正确的 myfunc 进行编译。

现在,如果 myfunc()(我假设它是一些 Python C API 调用)被声明为类似于 C 的 printf 语句,那么您有一些选择。C 中的 Printf 是用变量参数列表声明的,例如,stdio.h 可能具有类似于以下的声明:

int printf ( const char * format, ... );

这允许 printf 接受任意数量的参数。但是根据您的描述,这听起来不像您要包装的函数(或函数集?)。有时,C++ 模板元编程技术提供的东西接近于您的需求:如果您可以在编译时计算参数的数量,则可以使用模板类来选择要调用的 myfunc 的正确原型。例如,也许是这样的:

template<int N>
class Wrapper {
  public:
    mywrapperfunc() {
      if (N == 0) {
        // Call myfunc() with zero args here...
      } else if (N == 1) {
        // Call myfunc() with 1 arg here...
      } // etc...
    }
};

C++11constexpr允许您在通过关键字计算在编译时使用的“N”值时有一些余地。但同样,您必须能够在编译时知道或至少计算 N 才能使其工作。

老实说,我想不出任何其他方法来完成这项工作。通过多年的编程,我提出了定义这种情况的两个基本规则:

  1. 如果你想出了一个很酷的库点子,那么其他人可能已经编写了它,而且它可能比你的想法更好。更努力地看。
  2. 如果还没有其他人提出该库,并且您无法弄清楚(经过大量研究)如何做到这一点,那么您使用的技术可能无法完成(例如:C++) .
于 2013-04-14T16:35:48.247 回答