6

Suppose there's a library, one version of which defines a function with name foo, and another version has the name changed to foo_other, but both these functions still have the same arguments and return values. I currently use conditional compilation like this:

#include <foo.h>
#ifdef USE_NEW_FOO
#define trueFoo foo_other
#else
#define trueFoo foo
#endif

But this requires some external detection of the library version and setting the corresponding compiler option like -DUSE_NEW_FOO. I'd rather have the code automatically figure what function it should call, based on it being declared or not in <foo.h>.

Is there any way to achieve this in any version of C?

If not, will switching to any version of C++ provide me any ways to do this? (assuming the library does all the needed actions like extern "C" blocks in its headers)? Namely, I'm thinking of somehow making use of SFINAE, but for a global function, rather than method, which was discussed in the linked question.

Community
  • 1
  • 1
Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • with gcc ... maybe something like `prototype ... foo ... __attribute__ ((alias ("foo_other")));` ... see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html – pmg Jun 04 '15 at 13:19
  • This is a duplicate of http://stackoverflow.com/questions/1749233/check-whether-function-is-declared-with-c-preprocessor – Brian Sidebotham Jun 04 '15 at 13:20
  • @BrianSidebotham no it's not. I didn't specify that one must use preprocessor. Also I ask for alternative ways like switching to C++. – Ruslan Jun 04 '15 at 13:24
  • C++ is not a superset of C. You can't just go ahead and start compiling your code as C++ code. – Columbo Jun 04 '15 at 13:29
  • 1
    @Columbo that's not a problem, assume I can port it. – Ruslan Jun 04 '15 at 13:30

3 Answers3

4

In C++ you can use expression SFINAE for this:

//this template only enabled if foo is declared with the right args
template <typename... Args>
auto trueFoo (Args&&... args) -> decltype(foo(std::forward<Args>(args)...))
{
    return foo(std::forward<Args>(args)...);
}

//ditto for fooOther
template <typename... Args>
auto trueFoo (Args&&... args) -> decltype(fooOther(std::forward<Args>(args)...))
{
    return fooOther(std::forward<Args>(args)...);
}
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
0

If you are statically linking to a function, in most versions of C++, the name of the function is "mangled" to reflect its argument list. Therefore, an attempt to statically link to the library, by a program with an out-of-date .hpp file, will result in an "unknown symbol" linker-error.

In the C language, there's no metadata of any kind which indicates what the argument list of any exported function actually is.

Realistically, I think, you simply need to be sure that the .h or .hpp files that you're using to link to a library, actually reflect the corresponding object-code within whatever version of that library you are using. You also need to be sure that the Makefile (or "auto-make" process) will correctly identify any-and-all modules within your application which link-to that library and which therefore must be recompiled in case of any changes to it. (If it were me, I would recompile the entire application.) In short, you must see to it that this issue doesn't occur.

Mike Robinson
  • 8,490
  • 5
  • 28
  • 41
  • As I noted after the C++-related part of question, assume that the function is declared in an `extern "C"` block, so the name won't get mangled. – Ruslan Jun 04 '15 at 13:25
  • Yes, indeed. And, if the name is "not mangled," the linker will have no way to know whether the actual binary code to which it (successfully) links your code, actually matches the `.h` or `.hpp` file that you used when compiling. And there's no way that it could. Realistically, I think that this is just like what faces you if the meaning of an existing external-subroutine gets changed: *you* have to be the one to keep on top of such things. – Mike Robinson Jun 04 '15 at 13:35
0

In C++ you can do something like this:

#include <iostream>
#include <type_traits>

//#define DEFINE_F

#ifdef DEFINE_F
void f()
{

}
#endif

namespace
{
    constexpr struct special
    {
      std::false_type operator()() const;
    }f; 
}

struct checkForF
{
    static const constexpr auto value = std::conditional< std::is_same<std::false_type, decltype(::f())>::value, std::false_type, std::true_type >::type();
};

int main()
{
    std::cout << checkForF::value << std::endl;
}

ideone

Please note I only handle f without any parameters.

user1233963
  • 1,450
  • 15
  • 41