我有一组函数,我在这样的标题中声明:
actual_function.hpp
#ifndef ACTUAL_FUNCTION_HPP
#define ACTUAL_FUNCTION_HPP
#include <iostream>
#ifdef CONDITION
#warning Compiling version 1
template<typename T>
T fun (T x) {
std::cout << "Version 1 implementation is called.\n";
return x + x;
}
#else
#warning Compiling version 2
template<typename T>
T fun (T x) {
std::cout << "Version 2 implementation is called.\n";
return 2 * x + 1;
}
#endif
#endif
我试图在一个测试程序中测试该函数的两个版本。我认为我可以用多个翻译单元来完成这个,所以我有一个这样的文件布局:
main.cpp中:
void test_version_1 ();
void test_version_2 ();
int main () {
test_version_1 ();
test_version_2 ();
return 0;
}
test1.cpp:
#include <cassert>
#include <iostream>
#define CONDITION
#include "actual_function.hpp"
void test_version_1 () {
std::cout << "Version 1 is called.\n";
assert (fun (8) == 16);
}
测试2.cpp
#include <cassert>
#include <iostream>
#undef CONDITION
#include "actual_function.hpp"
void test_version_2 () {
std::cout << "Version 2 is called.\n";
assert (fun (8) == 17);
}
我的想法是,这会给test1.cpp版本1带来乐趣,而test2.cpp版本2则有趣。预处理器输出似乎支持这种想法:
g++ main.cpp test1.cpp test2.cpp
In file included from test1.cpp:4:0:
actual_function.hpp:7:2: warning: #warning Compiling version 1 [-Wcpp]
In file included from test2.cpp:4:0:
actual_function.hpp:14:2: warning: #warning Compiling version 2 [-Wcpp]
然而,我的猜测是链接器正在混淆我。当我运行程序时,会发生这种情况:
./a.out
Version 1 is called.
Version 1 implementation is called.
Version 2 is called.
Version 1 implementation is called.
a.out: test2.cpp:7: void test_version_2(): Assertion `fun (8) == 17' failed.
Aborted (core dumped)
如果我只在其中一个定义中重命名其他内容,并调用新命名的函数,则一切都按预期工作,这表明正确的函数在正确的位置可见。如果我只在定义中重命名一个函数,但不更改调用点,则会出现编译器错误test2.cpp:7:2: error: ‘fun’ was not declared in this scope
。这让我觉得链接器覆盖了函数,因为它们具有相同的名称和签名。
这真的发生了什么事吗?如果是这样,最佳解决方案是什么?我的两个想法如下:
1:让我的函数采用额外的模板参数,因此它将是模板,然后专注于true和false。实际上,我可能需要比这更复杂的东西(可能专注于int或其他东西),因为我的真正问题还有一些选择。如果定义了CONDITION宏,则它使用手动版本。如果未定义条件宏,那么它会看到它是否知道任何编译器内在函数执行我手动执行的操作,如果是,它会使用它们,否则,无论宏是否存在,它都会依赖于手动定义。不过,某种模板专业化仍然适用于此。
2:创建具有不同名称fun_manual
和fun_intrinsic
的函数,并使fun
成为基于其名称调用它们的包装函数。我不完全确定这是如何工作的。
我主要担心的是,如果编译器不支持内部版本,编译器就无法看到内部版本,或者它会产生错误。
我的两种解决方案中的任何一种都是我能做的最好的,还是有更好的东西?
答案 0 :(得分:5)
您违反了One Definition Rule。基本上,允许编译器和链接器的组合将函数的不同版本视为相同。
答案 1 :(得分:0)
我的最终解决方案遵循我的想法#2,看起来像这样:
#undef NO_INTRINSICS
#undef FUNCTION
// INTRINSIC_VERSION_?_EXISTS are a set of conditional statements that are
// defined by my environment
#if INTRINSIC_VERSION_X_EXISTS
#define FUNCTION INTRINSIC_FUNCTION_X
#elif INTRINSIC_VERSION_Y_EXISTS
#define FUNCTION INTRINSIC_FUNCTION_Y
#else
#define NO_INTRINSICS
#endif
template<typename T>
T manual_function (T x) {
// implementation
}
// Compilation will fail if the user requests intrinsic versions but none
// exist.
template<typename T>
T intrinsic_function (T x) {
return FUNCTION (x);
}
template<typename T>
T function (T x) {
#if defined NO_INTRINSICS
return manual_function (x);
#else
return intrinsic_function (x);
#endif
}
#undef FUNCTION
#undef NO_INTRINSICS
这允许我测试我的函数的手动和内在版本。但是,并不总是有可用的内在版本,因此用户可以安全地调用函数,如果存在则会选择内在版本(速度),否则将返回手动版本(为了便携性)。
我只需调用manual_function即可测试手册版本。我通过调用函数测试内在版本(因此如果在该特定平台上没有内部版本,则没有编译错误)。显然,如果没有内在版本,我不会通过调用函数来测试任何新东西,但那是因为没有什么可以测试,这意味着我的测试仍然有完全覆盖。