7

This is a question on C/C++ function definitions. The code discussed is the static library libRmath that provides the definitions in the Rmath.h header file in R.

The documentation provided for the library states that it is optional for the user to provide the function definition for the function double unif_rand(void).

So my question is if such a function definition is optional, wouldn't there be a problem of multiple function definitions which is not allowed in C/C++?

Edit: It might be tempting to speculate on how things works without looking at the source code, but that is not what I want. I am interested to know how it really works, so you would probably need to read the source code and documentations to answer this question.

ggg
  • 1,857
  • 2
  • 21
  • 33
  • My guess would be that the function def is in an #ifdef so that if you define it, then yours is found by the linker first, so the one already there would be ignored. – baash05 Mar 27 '12 at 01:34

2 Answers2

15

When you application is linked, unresolved symbols will be resolved using the libraries you provide. If you don't define a function, it will be an unresolved symbol during linking, therefore, the linked will try to resolve that symbol using librmath, in this case. If one or more symbols can't be resolved, you'll get a linker error.

However, if you do define the function in your code, it will be already defined during linking, so there will be no need to resolve it using symbols from external libraries.

What you can't do is to define the same symbol more than once in your application.

Edit: Since there is a lot of debate in another answer, i've made a practical example. I've created a shared object(similar to a DLL in windows), which defines and exports a function foo:

//lib.h
extern "C" {
    void foo();
    void bar();
};

//lib.cpp
#include <iostream>
#include "lib.h"

void foo() {
    std::cout << "From lib\n";
}

void bar() {
    std::cout << "Bar, calling foo\n";
    foo();
}

In order to test this shared object, i've created an application which is linked with it:

//test.cpp
#include <iostream>
#include "lib.h"

void foo() {
    std::cout << "From app\n";
}

int main() {
    bar();
}

I've compiled both, the shared object and the application:

g++ lib.cpp -o libtest.so -Wall -fPIC -shared -Wl,--export-dynamic -Wl,-soname,libtest.so -Wl,-z,defs
g++ test.cpp -o test -L. -ltest

And when i execute test, setting the library path to ".", so my shared object can be loaded, i get this output:

matias@master:/tmp$ LD_LIBRARY_PATH="." ./test
Bar, calling foo
From app

As you can see, the foo function defined in the application(not the shared object) is called. You can basically do this for every exported symbol in a shared object.

EDIT2: i've added another exported function in lib.h. The application now calls this function, which ends up calling foo. The result is the same, as expected.

EDIT3: Ok, let's go deeper. This is the dump from function bar:

Dump of assembler code for function bar@plt:
   0x0804855c <+0>: jmp    DWORD PTR ds:0x804a004
   0x08048562 <+6>: push   0x8
   0x08048567 <+11>:    jmp    0x804853c

If we go to address 0x804a004:

Dump of assembler code for function _GLOBAL_OFFSET_TABLE_:
   0x08049ff4 <+0>: or     BYTE PTR [edi+0x804],bl
   0x08049ffa <+6>: add    BYTE PTR [eax],al
   0x08049ffc <+8>: add    BYTE PTR [eax],al
   0x08049ffe <+10>:    add    BYTE PTR [eax],al
   .....

As you can see, it's jumping to the Global Offset Table. You can read about the GOT here and here. Dynamic symbols(which are resolved at runtime) are stored in this table. Whenever you call a symbol that should be resolved at runtime, you actually jump to this table, and then jump to the address that is stored in the this table's corresponding entry. Since the application defines foo, the GOT contains the address of the definition from test.cpp, not the one in our shared object.

EDIT4: Okay, last edit. Quoting from the documentation:

You will need to supply the uniform random number generator

 double unif_rand(void)

or use the one supplied (and with a dynamic library or DLL you will have to use the one supplied(...)

The documentation clearly says that you cannot provide you own implementation of unif_rand if you're using the dynamic library. Therefore, i believe that what i pointed out, actually answers your question.

Suma
  • 33,181
  • 16
  • 123
  • 191
mfontanini
  • 21,410
  • 4
  • 65
  • 73
  • This is essentially saying the same thing as I said. – Nawaz Mar 27 '12 at 01:35
  • The last paragraph is essentially the same. The rest is not. – mfontanini Mar 27 '12 at 01:42
  • How exactly? The rest is just the details. – Nawaz Mar 27 '12 at 01:43
  • You give no explanation on how "you optionally may use definitions". – mfontanini Mar 27 '12 at 01:44
  • that is why I said the rest in your answer is the details. Also, I said that in short : *Now it all depends on how you link them to your program.* On windows, there are several ways to do that. – Nawaz Mar 27 '12 at 01:46
  • There you go, i added an example. – mfontanini Mar 27 '12 at 02:11
  • That is good, but incomplete example as it doesn't show how you would use the symbol in DLL. – Nawaz Mar 27 '12 at 02:16
  • 1
    But do know notice `foo` from app is getting called? Not the one from the DLL? I'm surprised to see that by the way; I think you've done something wrong. Anyway, how would you call `foo` from DLL? – Nawaz Mar 27 '12 at 02:21
  • @fontanini The library mentioned is a static library so that is probably not how it works. Anyway thanks for the detailed explanation. – ggg Mar 27 '12 at 02:29
  • @ggg i checked the code, there is nothing special about it. The headers doesn't contain anything rare, just a definiton of `unif_rand` inside an `extern "C"` scope. @Nawaz i've eddited my code again. I did notice `foo` from `test.cpp` is called, that was the point of my example. – mfontanini Mar 27 '12 at 02:41
  • @ggg i've quoted the documentation. – mfontanini Mar 27 '12 at 02:53
  • 1
    Docs say with DLL you cannot provide your own ("with ... DLL you will have to use the one supplied"). I am not familiar with shared objects, but knowing how Win DLL work, this sounds correct. When linking a DLL, DLL function calls are not changed, it is the calls from the program using the DLL which are changed. – Suma Mar 27 '12 at 10:54
  • @fontanini accepted you answer. It seems that this also works if the library is static, was there a particular reason the you used a dynamic library in your example? – ggg Mar 27 '12 at 14:40
  • 1
    ggg, your question didn't mention the static lib at first, i went for the dynamic library approach, since i knew this happens when using the latter. @Suma thanks, i got that wrong. Thanks for your edit :D. – mfontanini Mar 27 '12 at 15:24
0

Linking a static library is slightly different from linking all the objects inside the static library.

Definitions in the static library are pulled in only if needed, so they can't cause multiple definition errors.

This has some side effects, for example the well-known problem of global initializers inside a static library not running when nothing in the main program references that object.

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • A small comment; it might be worth explaining "for example the well-known problem of global initializers inside a static library not running when nothing in the main program references that object" because that does not sound like a problem but sounds correct; "object not used, so no overhead of initialisation" sounds better than "even though the object is not used, there is still the overhead of initialisation". Is there already a stackoverflow question that could be used as a reference? Just a thought. – gbulmer Mar 27 '12 at 08:04
  • Can you elaborate with some C++ code and compilation commands? – ggg Mar 27 '12 at 10:35
  • @gbulmer: http://stackoverflow.com/questions/6317796/ctor-init-not-calling-the-global-ctor-instances-in-library and http://stackoverflow.com/questions/9459980/c-global-variable-not-initialized-when-linked-through-static-libraries-but-ok and http://stackoverflow.com/questions/6221947/ensuring-that-a-static-method-gets-called-before-main – Ben Voigt Mar 27 '12 at 14:01