C(++):用宏代替函数声明而不是它的调用
C(++): Replace function declaration with macros but not its invocations
我有一个库,我无法在单个文件中更改某些函数的声明、实现和使用位置。在同一个文件中完成了许多其他事情。我想覆盖那个单一的功能来做另一件事。问题是当我使用重命名宏时,所有找到 macro/function 名称的地方都被替换,包括声明和调用。我想要一些方法来仅重命名声明或调用。然后我就可以简单地 drop-in 我自己的函数在初始名称下作为替代。
代码如下所示:
// library-module.cpp
int foo(int a, int b) // declaration
{
return a+b;
}
int bar()
{
return foo(1, 2); // invocation
}
// main.cpp
// MAGIC HAPPENS HERE
#include "library-module.cpp"
int foo(int a, int b) // drop-in replacement
{
return a-b;
}
int main()
{
return bar(); // should be -1, not 3
}
另一种选择是执行 vice-versa 操作:替换对其他符号的所有调用,但不替换声明。虽然这不是首选,因为在野外可能还有其他用户使用该函数名称。
还有一个选择是在运行时执行,但我认为它不是那么好。
谢谢。
UPD1:
是c++。
这是文件中的实际函数 declaration, implementation and usage。
我想要的是从使用该应用程序所有代码的我的其他应用程序中替换函数 set_scenario
的一行,而不触及任何其他内容。我的想法是让核心团队维护所有代码,除了那个非常重要的功能,并自己处理这个代码,因为我需要一些特定的实现。因此,我不包括 headers,而是包括实现,并且想要这样一个肮脏的 hack。也许我走错路了?
最后我做了一个分叉,改变了线路并解决了这个问题,虽然我相信如果我不做分叉会更好,因为它变得更难获得变化.
我想到的另一个想法是,我可能必须为我要更改的功能制作一个补丁,并将其应用到每个构建中。这样就可以准确地知道那一行发生了什么变化,如果我有补丁应用错误,我会立即知道初始函数发生了变化。也许这是实现相同目标的更好方法?
最后,使用宏来处理这个问题对我来说非常有趣,因为这对我来说相当黑魔法。
再次感谢。
The issue is that when I use renaming macro, all the places where that macro/function name is found are replaced, both declarations and invocations.
是的。
I want some way to rename only declaration or invocations.
预处理器不了解 C 或 C++ 语言。它知道如何识别标识符,但不了解它们的 C 或 C++ 上下文,从而将声明与其他用途区分开来。因此,如果您有一个无法修改的代码块,那么您就不能使用预处理器有选择地仅替换该块中给定标识符的某些外观,而不是其他外观。
即使您可以修改有问题的代码,也可以在同一作用域中多次声明多种标识符,只要它们 定义 不超过一次。因此,虽然原则上您可以遍历代码并在所有需要的地方插入 #define
和 #undef
指令,以便在所有声明性上下文中将给定标识符识别为宏名称,但这并不容易在一般情况下,不仅仅是直接修改声明。
总的来说,这从一开始就听起来像是个坏主意。然而,尚不清楚推荐什么作为替代方案,因为该问题没有提供有关更高级别目标的详细信息,也没有提供所提议的方法为何听起来很有吸引力。
这可能会也可能不会,具体取决于函数的定义和使用方式。
假设您显示的声明和用法完全相同,您可以这样做:
#define foo(a, b) FOO_LOW(CAT(DETECT_, a) CAT(DETECT_, b))(a, b)
#define CAT(a, b) CAT_(a, b)
#define CAT_(a, b) a##b
#define DETECT_int ,
#define FOO_LOW(...) FOO_LOW_(__VA_ARGS__)
#define FOO_LOW_(a, ...) CAT(FOO_IMPL, __VA_OPT__(_DECL))
#define FOO_IMPL(a, b) replacement(a, b)
#define FOO_IMPL_DECL(a, b) foo(a, b)
constexpr int foo(int a, int b) {return a + b;}
constexpr int replacement(int a, int b) {return a - b;}
static_assert(foo(100, 10) == 90);
此实现需要 C++20,另外 MSVC 需要 /Zc:preprocessor
和 Clang(13 及更早版本,参见 bug)需要 -Wno-gnu-zero-variadic-macro-arguments
。可以使用较早的标准而不使用这些标志来实现,但宏会更加复杂。
这个具体实现有一些限制:
- 它检查两个参数是否以
int
开头,因此如果它发生在函数调用中,它将被错误地认为是一个声明,例如foo(int(1), int(2))
.
- 如果参数包含
(
)
之外的 ,
,则会出现编译错误,例如foo(std::array{1,2}[0], 1)
.
- 如果参数以标点符号开头,则会出现编译错误,例如
foo((1), (2))
.
我有一个库,我无法在单个文件中更改某些函数的声明、实现和使用位置。在同一个文件中完成了许多其他事情。我想覆盖那个单一的功能来做另一件事。问题是当我使用重命名宏时,所有找到 macro/function 名称的地方都被替换,包括声明和调用。我想要一些方法来仅重命名声明或调用。然后我就可以简单地 drop-in 我自己的函数在初始名称下作为替代。
代码如下所示:
// library-module.cpp
int foo(int a, int b) // declaration
{
return a+b;
}
int bar()
{
return foo(1, 2); // invocation
}
// main.cpp
// MAGIC HAPPENS HERE
#include "library-module.cpp"
int foo(int a, int b) // drop-in replacement
{
return a-b;
}
int main()
{
return bar(); // should be -1, not 3
}
另一种选择是执行 vice-versa 操作:替换对其他符号的所有调用,但不替换声明。虽然这不是首选,因为在野外可能还有其他用户使用该函数名称。
还有一个选择是在运行时执行,但我认为它不是那么好。
谢谢。
UPD1:
是c++。
这是文件中的实际函数 declaration, implementation and usage。
我想要的是从使用该应用程序所有代码的我的其他应用程序中替换函数 set_scenario
的一行,而不触及任何其他内容。我的想法是让核心团队维护所有代码,除了那个非常重要的功能,并自己处理这个代码,因为我需要一些特定的实现。因此,我不包括 headers,而是包括实现,并且想要这样一个肮脏的 hack。也许我走错路了?
最后我做了一个分叉,改变了线路并解决了这个问题,虽然我相信如果我不做分叉会更好,因为它变得更难获得变化.
我想到的另一个想法是,我可能必须为我要更改的功能制作一个补丁,并将其应用到每个构建中。这样就可以准确地知道那一行发生了什么变化,如果我有补丁应用错误,我会立即知道初始函数发生了变化。也许这是实现相同目标的更好方法?
最后,使用宏来处理这个问题对我来说非常有趣,因为这对我来说相当黑魔法。
再次感谢。
The issue is that when I use renaming macro, all the places where that macro/function name is found are replaced, both declarations and invocations.
是的。
I want some way to rename only declaration or invocations.
预处理器不了解 C 或 C++ 语言。它知道如何识别标识符,但不了解它们的 C 或 C++ 上下文,从而将声明与其他用途区分开来。因此,如果您有一个无法修改的代码块,那么您就不能使用预处理器有选择地仅替换该块中给定标识符的某些外观,而不是其他外观。
即使您可以修改有问题的代码,也可以在同一作用域中多次声明多种标识符,只要它们 定义 不超过一次。因此,虽然原则上您可以遍历代码并在所有需要的地方插入 #define
和 #undef
指令,以便在所有声明性上下文中将给定标识符识别为宏名称,但这并不容易在一般情况下,不仅仅是直接修改声明。
总的来说,这从一开始就听起来像是个坏主意。然而,尚不清楚推荐什么作为替代方案,因为该问题没有提供有关更高级别目标的详细信息,也没有提供所提议的方法为何听起来很有吸引力。
这可能会也可能不会,具体取决于函数的定义和使用方式。
假设您显示的声明和用法完全相同,您可以这样做:
#define foo(a, b) FOO_LOW(CAT(DETECT_, a) CAT(DETECT_, b))(a, b)
#define CAT(a, b) CAT_(a, b)
#define CAT_(a, b) a##b
#define DETECT_int ,
#define FOO_LOW(...) FOO_LOW_(__VA_ARGS__)
#define FOO_LOW_(a, ...) CAT(FOO_IMPL, __VA_OPT__(_DECL))
#define FOO_IMPL(a, b) replacement(a, b)
#define FOO_IMPL_DECL(a, b) foo(a, b)
constexpr int foo(int a, int b) {return a + b;}
constexpr int replacement(int a, int b) {return a - b;}
static_assert(foo(100, 10) == 90);
此实现需要 C++20,另外 MSVC 需要 /Zc:preprocessor
和 Clang(13 及更早版本,参见 bug)需要 -Wno-gnu-zero-variadic-macro-arguments
。可以使用较早的标准而不使用这些标志来实现,但宏会更加复杂。
这个具体实现有一些限制:
- 它检查两个参数是否以
int
开头,因此如果它发生在函数调用中,它将被错误地认为是一个声明,例如foo(int(1), int(2))
. - 如果参数包含
(
)
之外的,
,则会出现编译错误,例如foo(std::array{1,2}[0], 1)
. - 如果参数以标点符号开头,则会出现编译错误,例如
foo((1), (2))
.