Несколько функций с одинаковым именем и подписью перезаписывают друг друга? - PullRequest
0 голосов
/ 21 марта 2012

У меня есть набор функций, которые я объявляю в заголовке следующим образом:

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);
}

test2.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)

Если я переименую fun для чего-то другого только в одном из определений и вызову эту вновь названную функцию, все будет работать, как ожидается, что показывает, чтоправильные функции видны в правильных местах.Если я только переименую функцию в определении, но не меняю точку вызова, я получаю ошибку компилятора test2.cpp:7:2: error: ‘fun’ was not declared in this scope.Это заставляет меня думать, что компоновщик перезаписывает функции, потому что они имеют одно и то же имя и подпись.

Это действительно то, что происходит?Если да, то какое решение лучше?Мои две мысли заключаются в следующем:

1: Пусть мои функции принимают дополнительный аргумент шаблона, чтобы он был шаблоном, а затем специализировался на значениях true и false.В действительности мне, вероятно, понадобится что-то более сложное, чем это (возможно, специализирующееся на int или чем-то еще), потому что у моей настоящей проблемы есть еще несколько вариантов.Если макрос CONDITION определен, он использует ручную версию.Если макрос условия не определен, то он видит, знает ли он о каких-либо встроенных функциях компилятора, которые делают то, что я делаю вручную, и, если это так, он использует их, в противном случае он прибегает к ручным определениям независимо от наличия макроса.Тем не менее, некоторая специализация шаблонов может работать и здесь.

2: создавать функции с разными именами fun_manual и fun_intrinsic и иметь fun функцию-обертку, которая вызывает их на основе их имени.Я не совсем уверен, как это будет работать.

Моя основная проблема заключается в том, что если компилятор не поддерживает встроенную версию, встроенная версия не будет видна компилятору, или это приведет к ошибке.

Одно из моих двух решений лучшее, что я могу сделать, или есть что-то лучшее?

Ответы [ 2 ]

5 голосов
/ 21 марта 2012

Вы нарушаете правило One Definition . По сути, комбинация компилятора и компоновщика позволяет рассматривать разные версии функции так, как если бы они были одинаковыми.

0 голосов
/ 03 апреля 2012

Мое окончательное решение последовало моей идее # 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.Я проверяю внутреннюю версию, вызывая функцию (поэтому нет ошибок компиляции, если на этой конкретной платформе нет внутренних версий).Очевидно, что если нет встроенных версий, я не проверяю ничего нового, вызывая функцию, но это потому, что ничего не осталось для тестирования, что означает, что мои тесты все еще имеют полный охват.

...