【问题标题】:How to repeat a block of C code, every time just changing a few keywords如何重复一段C代码,每次只改变几个关键字
【发布时间】:2021-04-04 18:44:52
【问题描述】:

我想重复一段 C 代码 N 次,每次只更改几个单词(如变量名、变量类型...)。

我想获得类似于 NumPy 的 .src 文件预处理器的最终效果。例如在their code 你可以看到:

/**begin repeat
 * #name = number, integer, signedinteger, unsignedinteger, inexact,
 *         floating, complexfloating, flexible, character#
 * #NAME = Number, Integer, SignedInteger, UnsignedInteger, Inexact,
 *         Floating, ComplexFloating, Flexible, Character#
 */
NPY_NO_EXPORT PyTypeObject Py@NAME@ArrType_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "numpy.@name@",
    .tp_basicsize = sizeof(PyObject)
};
/**end repeat**/

会变成这样的:

NPY_NO_EXPORT PyTypeObject PyNumberArrType_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "numpy.number",
    .tp_basicsize = sizeof(PyObject)
};
NPY_NO_EXPORT PyTypeObject PyIntegerArrType_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "numpy.integer",
    .tp_basicsize = sizeof(PyObject)
};
NPY_NO_EXPORT PyTypeObject PySignedIntegerArrType_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "numpy.signedinteger",
    .tp_basicsize = sizeof(PyObject)
};
// And so on for all the couples of `name` and `NAME`.

有没有办法做同样的事情,但不必使用外部预处理器?我考虑过使用宏,但我不这么认为。

【问题讨论】:

  • “琐碎”的解决方案是创建一个模板,填写更改的内容,然后生成源代码。
  • @DaveN ...我认为这基本上是 Numpy 所做的,并且显然不合适,因为它“太老套了”。 (但我不确定我是否看到任何其他选项不是很大程度上的变化)
  • "有没有办法解决我的问题" 您需要解决的问题究竟是什么?您是否知道 Python 附带了各种工具来交互/使用原始 c 数据,例如 ctypesstructarray?您是否了解用于简化编写 C 扩展/包装器的工具,例如 Cython?
  • 如果我正确理解了您的问题,归结为“如何在 C 中为多种类型重复代码”。它似乎并不特定于 Python C API,甚至不是 Python 本身。您是否考虑过 C 宏或 C++ 模板?
  • 第一:只问1个问题。 PS 在您的帖子中提出提出问题所需的内容。不要提供链接并期望我们阅读它们或知道哪些位是相关的或它们是如何相关的。你应该这样做并从中形成你的问题。 PS您甚至从未说过“为C固定数据类型创建包装器”的确切含义。 PS请通过编辑而不是cmets进行澄清。 PS 诸如“相当于”之类的东西没有帮助。你没有清楚地说什么是(不是)一个例子。

标签: c


【解决方案1】:

X 宏有帮助吗?

#define LIST_OF_TYPES               \
    X(Number, number)               \
    X(Integer, integer)             \
    X(SignedInteger, signedinteger)

#define X(type, name)                                       \
    NPY_NO_EXPORT PyTypeObject Py##type##ArrType_Type = {   \
        PyVarObject_HEAD_INIT(NULL, 0)                      \
        .tp_name = "numpy."#name,                           \
        .tp_basicsize = sizeof(PyObject)                    \
    };
LIST_OF_TYPES
#undef X

或在引号之间传递第二个参数:

#define LIST_OF_TYPES                   \
    X(Number, "number")                 \
    X(Integer, "integer")               \
    X(SignedInteger, "signedinteger")

#define X(type, name)                                       \
    NPY_NO_EXPORT PyTypeObject Py##type##ArrType_Type = {   \
        PyVarObject_HEAD_INIT(NULL, 0)                      \
        .tp_name = "numpy."name,                            \
        .tp_basicsize = sizeof(PyObject)                    \
    };
LIST_OF_TYPES
#undef X

或者,如果您愿意,可以使用类似函数的宏,在这种情况下,我会将分号移至宏调用:

#define PY_TYPE_OBJECT(type, name)                          \
    NPY_NO_EXPORT PyTypeObject Py##type##ArrType_Type = {   \
        PyVarObject_HEAD_INIT(NULL, 0)                      \
        .tp_name = "numpy."name,                            \
        .tp_basicsize = sizeof(PyObject)                    \
    }

PY_TYPE_OBJECT(Number, "number");
PY_TYPE_OBJECT(Integer, "integer");
PY_TYPE_OBJECT(SignedInteger, "signedinteger");

【讨论】:

  • 您好,感谢您的回答。我不太明白在你的宏定义中使用#s(比如Py##type##ArrType_Type )。
  • 单个# 用于字符串化,而## 用于连接,您可以在此处查看两个标记的有用示例:gcc.gnu.org/onlinedocs/cpp/Concatenation.html
  • 至于你的最后一个例子,有没有办法做同样的事情,但定义一个函数?
  • @Giuppox 我不这么认为,你可以用enums 做一些技巧,但你最终会在本地范围(函数的范围)分配一个结构/变量,我不'认为这不是你想要的。
  • 当然,您可以使用PyObject *object = PyObject_New(...) 从函数中返回分配的对象,如documentation 中所述,但这意味着使用堆。
【解决方案2】:

你可以写一个预处理宏:

#define PYOBJ(t, n) \
NPY_NO_EXPORT PyTypeObject Py ## t ## ArrType_Type = { /*
*/    PyVarObject_HEAD_INIT(NULL, 0) /*
*/    .tp_name = "numpy." # n, /*
*/    .tp_basicsize = sizeof(PyObject), /*
*/}

PYOBJ(Number, number);
PYOBJ(Integer, integer);
PYOBJ(SignedInteger, signedinteger);

运行 gcc -E -CC test.c 会产生这样的结果:

NPY_NO_EXPORT PyTypeObject PyNumberArrType_Type = { /*
*/ PyVarObject_HEAD_INIT(NULL, 0) /*
*/ .tp_name = "numpy." "number", /*
*/ .tp_basicsize = sizeof(PyObject), /*
*/};
# 9 "test.c"
NPY_NO_EXPORT PyTypeObject PyIntegerArrType_Type = { /*
*/ PyVarObject_HEAD_INIT(NULL, 0) /*
*/ .tp_name = "numpy." "integer", /*
*/ .tp_basicsize = sizeof(PyObject), /*
*/};
# 10 "test.c"
NPY_NO_EXPORT PyTypeObject PySignedIntegerArrType_Type = { /*
*/ PyVarObject_HEAD_INIT(NULL, 0) /*
*/ .tp_name = "numpy." "signedinteger", /*
*/ .tp_basicsize = sizeof(PyObject), /*
*/};

请参阅stringizing 上的预处理器宏文档:

当宏参数与前导“#”一起使用时,预处理器将其替换为实际参数的文字文本,并转换为字符串常量。

concatenation:

“##”预处理运算符执行令牌粘贴。扩展宏时,每个“##”运算符两侧的两个标记组合成一个标记,然后替换“##”和宏扩展中的两个原始标记。

您需要为宏提供两个参数,即使第二个参数是第一个参数的小写版本(Integerinteger)。因为预处理器不允许令牌转换。见问题Can you capitalize a pasted token in a macro?

至于我使用的/* */ cmets 和-CC 编译器标志,它将换行符放入预处理器输出中以使其可读。这个想法来自这个问题:How to make G++ preprocessor output a newline in a macro?。你可以改写:

#define PYOBJ(t, n) \
NPY_NO_EXPORT PyTypeObject Py ## t ## ArrType_Type = { \
    PyVarObject_HEAD_INIT(NULL, 0) \
    .tp_name = "numpy." # n, \
    .tp_basicsize = sizeof(PyObject), \
}

【讨论】:

  • /* */ 是一个非常好的想法。如果我理解正确,有人可能只想删除 cmets 并添加反斜杠,代码将以完全相同的方式工作。
  • @Giuppox 没错。编辑以添加该版本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-18
  • 2021-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
  • 2011-09-18
相关资源
最近更新 更多