0

I have a set of functions that I declare in a header like this:

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

I am trying to test both versions of the function in one test program. I thought that I could do this with multiple translation units, so I have a file layout like this:

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

My thought was that this would then give test1.cpp version 1 of fun, and test2.cpp version 2 of fun. The pre-processor output appears to support this thought:

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]

However, my guess is that the linker is mixing things up on me. When I run the program, this is what happens:

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

If I rename fun to something else in only one of the definitions, and call that newly named function, everything works as expected, which shows that the correct functions are visible in the correct spots. If I only rename a function at the definition, but do not change the point of call, I get a compiler error test2.cpp:7:2: error: ‘fun’ was not declared in this scope. This makes me think that the linker is overwriting functions because they have the same name and signature.

Is this really what is happening? If so, what is the best solution? My two thoughts are as follows:

1: Have my functions take an extra template argument, so it would be template, and then specialize on true vs. false. In reality, I would probably need something a little more complex than this (perhaps specialize on an int or something), because my real problem has a few more options. If the CONDITION macro is defined, it uses a manual version. If the condition macro is not defined, then it sees if it knows about any compiler intrinsics that do what I do manually, and if so, it uses them, otherwise, it falls back on the manual definitions regardless of the presence of the macro. Some sort of template specialization could work here still, though.

2: Create functions with different names fun_manual and fun_intrinsic, and have fun be a wrapper function that calls them based on their name. I'm not entirely sure how this would work.

My primary concern is that if the compiler does not support the intrinsic version, the intrinsic version cannot be seen by the compiler, or it will give an error.

Are either of my two solutions the best I can do, or is there something better?

David Stone
  • 26,872
  • 14
  • 68
  • 84
  • 1
    It is undefined behaviour if not all translation units see exactly the same definitions. – Kerrek SB Mar 20 '12 at 22:37
  • 2
    You have function templates, not functions. – Lightness Races in Orbit Mar 20 '12 at 22:39
  • Is there a warning in GCC that can be turned on to alert the user that multiple functions are being combined into one? After fixing up my `actual_function.hpp` header, I still got the same erroneous results. I then realized that in another part of my code, I forgot to stick some functions in the unnamed namespace. – David Stone Mar 20 '12 at 23:43

1 Answers1

5

You are violating the One Definition Rule. Essentially the combination of compiler and linker are allowed to treat the different versions of the function as if they were the same.

Community
  • 1
  • 1
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622